Skip to content

Instantly share code, notes, and snippets.

@dicej
Last active February 4, 2025 13:16
Show Gist options
  • Save dicej/21519f9cf2e4d57a3316ea0b2167d281 to your computer and use it in GitHub Desktop.
Save dicej/21519f9cf2e4d57a3316ea0b2167d281 to your computer and use it in GitHub Desktop.
Sketch of alternative `wasmtime-wit-bindgen` `concurrent-imports` approach based on thread-local storage
// Somewhere in Wasmtime:
thread_local! {
static LOCAL_STORE: Cell<*mut dyn VMStore> = Cell::new(ptr::null_mut());
}
pub struct LocalStore<'a, T> {
// We only use `T` in the `with` method below.
_phatom_type: PhantomData<T>,
// 'a is a dummy lifetime to discourage application code from storing an
// `LocalStore` instance and using it outside of the `async fn` it was
// passed to.
_phantom_lifetime: PhantomData<&'a ()>,
}
impl<'a, T> LocalStore<'a, T> {
/// Run the specified function using the current store and return the
/// result.
///
/// This will only succeed if called from a `Future` being polled by the
/// Wasmtime runtime as part of an async host function which was registered
/// using `LinkerInstance::func_wrap_concurrent`; otherwise it will panic.
fn with<R>(&self, fun: impl FnOnce(StoreContextMut<'_, T>) -> R) -> R {
LOCAL_STORE.with(|store| {
assert!(!store.get().is_null());
fun(unsafe { StoreContextMut::<T>(&mut *store.get().cast()) })
})
}
}
// Elsewhere in wasmtime:
// the function where we poll host function futures for progress
fn poll_loop<T>(store: StoreContextMut<T> /* ...*/) -> Result<()> {
// ...
let ready = LOCAL_STORE.with(|s| {
// set the thread local to point to a valid store
s.set(store.0.traitobj().as_ptr());
// poll the host function futures
let cx = AsyncCx::new(&mut store);
let mut future = pin!(store.concurrent_state().futures.next());
let ready = unsafe { cx.poll(future.as_mut()) };
// Reset the thread local to null.
// In reality, we'd use RAII to ensure this line is always run:
s.set(ptr::null_mut());
ready
});
// ...
}
// In `bindgen!`-generated code:
trait Host {
// ...
async fn foo(local_store: LocalStore<'_, Self>, param: String) -> Result<String>;
// ...
}
// In application code:
struct MyHost {/*...*/}
impl Host for MyHost {
// ...
async fn foo(local_store: LocalStore<'_, Self>, param: String) -> Result<String> {
do_something(param).await?;
// Note that the API of `LocalStore::with` prevents returning anything
// from the closure that uses the `StoreContextMut`'s lifetime, thus
// preventing `Store`-based references from being held across `await`
// points:
let bar = local_store.with(|store| get_something_from_the_store(store))?;
let baz = do_something_else(bar).await?;
let result = local_store.with(|store| put_something_into_the_store(store, baz))?;
Ok(result)
}
// ...
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment