WebAssembly / component-model

Repository for design and specification of the Component Model
Other
899 stars 75 forks source link

Can you move resource ownership inside a component? #294

Closed jcbhmr closed 5 months ago

jcbhmr commented 5 months ago

Unsure if this is a jco issue or if this is me misunderstanding something or if this is just not possible or what. I'm opening this issue here since I'm looking for answer to "is this possible or intended to be possible in WIT using WASM components". You can close this if this is not relevant to WASM Component Model directly. ❤️

See jco issue https://github.com/bytecodealliance/jco/issues/337

Basically I want to setGlobal(importantResource) from JS and then later call a doTheThingUsingTheGlobalImportantResource() function that's not a method of the importantResource -- its just a freestanding function that uses shared state.

First the code context:

interface my-box {
  resource my-box {
    constructor(value: u32);
    value: func() -> u32;
  }
  set-global: func(value: my-box);
  global-value: func() -> u32;
}
thread_local!(static MY_BOX_GLOBAL: RefCell<my_box::OwnMyBox> = RefCell::new(my_box::OwnMyBox::new(MyBox::new(0))));
impl my_box::Guest for Component {
    fn set_global(value: my_box::OwnMyBox) {
        MY_BOX_GLOBAL.with(|global| {
            *global.borrow_mut() = value;
        });
    }
    fn global_value() -> u32 {
        MY_BOX_GLOBAL.with(|global| global.borrow().value())
    }
}

pub struct MyBox(u32);
impl my_box::GuestMyBox for MyBox {
    fn new(value: u32) -> Self {
        Self(value)
    }
    fn value(&self) -> u32 {
        self.0
    }
}

☝ right now that fails when i use it from js using jco with a Resource error: Invalid handle 1055504

I want to be able to "move" (I think that's the right terminology) the resource into the component and have the component own it. I assumed that this is what normally happened with the:

f: func(i-now-own-this: the-resource);

vs the borrowing approach which would leave the resource available to the host even after the component is done with it:

g: func(im-just-borrowing-this: borrow<the-resource>);

Thus, I assumed that I could stash ("move"?) the resource in the component's memory and use it at a later date. This doesn't appear to be the case. Am I misunderstanding? Is there a way to do what I want?

lukewagner commented 5 months ago

Yes, iiuc, I think what you're doing should totally work for the reason you're describing, and thus this seems like a bug. Maybe file a bug on wit-bindgen?

To help isolate the failure, it might be useful to test and report:

jcbhmr commented 5 months ago

it works in Rust using Wasmtime bindgen!() to import and use the component!

let i = bindings.jcbhmr_hello_world_rust_wasm_component_lib_my_box_interface();
let a = i.call_global_value(&mut store)?;
println!("a = {}", a);
let b = i.my_box();
let c = b.call_constructor(&mut store, 42)?;
i.call_set_global(&mut store, c)?;
let d = i.call_global_value(&mut store)?;
println!("d = {}", d);
a = 0
d = 42

If I do that same thing-ish with the before-after print in jco it breaks 😢

Welcome to Node.js v20.10.0.
Type ".help" for more information.
> let m  = await import("./jco/c.js")
undefined
> m.myBoxInterface.globalValue()
0
> m.myBoxInterface.setGlobal(new  m.myBoxInterface.MyBox(42))
undefined
> m.myBoxInterface.globalValue()
Uncaught Error: Resource error: Invalid handle 1114136
    at trampoline1 (file:///home/jcbhmr/Documents/rswasmtest/jco/c.js:36:11)
    at _ZN35hello_world_rust_wasm_component_lib8bindings7exports6jcbhmr35hello_world_rust_wasm_component_lib16my_box_interface98_$LT$impl$u20$wit_bindgen..RustResource$u20$for$u20$hello_world_rust_wasm_component_lib..MyBox$GT$3rep17hba8eecbe3f5df761E (wasm://wasm/007402ee:wasm-function[61]:0x2881)
    at _ZN74_$LT$wit_bindgen..Resource$LT$T$GT$$u20$as$u20$core..ops..deref..Deref$GT$5deref17h78e74767609c0261E (wasm://wasm/007402ee:wasm-function[50]:0x22bc)
    at _ZN191_$LT$hello_world_rust_wasm_component_lib..Component$u20$as$u20$hello_world_rust_wasm_component_lib..bindings..exports..jcbhmr..hello_world_rust_wasm_component_lib..my_box_interface..Guest$GT$12global_value28_$u7b$$u7b$closure$u7d$$u7d$17h536f4cf76035854aE (wasm://wasm/007402ee:wasm-function[54]:0x252d)
    at _ZN3std6thread5local17LocalKey$LT$T$GT$8try_with17h348fe79a762b4901E (wasm://wasm/007402ee:wasm-function[14]:0xbaa)
    at _ZN3std6thread5local17LocalKey$LT$T$GT$4with17h3333c07318fcbf3aE (wasm://wasm/007402ee:wasm-function[13]:0x9fd)
    at _ZN191_$LT$hello_world_rust_wasm_component_lib..Component$u20$as$u20$hello_world_rust_wasm_component_lib..bindings..exports..jcbhmr..hello_world_rust_wasm_component_lib..my_box_interface..Guest$GT$12global_value17hd17d6eecb02803faE (wasm://wasm/007402ee:wasm-function[57]:0x27ad)
    at jcbhmr:hello-world-rust-wasm-component-lib/my-box-interface#global-value (wasm://wasm/007402ee:wasm-function[9]:0x707)
    at Object.globalValue (file:///home/jcbhmr/Documents/rswasmtest/jco/c.js:99:99)
> 

So I think you're right: this is POSSIBLE and jco does it INCORRECTLY as of right now. Sound like the right conclusion?

You gave me the kick in the pants to try this with another wasm component runtime and clarified that yes, what I'm trying to do is possible. 👍 Thanks!

ref https://github.com/bytecodealliance/jco/issues/337

Sidenote: discovered https://github.com/bytecodealliance/wasmtime/issues/7775 while doing this