Stream: git-wasmtime

Topic: wasmtime / issue #11437 c-api: component-model: Resources


view this post on Zulip Wasmtime GitHub notifications bot (Aug 15 2025 at 11:21):

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 the ResourceTable. Lets call this type CApiResource.

When a function is called, we get a ResourceAny, that can be then res.try_into_resource::<CApiResource>(&mut store), and then store.data().table.get(&capi_res).

But that brings up the question: What should be passed to C?

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.

view this post on Zulip Wasmtime GitHub notifications bot (Aug 15 2025 at 15:26):

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> and ResourceAny. 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 and bindgen!-generated bindings. The T type is only used for a TypeId and is pretty arbitrary, but in wasmtime-wasi it's often used to match the type of something stored in a ResourceTable. They need not necessarily be equivalent, however. The other assertion of Resource<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-less Resource<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.

ResourceAny is 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 as ResourceAny has some indexes/connection to host-defined state within Wasmtime (e.g. the "host tables"). This state is severed with Resource<T> but still present with ResourceAny.

IMO neither of these are perfectly suitable for usage in the C API:

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:

  1. wasmtime_component_valunion_t is extended with a resource field which is more-or-less ResourceAny. This would be some sort of opaque structure and boxed up ResourceAny most likely. This would indeed have a requirement that it must be operated on to be properly cleaned up (beyond *_delete to avoid host memory leaks).
  2. wasmtime_component_resource_any_t, let's say, would be C bindings for the ResourceAny type. This notably includes the owned and resource_drop methods. Eventually could bind ty too, but that's ok to not dig in too deeply at this time.
  3. 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) -> ResourceType will be added. This is similar to ResourceType::Host(TypeId) but it'll just be ResourceType::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 ResourceRuntimeTyped will be added. This'll more-or-less be an exact copy of Resource<T> except that type-checks look for ResourceType::RuntimeTyped instead of ResourceType::Host
    • ResourceAny will have new conversion between ResourceRuntimeTyped in the same manner as Resource<T>
    • This effectively means that ResourceAny sort of has two subclasses of Resource<T> and ResourceRuntimeTyped in a sense.
    • The naming "runtime typed" probably wants bikeshedding...
  4. wasmtime_component_resource_any_t will have conversions to/from wasmtime_component_resource_t which would be a binding to ResourceRuntimeTyped in Rust. These conversions would take the type as an argument for example to say "I'm asserting that this ResourceAny is of runtime type 4". Conversion to wasmtime_component_resource_t would "consume" the original ResourceAny and release the host from its destructor requirement in the exact same way as Resource<T>.

  5. wasmtime_component_resource_t would 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.
  6. wasmtime_component_linker_instance_add_resource would be added taking a 32-bit integer which would call LinkerInstance::resource with ResourceType::runtime_typed(...). That would define the host destructor for a resource as well as the type information.
  7. Host functions in C would almost always convert wasmtime_component_resource_any_t to wasmtime_component_resource_t and could then do whatever indexing they want.
  8. Host embeddings in C could use wasmtime_component_resource_any_t when they invoke a wasm function to handle the case that the resource may be owned by wasm and thus needs to behave and operate like ResourceAny.

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.

view this post on Zulip Wasmtime GitHub notifications bot (Aug 21 2025 at 22:59):

alexcrichton added the wasmtime:c-api label to Issue #11437.

view this post on Zulip Wasmtime GitHub notifications bot (Aug 21 2025 at 22:59):

alexcrichton added the wasm-proposal:component-model label to Issue #11437.

view this post on Zulip Wasmtime GitHub notifications bot (Oct 19 2025 at 04:45):

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.

view this post on Zulip Wasmtime GitHub notifications bot (Oct 23 2025 at 00:04):

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 the ResourceTable. Lets call this type CApiResource.

When a function is called, we get a ResourceAny, that can be then res.try_into_resource::<CApiResource>(&mut store), and then store.data().table.get(&capi_res).

But that brings up the question: What should be passed to C?

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