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

Wasi threads #3774

Merged
merged 7 commits into from
Apr 18, 2023
Merged
Show file tree
Hide file tree
Changes from 3 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
12 changes: 12 additions & 0 deletions lib/wasi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,14 @@ pub fn generate_import_object_from_env(
}
}

fn wasi_exports_generic(mut store: &mut impl AsStoreMut, env: &FunctionEnv<WasiEnv>) -> Exports {
use syscalls::*;
let namespace = namespace! {
"thread-spawn" => Function::new_typed_with_env(&mut store, env, thread_spawn_legacy::<Memory32>),
};
namespace
}

fn wasi_unstable_exports(mut store: &mut impl AsStoreMut, env: &FunctionEnv<WasiEnv>) -> Exports {
use syscalls::*;
let namespace = namespace! {
Expand Down Expand Up @@ -416,6 +424,7 @@ fn wasi_unstable_exports(mut store: &mut impl AsStoreMut, env: &FunctionEnv<Wasi
"sock_recv" => Function::new_typed_with_env(&mut store, env, sock_recv::<Memory32>),
"sock_send" => Function::new_typed_with_env(&mut store, env, sock_send::<Memory32>),
"sock_shutdown" => Function::new_typed_with_env(&mut store, env, sock_shutdown),
"thread-spawn" => Function::new_typed_with_env(&mut store, env, thread_spawn_legacy::<Memory32>),
};
namespace
}
Expand Down Expand Up @@ -471,6 +480,7 @@ fn wasi_snapshot_preview1_exports(
"sock_recv" => Function::new_typed_with_env(&mut store, env, sock_recv::<Memory32>),
"sock_send" => Function::new_typed_with_env(&mut store, env, sock_send::<Memory32>),
"sock_shutdown" => Function::new_typed_with_env(&mut store, env, sock_shutdown),
"thread-spawn" => Function::new_typed_with_env(&mut store, env, thread_spawn_legacy::<Memory32>),
};
namespace
}
Expand Down Expand Up @@ -742,6 +752,7 @@ fn import_object_for_all_wasi_versions(
store: &mut impl AsStoreMut,
env: &FunctionEnv<WasiEnv>,
) -> (Imports, ModuleInitializer) {
let exports_wasi_generic = wasi_exports_generic(store, env);
let exports_wasi_unstable = wasi_unstable_exports(store, env);
let exports_wasi_snapshot_preview1 = wasi_snapshot_preview1_exports(store, env);
let exports_wasix_32v1 = wasix_exports_32(store, env);
Expand All @@ -750,6 +761,7 @@ fn import_object_for_all_wasi_versions(
// Allowed due to JS feature flag complications.
#[allow(unused_mut)]
let mut imports = imports! {
"wasi" => exports_wasi_generic,
"wasi_unstable" => exports_wasi_unstable,
"wasi_snapshot_preview1" => exports_wasi_snapshot_preview1,
"wasix_32v1" => exports_wasix_32v1,
Expand Down
2 changes: 2 additions & 0 deletions lib/wasi/src/syscalls/wasi/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ mod poll_oneoff;
mod proc_exit;
mod proc_raise;
mod random_get;
mod thread_spawn;

pub use args_get::*;
pub use args_sizes_get::*;
Expand Down Expand Up @@ -83,5 +84,6 @@ pub use poll_oneoff::*;
pub use proc_exit::*;
pub use proc_raise::*;
pub use random_get::*;
pub use thread_spawn::*;

use tracing::{debug_span, field, instrument, trace_span, Span};
48 changes: 27 additions & 21 deletions lib/wasi/src/syscalls/wasix/thread_spawn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,37 +7,45 @@ use wasmer_wasix_types::wasi::ThreadStart;
/// ### `thread_spawn()`
/// Creates a new thread by spawning that shares the same
/// memory address space, file handles and main event loops.
/// The function referenced by the fork call must be
/// exported by the web assembly process.
///
/// ## Parameters
///
/// * `name` - Name of the function that will be invoked as a new thread
/// * `user_data` - User data that will be supplied to the function when its called
/// * `reactor` - Indicates if the function will operate as a reactor or
/// as a normal thread. Reactors will be repeatable called
/// whenever IO work is available to be processed.
/// * `start_ptr` - Pointer to the structure that describes the thread to be launched
/// * `ret_tid` - ID of the thread that was launched
///
/// ## Return
///
/// Returns the thread index of the newly created thread
/// (indices always start from zero)
#[instrument(level = "debug", skip_all, fields(user_data, stack_base, stack_start, reactor, tid = field::Empty), ret)]
/// (indices always start from the same value as `pid` and increments in steps)
#[instrument(level = "debug", skip_all, ret)]
pub fn thread_spawn<M: MemorySize>(
mut ctx: FunctionEnvMut<'_, WasiEnv>,
start_ptr: WasmPtr<ThreadStart<M>, M>,
ret_tid: WasmPtr<Tid, M>,
) -> Errno {
// Create the thread
let tid = wasi_try!(thread_spawn_internal(&ctx, start_ptr));

// Success
let memory = ctx.data().memory_view(&ctx);
wasi_try_mem!(ret_tid.write(&memory, tid));
Errno::Success
}

pub(crate) fn thread_spawn_internal<M: MemorySize>(
ctx: &FunctionEnvMut<'_, WasiEnv>,
start_ptr: WasmPtr<ThreadStart<M>, M>,
) -> Result<Tid, Errno> {
// Now we use the environment and memory references
let env = ctx.data();
let memory = env.memory_view(&ctx);
let runtime = env.runtime.clone();
let tasks = env.tasks().clone();

// Read the properties about the stack which we will use for asyncify
let start = wasi_try_mem!(start_ptr.read(&memory));
let stack_start: u64 = wasi_try!(start.stack_start.try_into().map_err(|_| Errno::Overflow));
let stack_size: u64 = wasi_try!(start.stack_size.try_into().map_err(|_| Errno::Overflow));
let start = start_ptr.read(&memory).map_err(mem_error_to_wasi)?;
let stack_start: u64 = start.stack_start.try_into().map_err(|_| Errno::Overflow)?;
let stack_size: u64 = start.stack_size.try_into().map_err(|_| Errno::Overflow)?;
let stack_base = stack_start - stack_size;

// Create the handle that represents this thread
Expand All @@ -50,7 +58,7 @@ pub fn thread_spawn<M: MemorySize>(
"failed to create thread handle",
);
// TODO: evaluate the appropriate error code, document it in the spec.
return Errno::Access;
return Err(Errno::Access);
}
};
let thread_id: Tid = thread_handle.id().into();
Expand Down Expand Up @@ -102,14 +110,14 @@ pub fn thread_spawn<M: MemorySize>(

// We need a copy of the process memory and a packaged store in order to
// launch threads and reactors
let thread_memory = wasi_try!(ctx
let thread_memory = ctx
.data()
.memory()
.clone_in_store(&ctx, &mut store)
.ok_or_else(|| {
error!("failed - the memory could not be cloned");
Errno::Notcapable
}));
})?;

// This function calls into the module
let start_ptr_offset = start_ptr.offset();
Expand Down Expand Up @@ -227,7 +235,7 @@ pub fn thread_spawn<M: MemorySize>(
// we can't spawn a background thread
if env.inner().thread_spawn.is_none() {
warn!("thread failed - the program does not export a `wasi_thread_start` function");
return Errno::Notcapable;
return Err(Errno::Notcapable);
}
let spawn_type = crate::runtime::SpawnType::NewThread(thread_memory);

Expand All @@ -242,12 +250,10 @@ pub fn thread_spawn<M: MemorySize>(
execute_module(&mut store, thread_module, &mut thread_memory);
};

wasi_try!(tasks
tasks
.task_wasm(Box::new(task), store, thread_module, spawn_type)
.map_err(|err| { Into::<Errno>::into(err) }));
.map_err(|err| Into::<Errno>::into(err))?;

// Success
let memory = ctx.data().memory_view(&ctx);
wasi_try_mem!(ret_tid.write(&memory, thread_id));
Errno::Success
Ok(thread_id)
}
12 changes: 12 additions & 0 deletions tests/integration/cli/tests/snapshot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -753,6 +753,18 @@ fn test_snapshot_multithreading() {
assert_json_snapshot!(snapshot);
}

// test for traditional wasi threads
#[cfg_attr(any(target_env = "musl", target_os = "windows"), ignore)]
#[test]
fn test_snapshot_wasi_threads() {
let snapshot = TestBuilder::new()
.with_name(function!())
.debug_output(true)
.enable_threads(true)
.run_wasm(include_bytes!("./wasm/wasi-threads.wasm"));
assert_json_snapshot!(snapshot);
}

// multithreading with shared memory access
#[cfg_attr(any(target_env = "musl", target_os = "windows"), ignore)]
#[test]
Expand Down
Binary file not shown.