Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/crates/c-api/src/async.rs
3068 views
1
use std::ffi::c_void;
2
use std::future::Future;
3
use std::mem::{self, MaybeUninit};
4
use std::num::NonZeroU64;
5
use std::ops::Range;
6
use std::pin::Pin;
7
use std::sync::Arc;
8
use std::task::{Context, Poll, Waker};
9
use std::{ptr, str};
10
use wasmtime::{
11
AsContextMut, Func, Instance, Result, RootScope, StackCreator, StackMemory, Trap, Val,
12
};
13
14
use crate::{
15
WASMTIME_I32, WasmtimeCaller, WasmtimeStoreContextMut, bad_utf8, handle_result, to_str,
16
translate_args, wasm_config_t, wasm_functype_t, wasm_trap_t, wasmtime_caller_t,
17
wasmtime_error_t, wasmtime_instance_pre_t, wasmtime_linker_t, wasmtime_module_t,
18
wasmtime_val_t, wasmtime_val_union,
19
};
20
21
#[unsafe(no_mangle)]
22
pub extern "C" fn wasmtime_config_async_stack_size_set(c: &mut wasm_config_t, size: usize) {
23
c.config.async_stack_size(size);
24
}
25
26
#[unsafe(no_mangle)]
27
pub extern "C" fn wasmtime_context_epoch_deadline_async_yield_and_update(
28
mut store: WasmtimeStoreContextMut<'_>,
29
delta: u64,
30
) {
31
store.epoch_deadline_async_yield_and_update(delta);
32
}
33
34
#[unsafe(no_mangle)]
35
pub extern "C" fn wasmtime_context_fuel_async_yield_interval(
36
mut store: WasmtimeStoreContextMut<'_>,
37
interval: Option<NonZeroU64>,
38
) -> Option<Box<wasmtime_error_t>> {
39
handle_result(
40
store.fuel_async_yield_interval(interval.map(|n| n.get())),
41
|()| {},
42
)
43
}
44
45
pub type wasmtime_func_async_callback_t = extern "C" fn(
46
*mut c_void,
47
*mut wasmtime_caller_t,
48
*const wasmtime_val_t,
49
usize,
50
*mut wasmtime_val_t,
51
usize,
52
&mut Option<Box<wasm_trap_t>>,
53
&mut wasmtime_async_continuation_t,
54
);
55
56
#[repr(C)]
57
pub struct wasmtime_async_continuation_t {
58
pub callback: wasmtime_func_async_continuation_callback_t,
59
pub env: *mut c_void,
60
pub finalizer: Option<extern "C" fn(*mut c_void)>,
61
}
62
63
unsafe impl Send for wasmtime_async_continuation_t {}
64
unsafe impl Sync for wasmtime_async_continuation_t {}
65
impl Drop for wasmtime_async_continuation_t {
66
fn drop(&mut self) {
67
if let Some(f) = self.finalizer {
68
f(self.env);
69
}
70
}
71
}
72
impl Future for wasmtime_async_continuation_t {
73
type Output = ();
74
fn poll(self: Pin<&mut Self>, _cx: &mut Context) -> Poll<Self::Output> {
75
let this = self.get_mut();
76
let cb = this.callback;
77
if cb(this.env) {
78
Poll::Ready(())
79
} else {
80
Poll::Pending
81
}
82
}
83
}
84
85
/// Internal structure to add Send/Sync to a c_void member.
86
///
87
/// This is useful in closures that need to capture some C data.
88
#[derive(Debug)]
89
struct CallbackDataPtr {
90
pub ptr: *mut std::ffi::c_void,
91
}
92
93
unsafe impl Send for CallbackDataPtr {}
94
unsafe impl Sync for CallbackDataPtr {}
95
96
pub type wasmtime_func_async_continuation_callback_t = extern "C" fn(*mut c_void) -> bool;
97
98
async fn invoke_c_async_callback<'a>(
99
cb: wasmtime_func_async_callback_t,
100
data: CallbackDataPtr,
101
mut caller: WasmtimeCaller<'a>,
102
params: &'a [Val],
103
results: &'a mut [Val],
104
) -> Result<()> {
105
// Convert `params/results` to `wasmtime_val_t`. Use the previous
106
// storage in `hostcall_val_storage` to help avoid allocations all the
107
// time.
108
let mut hostcall_val_storage = mem::take(&mut caller.data_mut().hostcall_val_storage);
109
debug_assert!(hostcall_val_storage.is_empty());
110
hostcall_val_storage.reserve(params.len() + results.len());
111
hostcall_val_storage.extend(
112
params
113
.iter()
114
.cloned()
115
.map(|p| wasmtime_val_t::from_val_unscoped(&mut caller, p)),
116
);
117
hostcall_val_storage.extend((0..results.len()).map(|_| wasmtime_val_t {
118
kind: WASMTIME_I32,
119
of: wasmtime_val_union { i32: 0 },
120
}));
121
let (params, out_results) = hostcall_val_storage.split_at_mut(params.len());
122
123
// Invoke the C function pointer.
124
// The result will be a continuation which we will wrap in a Future.
125
let mut caller = wasmtime_caller_t { caller };
126
let mut trap = None;
127
extern "C" fn panic_callback(_: *mut c_void) -> bool {
128
panic!("callback must be set")
129
}
130
let mut continuation = wasmtime_async_continuation_t {
131
callback: panic_callback,
132
env: ptr::null_mut(),
133
finalizer: None,
134
};
135
cb(
136
data.ptr,
137
&mut caller,
138
params.as_ptr(),
139
params.len(),
140
out_results.as_mut_ptr(),
141
out_results.len(),
142
&mut trap,
143
&mut continuation,
144
);
145
continuation.await;
146
147
if let Some(trap) = trap {
148
return Err(trap.error);
149
}
150
151
// Translate the `wasmtime_val_t` results into the `results` space
152
for (i, result) in out_results.iter().enumerate() {
153
unsafe {
154
results[i] = result.to_val_unscoped(&mut caller.caller);
155
}
156
}
157
// Move our `vals` storage back into the store now that we no longer
158
// need it. This'll get picked up by the next hostcall and reuse our
159
// same storage.
160
hostcall_val_storage.truncate(0);
161
caller.caller.data_mut().hostcall_val_storage = hostcall_val_storage;
162
Ok(())
163
}
164
165
unsafe fn c_async_callback_to_rust_fn(
166
callback: wasmtime_func_async_callback_t,
167
data: *mut c_void,
168
finalizer: Option<extern "C" fn(*mut std::ffi::c_void)>,
169
) -> impl for<'a> Fn(
170
WasmtimeCaller<'a>,
171
&'a [Val],
172
&'a mut [Val],
173
) -> Box<dyn Future<Output = Result<()>> + Send + 'a>
174
+ Send
175
+ Sync
176
+ 'static {
177
let foreign = crate::ForeignData { data, finalizer };
178
move |caller, params, results| {
179
let _ = &foreign; // move entire foreign into this closure
180
let data = CallbackDataPtr { ptr: foreign.data };
181
Box::new(invoke_c_async_callback(
182
callback, data, caller, params, results,
183
))
184
}
185
}
186
187
#[repr(transparent)]
188
pub struct wasmtime_call_future_t<'a> {
189
underlying: Pin<Box<dyn Future<Output = ()> + 'a>>,
190
}
191
192
#[unsafe(no_mangle)]
193
pub extern "C" fn wasmtime_call_future_delete(_future: Box<wasmtime_call_future_t>) {}
194
195
#[unsafe(no_mangle)]
196
pub extern "C" fn wasmtime_call_future_poll(future: &mut wasmtime_call_future_t) -> bool {
197
match future
198
.underlying
199
.as_mut()
200
.poll(&mut Context::from_waker(Waker::noop()))
201
{
202
Poll::Ready(()) => true,
203
Poll::Pending => false,
204
}
205
}
206
207
fn handle_call_error(
208
err: wasmtime::Error,
209
trap_ret: &mut *mut wasm_trap_t,
210
err_ret: &mut *mut wasmtime_error_t,
211
) {
212
if err.is::<Trap>() {
213
*trap_ret = Box::into_raw(Box::new(wasm_trap_t::new(err)));
214
} else {
215
*err_ret = Box::into_raw(Box::new(wasmtime_error_t::from(err)));
216
}
217
}
218
219
async fn do_func_call_async(
220
mut store: RootScope<WasmtimeStoreContextMut<'_>>,
221
func: &Func,
222
args: impl ExactSizeIterator<Item = Val>,
223
results: &mut [MaybeUninit<wasmtime_val_t>],
224
trap_ret: &mut *mut wasm_trap_t,
225
err_ret: &mut *mut wasmtime_error_t,
226
) {
227
let mut params = mem::take(&mut store.as_context_mut().data_mut().wasm_val_storage);
228
let (wt_params, wt_results) = translate_args(&mut params, args, results.len());
229
let result = func.call_async(&mut store, wt_params, wt_results).await;
230
231
match result {
232
Ok(()) => {
233
for (slot, val) in results.iter_mut().zip(wt_results.iter()) {
234
crate::initialize(slot, wasmtime_val_t::from_val(&mut store, *val));
235
}
236
params.truncate(0);
237
store.as_context_mut().data_mut().wasm_val_storage = params;
238
}
239
Err(err) => handle_call_error(err, trap_ret, err_ret),
240
}
241
}
242
243
#[unsafe(no_mangle)]
244
pub unsafe extern "C" fn wasmtime_func_call_async<'a>(
245
store: WasmtimeStoreContextMut<'a>,
246
func: &'a Func,
247
args: *const wasmtime_val_t,
248
nargs: usize,
249
results: *mut MaybeUninit<wasmtime_val_t>,
250
nresults: usize,
251
trap_ret: &'a mut *mut wasm_trap_t,
252
err_ret: &'a mut *mut wasmtime_error_t,
253
) -> Box<wasmtime_call_future_t<'a>> {
254
let mut scope = RootScope::new(store);
255
let args = crate::slice_from_raw_parts(args, nargs)
256
.iter()
257
.map(|i| i.to_val(&mut scope))
258
.collect::<Vec<_>>();
259
let results = crate::slice_from_raw_parts_mut(results, nresults);
260
let fut = Box::pin(do_func_call_async(
261
scope,
262
func,
263
args.into_iter(),
264
results,
265
trap_ret,
266
err_ret,
267
));
268
Box::new(wasmtime_call_future_t { underlying: fut })
269
}
270
271
#[unsafe(no_mangle)]
272
pub unsafe extern "C" fn wasmtime_linker_define_async_func(
273
linker: &mut wasmtime_linker_t,
274
module: *const u8,
275
module_len: usize,
276
name: *const u8,
277
name_len: usize,
278
ty: &wasm_functype_t,
279
callback: crate::wasmtime_func_async_callback_t,
280
data: *mut c_void,
281
finalizer: Option<extern "C" fn(*mut std::ffi::c_void)>,
282
) -> Option<Box<wasmtime_error_t>> {
283
let ty = ty.ty().ty(linker.linker.engine());
284
let module = to_str!(module, module_len);
285
let name = to_str!(name, name_len);
286
let cb = c_async_callback_to_rust_fn(callback, data, finalizer);
287
288
handle_result(
289
linker.linker.func_new_async(module, name, ty, cb),
290
|_linker| (),
291
)
292
}
293
294
async fn do_linker_instantiate_async(
295
linker: &wasmtime_linker_t,
296
store: WasmtimeStoreContextMut<'_>,
297
module: &wasmtime_module_t,
298
instance_ptr: &mut Instance,
299
trap_ret: &mut *mut wasm_trap_t,
300
err_ret: &mut *mut wasmtime_error_t,
301
) {
302
let result = linker.linker.instantiate_async(store, &module.module).await;
303
match result {
304
Ok(instance) => *instance_ptr = instance,
305
Err(err) => handle_call_error(err, trap_ret, err_ret),
306
}
307
}
308
309
#[unsafe(no_mangle)]
310
pub extern "C" fn wasmtime_linker_instantiate_async<'a>(
311
linker: &'a wasmtime_linker_t,
312
store: WasmtimeStoreContextMut<'a>,
313
module: &'a wasmtime_module_t,
314
instance_ptr: &'a mut Instance,
315
trap_ret: &'a mut *mut wasm_trap_t,
316
err_ret: &'a mut *mut wasmtime_error_t,
317
) -> Box<crate::wasmtime_call_future_t<'a>> {
318
let fut = Box::pin(do_linker_instantiate_async(
319
linker,
320
store,
321
module,
322
instance_ptr,
323
trap_ret,
324
err_ret,
325
));
326
Box::new(crate::wasmtime_call_future_t { underlying: fut })
327
}
328
329
async fn do_instance_pre_instantiate_async(
330
instance_pre: &wasmtime_instance_pre_t,
331
store: WasmtimeStoreContextMut<'_>,
332
instance_ptr: &mut Instance,
333
trap_ret: &mut *mut wasm_trap_t,
334
err_ret: &mut *mut wasmtime_error_t,
335
) {
336
let result = instance_pre.underlying.instantiate_async(store).await;
337
match result {
338
Ok(instance) => *instance_ptr = instance,
339
Err(err) => handle_call_error(err, trap_ret, err_ret),
340
}
341
}
342
343
#[unsafe(no_mangle)]
344
pub extern "C" fn wasmtime_instance_pre_instantiate_async<'a>(
345
instance_pre: &'a wasmtime_instance_pre_t,
346
store: WasmtimeStoreContextMut<'a>,
347
instance_ptr: &'a mut Instance,
348
trap_ret: &'a mut *mut wasm_trap_t,
349
err_ret: &'a mut *mut wasmtime_error_t,
350
) -> Box<crate::wasmtime_call_future_t<'a>> {
351
let fut = Box::pin(do_instance_pre_instantiate_async(
352
instance_pre,
353
store,
354
instance_ptr,
355
trap_ret,
356
err_ret,
357
));
358
Box::new(crate::wasmtime_call_future_t { underlying: fut })
359
}
360
361
pub type wasmtime_stack_memory_get_callback_t =
362
extern "C" fn(env: *mut std::ffi::c_void, out_len: &mut usize) -> *mut u8;
363
364
#[repr(C)]
365
pub struct wasmtime_stack_memory_t {
366
env: *mut std::ffi::c_void,
367
get_stack_memory: wasmtime_stack_memory_get_callback_t,
368
finalizer: Option<extern "C" fn(arg1: *mut std::ffi::c_void)>,
369
}
370
371
struct CHostStackMemory {
372
foreign: crate::ForeignData,
373
get_memory: wasmtime_stack_memory_get_callback_t,
374
}
375
unsafe impl Send for CHostStackMemory {}
376
unsafe impl Sync for CHostStackMemory {}
377
unsafe impl StackMemory for CHostStackMemory {
378
fn top(&self) -> *mut u8 {
379
let mut len = 0;
380
let cb = self.get_memory;
381
cb(self.foreign.data, &mut len)
382
}
383
fn range(&self) -> Range<usize> {
384
let mut len = 0;
385
let cb = self.get_memory;
386
let top = cb(self.foreign.data, &mut len);
387
let base = unsafe { top.sub(len) as usize };
388
base..base + len
389
}
390
fn guard_range(&self) -> Range<*mut u8> {
391
std::ptr::null_mut()..std::ptr::null_mut()
392
}
393
}
394
395
pub type wasmtime_new_stack_memory_callback_t = extern "C" fn(
396
env: *mut std::ffi::c_void,
397
size: usize,
398
zeroed: bool,
399
stack_ret: &mut wasmtime_stack_memory_t,
400
) -> Option<Box<wasmtime_error_t>>;
401
402
#[repr(C)]
403
pub struct wasmtime_stack_creator_t {
404
env: *mut std::ffi::c_void,
405
new_stack: wasmtime_new_stack_memory_callback_t,
406
finalizer: Option<extern "C" fn(arg1: *mut std::ffi::c_void)>,
407
}
408
409
struct CHostStackCreator {
410
foreign: crate::ForeignData,
411
new_stack: wasmtime_new_stack_memory_callback_t,
412
}
413
unsafe impl Send for CHostStackCreator {}
414
unsafe impl Sync for CHostStackCreator {}
415
unsafe impl StackCreator for CHostStackCreator {
416
fn new_stack(&self, size: usize, zeroed: bool) -> Result<Box<dyn wasmtime::StackMemory>> {
417
extern "C" fn panic_callback(_env: *mut std::ffi::c_void, _out_len: &mut usize) -> *mut u8 {
418
panic!("a callback must be set");
419
}
420
let mut out = wasmtime_stack_memory_t {
421
env: ptr::null_mut(),
422
get_stack_memory: panic_callback,
423
finalizer: None,
424
};
425
let cb = self.new_stack;
426
let result = cb(self.foreign.data, size, zeroed, &mut out);
427
match result {
428
Some(error) => Err((*error).into()),
429
None => Ok(Box::new(CHostStackMemory {
430
foreign: crate::ForeignData {
431
data: out.env,
432
finalizer: out.finalizer,
433
},
434
get_memory: out.get_stack_memory,
435
})),
436
}
437
}
438
}
439
440
#[unsafe(no_mangle)]
441
pub unsafe extern "C" fn wasmtime_config_host_stack_creator_set(
442
c: &mut wasm_config_t,
443
creator: &wasmtime_stack_creator_t,
444
) {
445
c.config.with_host_stack(Arc::new(CHostStackCreator {
446
foreign: crate::ForeignData {
447
data: creator.env,
448
finalizer: creator.finalizer,
449
},
450
new_stack: creator.new_stack,
451
}));
452
}
453
454