Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

c-api: support yielding in wasmtime_store_epoch_deadline_callback #7476

Merged
merged 5 commits into from
Nov 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 29 additions & 4 deletions crates/c-api/include/wasmtime/store.h
Original file line number Diff line number Diff line change
Expand Up @@ -200,19 +200,44 @@ WASM_API_EXTERN wasmtime_error_t *wasmtime_context_set_wasi(wasmtime_context_t *
*/
WASM_API_EXTERN void wasmtime_context_set_epoch_deadline(wasmtime_context_t *context, uint64_t ticks_beyond_current);

/// \brief An enum for the behavior before extending the epoch deadline.
typedef uint8_t wasmtime_update_deadline_kind_t;
/// \brief Directly continue to updating the deadline and executing WebAssembly.
#define WASMTIME_UPDATE_DEADLINE_CONTINUE 0
/// \brief Yield control (via async support) then update the deadline.
#define WASMTIME_UPDATE_DEADLINE_YIELD 1

/**
* \brief Configures epoch deadline callback to C function.
*
* This function configures a store-local callback function that will be
* called when the running WebAssembly function has exceeded its epoch
* deadline. That function can return a #wasmtime_error_t to terminate
* the function, or set the delta argument and return NULL to update the
* epoch deadline and resume function execution.
* deadline. That function can:
* - return a #wasmtime_error_t to terminate the function
* - set the delta argument and return NULL to update the
* epoch deadline delta and resume function execution.
* - set the delta argument, update the epoch deadline,
* set update_kind to WASMTIME_UPDATE_DEADLINE_YIELD,
* and return NULL to yield (via async support) and
* resume function execution.
*
* To use WASMTIME_UPDATE_DEADLINE_YIELD async support must be enabled
* for this store.
*
* See also #wasmtime_config_epoch_interruption_set and
* #wasmtime_context_set_epoch_deadline.
*/
WASM_API_EXTERN void wasmtime_store_epoch_deadline_callback(wasmtime_store_t *store, wasmtime_error_t* (*func)(wasmtime_context_t*, void*, uint64_t*), void *data);
WASM_API_EXTERN void wasmtime_store_epoch_deadline_callback(
wasmtime_store_t *store,
wasmtime_error_t* (*func)(
wasmtime_context_t* context,
void* data,
uint64_t* epoch_deadline_delta,
wasmtime_update_deadline_kind_t* update_kind
),
void *data,
void (*finalizer)(void*)
);

#ifdef __cplusplus
} // extern "C"
Expand Down
22 changes: 14 additions & 8 deletions crates/c-api/src/async.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,16 +87,22 @@ impl Future for wasmtime_async_continuation_t {
}
}

pub type wasmtime_func_async_continuation_callback_t = extern "C" fn(*mut c_void) -> bool;

struct CallbackData {
env: *mut c_void,
/// Internal structure to add Send/Sync to a c_void member.
///
/// This is useful in closures that need to capture some C data.
#[derive(Debug)]
struct CallbackDataPtr {
pub ptr: *mut std::ffi::c_void,
}
unsafe impl Send for CallbackData {}

unsafe impl Send for CallbackDataPtr {}
unsafe impl Sync for CallbackDataPtr {}

pub type wasmtime_func_async_continuation_callback_t = extern "C" fn(*mut c_void) -> bool;

async fn invoke_c_async_callback<'a>(
cb: wasmtime_func_async_callback_t,
data: CallbackData,
data: CallbackDataPtr,
mut caller: Caller<'a, crate::StoreData>,
params: &'a [Val],
results: &'a mut [Val],
Expand Down Expand Up @@ -127,7 +133,7 @@ async fn invoke_c_async_callback<'a>(
finalizer: None,
};
cb(
data.env,
data.ptr,
&mut caller,
params.as_ptr(),
params.len(),
Expand Down Expand Up @@ -171,7 +177,7 @@ unsafe fn c_async_callback_to_rust_fn(
let foreign = crate::ForeignData { data, finalizer };
move |caller, params, results| {
let _ = &foreign; // move entire foreign into this closure
let data = CallbackData { env: foreign.data };
let data = CallbackDataPtr { ptr: foreign.data };
Box::new(invoke_c_async_callback(
callback, data, caller, params, results,
))
Expand Down
33 changes: 16 additions & 17 deletions crates/c-api/src/store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,20 +106,9 @@ pub extern "C" fn wasmtime_store_new(
})
}

// Internal structure to add Send/Sync to the c_void member.
#[derive(Debug)]
pub struct CallbackDataPtr {
pub ptr: *mut c_void,
}

impl CallbackDataPtr {
fn as_mut_ptr(&self) -> *mut c_void {
self.ptr
}
}

unsafe impl Send for CallbackDataPtr {}
unsafe impl Sync for CallbackDataPtr {}
pub type wasmtime_update_deadline_kind_t = u8;
pub const WASMTIME_UPDATE_DEADLINE_CONTINUE: wasmtime_update_deadline_kind_t = 0;
pub const WASMTIME_UPDATE_DEADLINE_YIELD: wasmtime_update_deadline_kind_t = 1;

#[no_mangle]
pub extern "C" fn wasmtime_store_epoch_deadline_callback(
Expand All @@ -128,22 +117,32 @@ pub extern "C" fn wasmtime_store_epoch_deadline_callback(
CStoreContextMut<'_>,
*mut c_void,
*mut u64,
*mut wasmtime_update_deadline_kind_t,
) -> Option<Box<wasmtime_error_t>>,
data: *mut c_void,
finalizer: Option<extern "C" fn(*mut c_void)>,
) {
let sendable = CallbackDataPtr { ptr: data };
let foreign = crate::ForeignData { data, finalizer };
store.store.epoch_deadline_callback(move |mut store_ctx| {
let _ = &foreign; // Move foreign into this closure
let mut delta: u64 = 0;
let mut kind = WASMTIME_UPDATE_DEADLINE_CONTINUE;
let result = (func)(
store_ctx.as_context_mut(),
sendable.as_mut_ptr(),
foreign.data,
&mut delta as *mut u64,
&mut kind as *mut wasmtime_update_deadline_kind_t,
);
match result {
Some(err) => Err(wasmtime::Error::from(<wasmtime_error_t as Into<
anyhow::Error,
>>::into(*err))),
None => Ok(UpdateDeadline::Continue(delta)),
None if kind == WASMTIME_UPDATE_DEADLINE_CONTINUE => {
Ok(UpdateDeadline::Continue(delta))
}
#[cfg(feature = "async")]
None if kind == WASMTIME_UPDATE_DEADLINE_YIELD => Ok(UpdateDeadline::Yield(delta)),
_ => panic!("unknown wasmtime_update_deadline_kind_t: {}", kind),
}
});
}
Expand Down
Loading