Skip to content

Commit

Permalink
Merge pull request #4184 from wasmerio/spawn-with-module
Browse files Browse the repository at this point in the history
Allow `VirtualTaskManager` to explicitly transfer a module to a blocking task on the threadpool
  • Loading branch information
Michael Bryan authored Aug 28, 2023
2 parents 98b1c6a + 7a3a51d commit 417e5bc
Show file tree
Hide file tree
Showing 2 changed files with 69 additions and 15 deletions.
3 changes: 0 additions & 3 deletions .github/workflows/web.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,6 @@ jobs:

- name: Setup Chromedriver
uses: nanasess/setup-chromedriver@v2
# Temporary workaround for https://github.com/nanasess/setup-chromedriver/issues/190
with:
chromedriver-version: '114.0.5735.90'

- name: Show Rust version
run: rustc --version --verbose
Expand Down
81 changes: 69 additions & 12 deletions lib/wasix/src/runtime/task_manager/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,21 @@ impl<'a, 'b> TaskWasm<'a, 'b> {
}
}

/// An implementation of task management
/// A task executor backed by a thread pool.
///
/// ## Thread Safety
///
/// Due to [#4158], it is possible to pass non-thread safe objects across
/// threads by capturing them in the task passed to
/// [`VirtualTaskManager::task_shared()`] or
/// [`VirtualTaskManager::task_dedicated()`].
///
/// If your task needs access to a [`wasmer::Module`], [`wasmer::Memory`], or
/// [`wasmer::Instance`], it should explicitly transfer the objects using
/// either [`VirtualTaskManager::task_wasm()`] when in syscall context or
/// [`VirtualTaskManager::spawn_with_module()`] for higher level code.
///
/// [#4158]: https://github.com/wasmerio/wasmer/issues/4158
#[allow(unused_variables)]
pub trait VirtualTaskManager: std::fmt::Debug + Send + Sync + 'static {
/// Build a new Webassembly memory.
Expand Down Expand Up @@ -130,35 +144,70 @@ pub trait VirtualTaskManager: std::fmt::Debug + Send + Sync + 'static {
}
}

/// Invokes whenever a WASM thread goes idle. In some runtimes (like singlethreaded
/// execution environments) they will need to do asynchronous work whenever the main
/// thread goes idle and this is the place to hook for that.
/// Pause the current thread of execution.
///
/// This is typically invoked whenever a WASM thread goes idle. Besides
/// acting as a platform-agnostic [`std::thread::sleep()`], this also gives
/// the runtime a chance to do asynchronous work like pumping an event
/// loop.
fn sleep_now(
&self,
time: Duration,
) -> Pin<Box<dyn Future<Output = ()> + Send + Sync + 'static>>;

/// Starts an asynchronous task that will run on a shared worker pool
/// This task must not block the execution or it could cause a deadlock
/// Run an asynchronous operation on the thread pool.
///
/// This task must not block execution or it could cause deadlocks.
///
/// See the "Thread Safety" documentation on [`VirtualTaskManager`] for
/// limitations on what a `task` can and can't contain.
fn task_shared(
&self,
task: Box<dyn FnOnce() -> BoxFuture<'static, ()> + Send + 'static>,
) -> Result<(), WasiThreadError>;

/// Starts an WebAssembly task will will run on a dedicated thread
/// pulled from the worker pool that has a stateful thread local variable
/// Run a blocking WebAssembly operation on the thread pool.
///
/// This is primarily used inside the context of a syscall and allows
/// the transfer of things like [`wasmer::Module`] across threads.
fn task_wasm(&self, task: TaskWasm) -> Result<(), WasiThreadError>;

/// Starts an asynchronous task will will run on a dedicated thread
/// pulled from the worker pool. It is ok for this task to block execution
/// and any async futures within its scope
/// Run a blocking operation on the thread pool.
///
/// It is okay for this task to block execution and any async futures within
/// its scope.
fn task_dedicated(
&self,
task: Box<dyn FnOnce() + Send + 'static>,
) -> Result<(), WasiThreadError>;

/// Returns the amount of parallelism that is possible on this platform
/// Returns the amount of parallelism that is possible on this platform.
fn thread_parallelism(&self) -> Result<usize, WasiThreadError>;

/// Schedule a blocking task to run on the threadpool, explicitly
/// transferring a [`Module`] to the task.
///
/// This should be preferred over [`VirtualTaskManager::task_dedicated()`]
/// where possible because [`wasmer::Module`] is actually `!Send` in the
/// browser and can only be transferred to background threads via
/// an explicit `postMessage()`. See [#4158] for more details.
///
/// This is very similar to [`VirtualTaskManager::task_wasm()`], but
/// intended for use outside of a syscall context. For example, when you are
/// running in the browser and want to run a WebAssembly module in the
/// background.
///
/// [#4158]: https://github.com/wasmerio/wasmer/issues/4158
fn spawn_with_module(
&self,
module: Module,
task: Box<dyn FnOnce(Module) + Send + 'static>,
) -> Result<(), WasiThreadError> {
// Note: Ideally, this function and task_wasm() would be superseded by
// a more general mechanism for transferring non-thread safe values
// to the thread pool.
self.task_dedicated(Box::new(move || task(module)))
}
}

impl<D, T> VirtualTaskManager for D
Expand Down Expand Up @@ -202,6 +251,14 @@ where
fn thread_parallelism(&self) -> Result<usize, WasiThreadError> {
(**self).thread_parallelism()
}

fn spawn_with_module(
&self,
module: Module,
task: Box<dyn FnOnce(Module) + Send + 'static>,
) -> Result<(), WasiThreadError> {
(**self).spawn_with_module(module, task)
}
}

impl dyn VirtualTaskManager {
Expand Down

0 comments on commit 417e5bc

Please sign in to comment.