MangoPeachGrape opened issue #11437:
(Part of the outstanding items of the component model: https://github.com/bytecodealliance/wasmtime/issues/8036#issuecomment-3129666977)
See previous comments: https://github.com/bytecodealliance/wasmtime/pull/11055#issue-3151376309, https://github.com/bytecodealliance/wasmtime/pull/11055#issuecomment-2980677120
My current limited understanding is that there should be a type that holds the resource's
ForeignData, which is then inserted to theResourceTable. Lets call this typeCApiResource.When a function is called, we get a
ResourceAny, that can be thenres.try_into_resource::<CApiResource>(&mut store), and thenstore.data().table.get(&capi_res).But that brings up the question: What should be passed to C?
- Should it be the
ResourceAnysomehow boxed into a C type?- Should it be the contents of the
CApiResourcealready fetched from theResourceTable, so the data pointer to data, and the embedder specified resource type integer?- Should it just be the resource representation
u32index?- Something else?
The same question exists in the reverse direction, when the C API wants to return a new resource.
I also seem to read that
ResourceAny::resource_drop()must be called at the end. Does this mean after calling the embedder's callback, we have to iterate the arguments, match if resource, and call this?Please note that I don't know much about working with resources, so I might be going at this at the wrong way.
alexcrichton commented on issue #11437:
Fair warning on this: resources are subtle and tricky in ways that don't often become apparent until later. I unfortunately don't have time to champion/design a full implementation here but I can try to help out where I can with thoughts/feedback. This'll likely be both a learning process for both you and me as we figure out how best to represent this in the C API and it may require some adjustments over time. Basically I don't think that the Rust API as-is today is perfect for providing a reasonable C API and may need changes, but I'm not sure exactly how. I'll also admit that I'm re-paging-in all this context too, so apologies if this is a bit verbose.
Today there are two ways to represent a resource in the Rust API of Wasmtime:
Resource<T>andResourceAny. Both are glorified wrappers over a 32-bit index which is "guarded" with type information. The use case of each though is somewhat subtle.
Resource<T>is ubiquitously used for host bindings in WASI impementations andbindgen!-generated bindings. TheTtype is only used for aTypeIdand is pretty arbitrary, but in wasmtime-wasi it's often used to match the type of something stored in aResourceTable. They need not necessarily be equivalent, however. The other assertion ofResource<T>is "I'm the host and I know what I'm doing" in particular around destructors. It's assume that the host knows how to "destroy" the resource (which is, after all, just an index). More-or-lessResource<T>is a free-floating 32-bit index which is almost entirely disconnected from Wasmtime's state management meaning the host can do whatever it wants and has no destructor requirement.
ResourceAnyis used, as the name might imply, to represent any possible resource. This notably includes guest-defined resources in addition to host-defined resources. The reason this has a destructor requirement is for the guest-defined case. This also has a destructor requirement asResourceAnyhas some indexes/connection to host-defined state within Wasmtime (e.g. the "host tables"). This state is severed withResource<T>but still present withResourceAny.IMO neither of these are perfectly suitable for usage in the C API:
Resource<T>isn't great becauseThas to be defined statically. This means that the C API would only be able to define one type of resource which I don't think is suitable because then there's no protection about using the correct type of resource.Resource<T>is good, however, in that it's "just an index" disconnected from other bits. I like this aspect because hosts don't have to worry about destructors or anything like that, so I think we do want something likeResource<T>.ResourceAnyisn't great as it's too general. It represents any possible resource, including guest resources, which has the burdensome requirement of needing a destructor to fully clean up after itself.After thinking about this for a bit I think we need some Rust API changes and careful modeling in C API. What I'm imagining looks like:
wasmtime_component_valunion_tis extended with a resource field which is more-or-lessResourceAny. This would be some sort of opaque structure and boxed upResourceAnymost likely. This would indeed have a requirement that it must be operated on to be properly cleaned up (beyond*_deleteto avoid host memory leaks).wasmtime_component_resource_any_t, let's say, would be C bindings for theResourceAnytype. This notably includes theownedandresource_dropmethods. Eventually could bindtytoo, but that's ok to not dig in too deeply at this time.A new API will be added to the Rust crate to support multiple types of resources in the C API:
- A new
fn ResourceType::runtime_typed(i: u32) -> ResourceTypewill be added. This is similar toResourceType::Host(TypeId)but it'll just beResourceType::RuntimeTyped(u32).- This new resource type will be used to represent C API resources defined with a 32-bit integer as their "type", in an RTTI-like fashion.
- A new
ResourceRuntimeTypedwill be added. This'll more-or-less be an exact copy ofResource<T>except that type-checks look forResourceType::RuntimeTypedinstead ofResourceType::HostResourceAnywill have new conversion betweenResourceRuntimeTypedin the same manner asResource<T>- This effectively means that
ResourceAnysort of has two subclasses ofResource<T>andResourceRuntimeTypedin a sense.- The naming "runtime typed" probably wants bikeshedding...
wasmtime_component_resource_any_twill have conversions to/fromwasmtime_component_resource_twhich would be a binding toResourceRuntimeTypedin Rust. These conversions would take the type as an argument for example to say "I'm asserting that thisResourceAnyis of runtime type 4". Conversion towasmtime_component_resource_twould "consume" the originalResourceAnyand release the host from its destructor requirement in the exact same way asResource<T>.wasmtime_component_resource_twould have index/type accessors both operating on 32-bit integers, and then the embedding host would be able to do whatever it wants at that point.wasmtime_component_linker_instance_add_resourcewould be added taking a 32-bit integer which would callLinkerInstance::resourcewithResourceType::runtime_typed(...). That would define the host destructor for a resource as well as the type information.- Host functions in C would almost always convert
wasmtime_component_resource_any_ttowasmtime_component_resource_tand could then do whatever indexing they want.- Host embeddings in C could use
wasmtime_component_resource_any_twhen they invoke a wasm function to handle the case that the resource may be owned by wasm and thus needs to behave and operate likeResourceAny.
Ok that's a lot of words and I didn't necessarily directly answer any of your questions. How's that all sound though? I realize this may be pretty opaque if you're new to resources (it took me a long time to get comfortable, and I still need time to "boot back up"). I'm more than happy to help explain anything else too.
alexcrichton added the wasmtime:c-api label to Issue #11437.
alexcrichton added the wasm-proposal:component-model label to Issue #11437.
alexcrichton commented on issue #11437:
My step (3) from above, a relatively meaty step, is done in #11885. I'll be trying to tackle this in the near future next.
fitzgen closed issue #11437:
(Part of the outstanding items of the component model: https://github.com/bytecodealliance/wasmtime/issues/8036#issuecomment-3129666977)
See previous comments: https://github.com/bytecodealliance/wasmtime/pull/11055#issue-3151376309, https://github.com/bytecodealliance/wasmtime/pull/11055#issuecomment-2980677120
My current limited understanding is that there should be a type that holds the resource's
ForeignData, which is then inserted to theResourceTable. Lets call this typeCApiResource.When a function is called, we get a
ResourceAny, that can be thenres.try_into_resource::<CApiResource>(&mut store), and thenstore.data().table.get(&capi_res).But that brings up the question: What should be passed to C?
- Should it be the
ResourceAnysomehow boxed into a C type?- Should it be the contents of the
CApiResourcealready fetched from theResourceTable, so the data pointer to data, and the embedder specified resource type integer?- Should it just be the resource representation
u32index?- Something else?
The same question exists in the reverse direction, when the C API wants to return a new resource.
I also seem to read that
ResourceAny::resource_drop()must be called at the end. Does this mean after calling the embedder's callback, we have to iterate the arguments, match if resource, and call this?Please note that I don't know much about working with resources, so I might be going at this at the wrong way.
Last updated: Dec 06 2025 at 07:03 UTC