Skip to content

Commit dbc21cd

Browse files
authored
Support resources in the C API of Wasmtime (#11920)
This commit builds on #11885 to build out support for resources in components in the C API of Wasmtime. Support is effectively the same as in Rust except more things are behind pointers and `ResourceDynamic` is used under the hood instead of `Resource<T>` due to the lack of monomorphization. Tests have been updated to go through some various situations of ensuring that guest and host resources are representable and can be manipulated.
1 parent d335c07 commit dbc21cd

File tree

8 files changed

+1006
-46
lines changed

8 files changed

+1006
-46
lines changed

crates/c-api/include/wasmtime/component/linker.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,29 @@ wasmtime_component_linker_add_wasip2(wasmtime_component_linker_t *linker);
157157

158158
#endif // WASMTIME_FEATURE_WASI
159159

160+
/// Type of the callback used in
161+
/// #wasmtime_component_linker_instance_add_resource
162+
typedef wasmtime_error_t *(*wasmtime_component_resource_destructor_t)(
163+
void *, wasmtime_context_t *, uint32_t);
164+
165+
/**
166+
* \brief Defines a new resource type within this instance.
167+
*
168+
* This can be used to define a new resource type that the guest will be able
169+
* to import. Here the `resource` is a type, often a host-defined type, which
170+
* can be used to distinguish and definie different types of resources. A
171+
* destruction callback is also specified via `destructor` which has private
172+
* data `data` along with an optional `finalizer` for the `data` too.
173+
*
174+
* \return on success `NULL`, otherwise an error
175+
*/
176+
WASM_API_EXTERN wasmtime_error_t *
177+
wasmtime_component_linker_instance_add_resource(
178+
wasmtime_component_linker_instance_t *linker_instance, const char *name,
179+
size_t name_len, const wasmtime_component_resource_type_t *resource,
180+
wasmtime_component_resource_destructor_t destructor, void *data,
181+
void (*finalizer)(void *));
182+
160183
/**
161184
* \brief Deletes a #wasmtime_component_linker_instance_t
162185
*

crates/c-api/include/wasmtime/component/linker.hh

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,42 @@ public:
116116
return Error(error);
117117
}
118118

119+
return std::monostate();
120+
}
121+
122+
private:
123+
template <typename F>
124+
static wasmtime_error_t *
125+
raw_resource_destructor_callback(void *env, wasmtime_context_t *store,
126+
uint32_t rep) {
127+
F *func = reinterpret_cast<F *>(env);
128+
Result<std::monostate> result = (*func)(Store::Context(store), rep);
129+
if (!result) {
130+
return result.err().release();
131+
}
132+
return nullptr;
133+
}
134+
135+
public:
136+
/// \brief Defines a new resource in this linker with the provided
137+
/// destructor.
138+
template <typename F,
139+
std::enable_if_t<std::is_invocable_r_v<Result<std::monostate>, F,
140+
Store::Context, uint32_t>,
141+
bool> = true>
142+
Result<std::monostate> add_resource(std::string_view name,
143+
const ResourceType &ty, F &&f) {
144+
auto *error = wasmtime_component_linker_instance_add_resource(
145+
ptr.get(), name.data(), name.length(), ty.capi(),
146+
raw_resource_destructor_callback<std::remove_reference_t<F>>,
147+
std::make_unique<std::remove_reference_t<F>>(std::forward<F>(f))
148+
.release(),
149+
raw_finalize<std::remove_reference_t<F>>);
150+
151+
if (error != nullptr) {
152+
return Error(error);
153+
}
154+
119155
return std::monostate();
120156
}
121157
};

crates/c-api/include/wasmtime/component/val.h

Lines changed: 238 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,243 @@
99

1010
#include <stdint.h>
1111
#include <wasm.h>
12+
#include <wasmtime/store.h>
1213

1314
#ifdef __cplusplus
1415
extern "C" {
1516
#endif
1617

18+
/// \brief Represents the type of a component resource.
19+
///
20+
/// This is an opaque structure which represents the type of a resource. This
21+
/// can be used to equate the type of two resources together to see if they are
22+
/// the same.
23+
typedef struct wasmtime_component_resource_type
24+
wasmtime_component_resource_type_t;
25+
26+
/// \brief Creates a new resource type representing a host-defined resource.
27+
///
28+
/// This function creates a new `wasmtime_component_resource_type_t` which
29+
/// represents a host-defined resource identified by the `ty` integer argument
30+
/// provided. Two host resources with different `ty` arguments are considered
31+
/// not-equal in terms of resource types. Through this the host can create
32+
/// distinct types of resources at runtime to ensure that components are also
33+
/// required to keep resources distinct.
34+
///
35+
/// The pointer returned from this function must be deallocated with
36+
/// `wasmtime_component_resource_type_delete`.
37+
WASM_API_EXTERN
38+
wasmtime_component_resource_type_t *
39+
wasmtime_component_resource_type_new_host(uint32_t ty);
40+
41+
/// \brief Clones a resource type.
42+
///
43+
/// Creates a new owned copy of a resource type.
44+
///
45+
/// The pointer returned from this function must be deallocated with
46+
/// `wasmtime_component_resource_type_delete`.
47+
WASM_API_EXTERN
48+
wasmtime_component_resource_type_t *wasmtime_component_resource_type_clone(
49+
const wasmtime_component_resource_type_t *ty);
50+
51+
/// \brief Compares two resource types for equality.
52+
///
53+
/// Returns whether `a` and `b` point to logically the same resource type under
54+
/// the hood.
55+
WASM_API_EXTERN
56+
bool wasmtime_component_resource_type_equal(
57+
const wasmtime_component_resource_type_t *a,
58+
const wasmtime_component_resource_type_t *b);
59+
60+
/// \brief Deallocates a resource type.
61+
///
62+
/// This will deallocate the pointer `resource` any any memory that it might
63+
/// own.
64+
WASM_API_EXTERN
65+
void wasmtime_component_resource_type_delete(
66+
wasmtime_component_resource_type_t *resource);
67+
68+
/// \brief Represents a component resource which can be either guest-owned or
69+
/// host-owned.
70+
///
71+
/// This type is an opaque type used to represent any component model resource.
72+
/// Internally this tracks information about ownership, type, etc. Values of
73+
/// this type have dynamic ownership guarantees associated with them. Notably
74+
/// from a component-model perspective values of this type must either be
75+
/// converted to a host resource with `wasmtime_component_resource_any_to_host`
76+
/// or dropped via `wasmtime_component_resource_any_drop`. This is required to
77+
/// handle various metadata tracking appropriately, and if this is not done
78+
/// then the resource will be leaked into the store and a trap may be raised.
79+
///
80+
/// Note that this type also has dynamic memory allocations associated with it
81+
/// and users must call `wasmtime_component_resource_any_delete` to deallocate
82+
/// the host-side resources. This destructor can be called in an RAII fashion
83+
/// and will only clean up memory, not metadata related to the resource.
84+
/// It is required to call `wasmtime_component_resource_any_delete` to prevent
85+
/// leaking memory on the host. It's highly recommended to call
86+
/// `wasmtime_component_resource_any_drop` to avoid leaking memory in a
87+
/// long-lived store, but if this is forgotten then deallocating the store will
88+
/// deallocate all memory still.
89+
typedef struct wasmtime_component_resource_any
90+
wasmtime_component_resource_any_t;
91+
92+
/// \brief Gets the type of a component resource.
93+
///
94+
/// Returns an owned `wasmtime_component_resource_type_t` which represents the
95+
/// type of this resource.
96+
///
97+
/// The pointer returned from this function must be deallocated with
98+
/// `wasmtime_component_resource_type_delete`.
99+
WASM_API_EXTERN
100+
wasmtime_component_resource_type_t *wasmtime_component_resource_any_type(
101+
const wasmtime_component_resource_any_t *resource);
102+
103+
/// \brief Clones a component resource.
104+
///
105+
/// Creates a new owned copy of a component resource. Note that the returned
106+
/// resource still logically refers to the same resource as before, but this
107+
/// can be convenient from an API perspective. Calls to
108+
/// `wasmtime_component_resource_any_drop` need only happen
109+
/// once-per-logical-resource, not once-per-handle-to-the-resource. Note though
110+
/// that calls to `wasmtime_component_resource_any_delete` must happen
111+
/// once-per-handle-to-the-resource.
112+
///
113+
/// The pointer returned from this function must be deallocated with
114+
/// `wasmtime_component_resource_any_delete`.
115+
WASM_API_EXTERN
116+
wasmtime_component_resource_any_t *wasmtime_component_resource_any_clone(
117+
const wasmtime_component_resource_any_t *resource);
118+
119+
/// \brief Returns whether this resource is an `own`, or a `borrow` in the
120+
/// component model.
121+
WASM_API_EXTERN
122+
bool wasmtime_component_resource_any_owned(
123+
const wasmtime_component_resource_any_t *resource);
124+
125+
/// \brief Drops a component resource.
126+
///
127+
/// This function is required to be called per "logical resource" to clean up
128+
/// any borrow-tracking state in the store, for example. Additionally this may
129+
/// invoke WebAssembly if it's a guest-owned resource with a destructor
130+
/// associated with it.
131+
///
132+
/// This operation is not to be confused with
133+
/// `wasmtime_component_resource_any_delete` which deallocates host-related
134+
/// memory for this resource. After `wasmtime_component_resource_any_drop` is
135+
/// called it's still required to call
136+
/// `wasmtime_component_resource_any_delete`.
137+
WASM_API_EXTERN
138+
wasmtime_error_t *wasmtime_component_resource_any_drop(
139+
wasmtime_context_t *ctx, const wasmtime_component_resource_any_t *resource);
140+
141+
/// \brief Deallocates a component resource.
142+
///
143+
/// This function deallocates any host-side memory associated with this
144+
/// resource. This function does not perform any component-model related
145+
/// cleanup, and `wasmtime_component_resource_any_drop` is required for that.
146+
WASM_API_EXTERN
147+
void wasmtime_component_resource_any_delete(
148+
wasmtime_component_resource_any_t *resource);
149+
150+
/// \brief Represents a host-defined component resource.
151+
///
152+
/// This structure is similar to `wasmtime_component_resource_any_t` except
153+
/// that it unconditionally represents an embedder-defined resource via this
154+
/// API. Host resources have a "rep" which is a 32-bit integer whose meaning
155+
/// is defined by the host. This "rep" is trusted in the sense that the guest
156+
/// cannot forge this so the embedder is the only one that can view this.
157+
///
158+
/// Host resources also have a 32-bit type whose meaning is also defined by the
159+
/// host and has no meaning internally. This is used to distinguish different
160+
/// types of resources from one another.
161+
///
162+
/// Also note that unlike `wasmtime_component_resource_any_t` host resources
163+
/// do not have a "drop" operation. It's up to the host to define what it means
164+
/// to drop an owned resource and handle that appropriately.
165+
typedef struct wasmtime_component_resource_host
166+
wasmtime_component_resource_host_t;
167+
168+
/// \brief Creates a new host-defined component resource.
169+
///
170+
/// This function creates a new host-defined component resource with the
171+
/// provided parameters. The `owned` parameter indicates whether this resource
172+
/// is an `own` or a `borrow` in the component model. The `rep` and `ty`
173+
/// parameters are 32-bit integers which only have meaning to the embedder and
174+
/// are plumbed through with this resource.
175+
///
176+
/// The pointer returned from this function must be deallocated with
177+
/// `wasmtime_component_resource_host_delete`.
178+
WASM_API_EXTERN
179+
wasmtime_component_resource_host_t *
180+
wasmtime_component_resource_host_new(bool owned, uint32_t rep, uint32_t ty);
181+
182+
/// \brief Clones a host-defined component resource.
183+
///
184+
/// Creates a new owned copy of a host-defined component resource. Note that the
185+
/// returned resource still logically refers to the same resource as before,
186+
/// but this can be convenient from an API perspective.
187+
///
188+
/// The pointer returned from this function must be deallocated with
189+
/// `wasmtime_component_resource_host_delete`.
190+
WASM_API_EXTERN
191+
wasmtime_component_resource_host_t *wasmtime_component_resource_host_clone(
192+
const wasmtime_component_resource_host_t *resource);
193+
194+
/// \brief Gets the "rep" of a host-defined component resource.
195+
///
196+
/// Returns the 32-bit integer "rep" associated with this resource. This is a
197+
/// trusted value that guests cannot forge.
198+
WASM_API_EXTERN
199+
uint32_t wasmtime_component_resource_host_rep(
200+
const wasmtime_component_resource_host_t *resource);
201+
202+
/// \brief Gets the "type" of a host-defined component resource.
203+
///
204+
/// Returns the 32-bit integer "type" associated with this resource. This is a
205+
/// trusted value that guests cannot forge.
206+
WASM_API_EXTERN
207+
uint32_t wasmtime_component_resource_host_type(
208+
const wasmtime_component_resource_host_t *resource);
209+
210+
/// \brief Returns whether this host-defined resource is an `own` or a `borrow`
211+
/// in the component model.
212+
WASM_API_EXTERN
213+
bool wasmtime_component_resource_host_owned(
214+
const wasmtime_component_resource_host_t *resource);
215+
216+
/// \brief Deallocates a host-defined component resource.
217+
///
218+
/// This function deallocates any host-side memory associated with this
219+
/// resource.
220+
WASM_API_EXTERN
221+
void wasmtime_component_resource_host_delete(
222+
wasmtime_component_resource_host_t *resource);
223+
224+
/// \brief Attempts to convert a `wasmtime_component_resource_any_t` into a
225+
/// `wasmtime_component_resource_host_t`.
226+
///
227+
/// This function will attempt to convert the provided `resource` into a
228+
/// host-defined resource. If the resource is indeed host-defined then a new
229+
/// owned `wasmtime_component_resource_host_t` is returned via `ret`. If the
230+
/// resource is guest-defined then an error is returned and `ret` is not
231+
/// modified.
232+
///
233+
/// If no error is returned then the pointer written to `ret` must be
234+
/// deallocated with `wasmtime_component_resource_host_delete`.
235+
WASM_API_EXTERN
236+
wasmtime_error_t *wasmtime_component_resource_any_to_host(
237+
wasmtime_context_t *ctx, const wasmtime_component_resource_any_t *resource,
238+
wasmtime_component_resource_host_t **ret);
239+
240+
/// \brief Same as `wasmtime_component_resource_any_to_host` except for
241+
/// converting the other way around.
242+
///
243+
/// This can fail in some edge-case scenarios but typically does not fail.
244+
WASM_API_EXTERN
245+
wasmtime_error_t *wasmtime_component_resource_host_to_any(
246+
wasmtime_context_t *ctx, const wasmtime_component_resource_host_t *resource,
247+
wasmtime_component_resource_any_t **ret);
248+
17249
/// \brief Discriminant used in #wasmtime_component_val_t::kind
18250
typedef uint8_t wasmtime_component_valkind_t;
19251

@@ -80,6 +312,9 @@ typedef uint8_t wasmtime_component_valkind_t;
80312
/// \brief Value of #wasmtime_component_valkind_t meaning that
81313
/// #wasmtime_component_val_t is flags
82314
#define WASMTIME_COMPONENT_FLAGS 20
315+
/// \brief Value of #wasmtime_component_valkind_t meaning that
316+
/// #wasmtime_component_val_t is a resource
317+
#define WASMTIME_COMPONENT_RESOURCE 21
83318

84319
struct wasmtime_component_val;
85320
struct wasmtime_component_valrecord_entry;
@@ -180,6 +415,9 @@ typedef union {
180415
wasmtime_component_valresult_t result;
181416
/// Field used if #wasmtime_component_val_t::kind is #WASMTIME_COMPONENT_FLAGS
182417
wasmtime_component_valflags_t flags;
418+
/// Field used if #wasmtime_component_val_t::kind is
419+
/// #WASMTIME_COMPONENT_RESOURCE
420+
wasmtime_component_resource_any_t *resource;
183421
} wasmtime_component_valunion_t;
184422

185423
/// \brief Represents possible runtime values which a component function can

0 commit comments

Comments
 (0)