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

Implemented an asyncify based implementation of asynchronous threading #3710

Merged
merged 72 commits into from
May 15, 2023
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
72 commits
Select commit Hold shift + click to select a range
00b41ab
Implemented the draft asyncify implementation for deep sleeping - a.k…
john-sharratt Mar 21, 2023
1d90f40
Merge remote-tracking branch 'origin/master' into asynchronous-threading
john-sharratt Mar 21, 2023
beceafe
cargo clippy --fix
john-sharratt Mar 21, 2023
4958ad4
More clippy fixes
john-sharratt Mar 21, 2023
69bb9ff
Fixed the linting errors
john-sharratt Mar 22, 2023
8dbdac2
Fixed some more clippy linting issues
john-sharratt Mar 22, 2023
0eaed79
Added an opt-out on asynchronous threading
john-sharratt Mar 22, 2023
65a850c
Updated the version of spin used for heapless which had been yanked
john-sharratt Mar 22, 2023
ccd852b
Refactored parts of asynchronous threading to fix snapshot tests
john-sharratt Mar 22, 2023
a8cad0c
Fixes a bug which causes libc to thrash the futex_wake_all because it…
john-sharratt Mar 22, 2023
acbdb03
Also fixed the futex_wake once call
john-sharratt Mar 22, 2023
235b57e
Merge branch 'futex-fix' into async-threads-refactor
john-sharratt Mar 23, 2023
9e9cdef
Refactoring of the async threading implementation to fix the snapshot…
john-sharratt Mar 23, 2023
262e049
Merge remote-tracking branch 'origin/master' into asynchronous-threading
john-sharratt Mar 29, 2023
5ffeb68
Fixes for clippy lints
john-sharratt Mar 30, 2023
89d3d3e
More linting fixes
john-sharratt Mar 30, 2023
0ed9ced
More linting fixes
john-sharratt Mar 30, 2023
525c189
Updated all the tests so that there are normal tests without async th…
john-sharratt Mar 30, 2023
748f6ae
Added a comment to retrigger the integration tests
john-sharratt Mar 30, 2023
9737c7d
Merge branch 'master' into asynchronous-threading
john-sharratt Mar 30, 2023
65d63fd
Merge branch 'asynchronous-threading' of github.com:wasmerio/wasmer i…
john-sharratt Mar 30, 2023
d5dece7
Flips from opt-out to opt-in for asynchronous threading
john-sharratt Mar 30, 2023
435bd7f
Merge branch 'master' into asynchronous-threading
john-sharratt Mar 30, 2023
66012f2
Merge branch 'asynchronous-threading' of github.com:wasmerio/wasmer i…
john-sharratt Mar 30, 2023
83e1159
Removed a redundant function(s)
john-sharratt Mar 31, 2023
1855c7f
Merge remote-tracking branch 'origin/master' into asynchronous-threading
john-sharratt Apr 1, 2023
f25c4ce
Merge branch 'master' into asynchronous-threading
john-sharratt Apr 2, 2023
e774697
Merge remote-tracking branch 'origin/master' into asynchronous-threading
john-sharratt Apr 7, 2023
6350cb1
Fixed compile errors after merge
john-sharratt Apr 7, 2023
eb8cfec
Updated the unit tests and fixed many of them
john-sharratt Apr 7, 2023
9867a77
Rewrote the async web server snapshot tests
john-sharratt Apr 7, 2023
c7ad86e
Fixed the signalling system which was causing spurious intr exits
john-sharratt Apr 7, 2023
1118937
Fixed some linting errors
john-sharratt Apr 7, 2023
37ef38e
Merge branch 'master' into asynchronous-threading
john-sharratt Apr 7, 2023
8c20bf6
Merge branch 'master' into asynchronous-threading
john-sharratt Apr 7, 2023
dc7b04f
Merge branch 'master' into asynchronous-threading
john-sharratt Apr 7, 2023
c4d500a
Lint fixes on some of the tests
john-sharratt Apr 7, 2023
4367920
More linting fixes
john-sharratt Apr 7, 2023
49f5360
Merge branch 'master' into asynchronous-threading
john-sharratt Apr 7, 2023
85ad071
Refactored async threading so that it works with the browser
john-sharratt Apr 8, 2023
19c4c62
Fixed the integration tests
john-sharratt Apr 14, 2023
276e0c2
Disable async threading for the browser for now
john-sharratt Apr 14, 2023
bf77986
Merge branch 'master' into asynchronous-threading
john-sharratt Apr 14, 2023
5899c06
Removed some redundant code
john-sharratt Apr 14, 2023
38425b1
Fixed the linting issues
john-sharratt Apr 14, 2023
82eab8c
Renamed some things and simplified the worker.js
john-sharratt Apr 14, 2023
d6d2e25
Converted WasiEnv so that it no longer has the unsafe send on it
john-sharratt Apr 14, 2023
c6cd14b
Fixed some lint errors
john-sharratt Apr 14, 2023
4762c58
Fixed some linting issues
john-sharratt Apr 14, 2023
f7a93b9
More fixes after removing the cloning of inner
john-sharratt Apr 14, 2023
2fe30db
Hidden the method and fixes some regression issues
john-sharratt Apr 14, 2023
cdbc9ce
Fixed deep sleep for the futux waits
john-sharratt Apr 14, 2023
7d8b70e
Fixed a race condition on futex and hidden the instance methods a bit…
john-sharratt Apr 14, 2023
31ce0ab
Fixed a race condition on the futex
john-sharratt Apr 15, 2023
44916a3
Disabled asynchronous threading for spawned threads on the browser fo…
john-sharratt Apr 15, 2023
e9a3a7a
Merge branch 'master' into asynchronous-threading
john-sharratt Apr 15, 2023
13b4d72
Removed the error enhancements so that the API is backwards compatible
john-sharratt Apr 15, 2023
d2ca673
Fixed the linting issues
john-sharratt Apr 15, 2023
4b7ddf1
Moved some of the rewind logic to its own file
john-sharratt Apr 16, 2023
65cda35
Merge branch 'master' into asynchronous-threading
john-sharratt Apr 16, 2023
6789bc9
The tests now run for longer and with a higher timeout so they don't …
john-sharratt Apr 16, 2023
d48f401
Merge branch 'master' into asynchronous-threading
john-sharratt Apr 18, 2023
da12bf1
Fixed linting issues
john-sharratt Apr 19, 2023
6c7e47b
Removed the store_id functions that were added
john-sharratt Apr 19, 2023
59c8a45
Changed the store reference
john-sharratt Apr 19, 2023
a064b9d
Made two of the tests more deterministic
john-sharratt Apr 19, 2023
499eb49
Reverted some unused changes
john-sharratt Apr 19, 2023
a5c03b4
Changes to address comments
john-sharratt Apr 19, 2023
83970e3
Fixed the merge conflicts after merging from master
john-sharratt May 11, 2023
0757f35
Merge branch 'master' into asynchronous-threading
john-sharratt May 13, 2023
f659e44
Fixed review comments
john-sharratt May 13, 2023
cd5bc2a
Fixed a compile error after the backport
john-sharratt May 13, 2023
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
2 changes: 1 addition & 1 deletion lib/api/src/externals/memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ impl Memory {

/// Creates a view into the memory that then allows for
/// read and write
pub fn view<'a>(&self, store: &'a impl AsStoreRef) -> MemoryView<'a> {
pub fn view<'a>(&self, store: &'a (impl AsStoreRef + ?Sized)) -> MemoryView<'a> {
MemoryView::new(self, store)
}

Expand Down
2 changes: 1 addition & 1 deletion lib/api/src/externals/memory_view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use crate::sys::externals::memory_view as memory_view_impl;
pub struct MemoryView<'a>(pub(crate) memory_view_impl::MemoryView<'a>);

impl<'a> MemoryView<'a> {
pub(crate) fn new(memory: &Memory, store: &'a impl AsStoreRef) -> Self {
pub(crate) fn new(memory: &Memory, store: &'a (impl AsStoreRef + ?Sized)) -> Self {
MemoryView(memory_view_impl::MemoryView::new(&memory.0, store))
}

Expand Down
2 changes: 1 addition & 1 deletion lib/api/src/js/externals/memory_view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ pub struct MemoryView<'a> {
}

impl<'a> MemoryView<'a> {
pub(crate) fn new(memory: &Memory, _store: &'a impl AsStoreRef) -> Self {
pub(crate) fn new(memory: &Memory, _store: &'a (impl AsStoreRef + ?Sized)) -> Self {
Self::new_raw(&memory.handle.memory)
}

Expand Down
2 changes: 1 addition & 1 deletion lib/api/src/ptr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ pub type WasmPtr64<T> = WasmPtr<T, Memory64>;
#[repr(transparent)]
pub struct WasmPtr<T, M: MemorySize = Memory32> {
offset: M::Offset,
_phantom: PhantomData<*mut T>,
john-sharratt marked this conversation as resolved.
Show resolved Hide resolved
_phantom: PhantomData<T>,
}

impl<T, M: MemorySize> WasmPtr<T, M> {
Expand Down
2 changes: 1 addition & 1 deletion lib/api/src/sys/externals/memory_view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ pub struct MemoryView<'a> {
}

impl<'a> MemoryView<'a> {
pub(crate) fn new(memory: &Memory, store: &'a impl AsStoreRef) -> Self {
pub(crate) fn new(memory: &Memory, store: &'a (impl AsStoreRef + ?Sized)) -> Self {
let size = memory.handle.get(store.as_store_ref().objects()).size();

let definition = memory.handle.get(store.as_store_ref().objects()).vmmemory();
Expand Down
4 changes: 2 additions & 2 deletions lib/cli/src/commands/run/wasi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use std::{
sync::Arc,
};
use virtual_fs::{DeviceFile, FileSystem, PassthruFileSystem, RootFileSystemBuilder};
use wasmer::{AsStoreMut, Instance, Module, RuntimeError, Value};
use wasmer::{AsStoreMut, Instance, Module, RuntimeError, Store, Value};
use wasmer_wasix::{
default_fs_backing, get_wasi_versions,
os::{tty_sys::SysTty, TtyBridge},
Expand Down Expand Up @@ -215,7 +215,7 @@ impl Wasi {
/// Helper function for instantiating a module with Wasi imports for the `Run` command.
pub fn instantiate(
&self,
store: &mut impl AsStoreMut,
store: &mut Store,
john-sharratt marked this conversation as resolved.
Show resolved Hide resolved
module: &Module,
program_name: String,
args: Vec<String>,
Expand Down
9 changes: 9 additions & 0 deletions lib/types/src/memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,9 @@ pub unsafe trait MemorySize: Copy {

/// Convert a `Native` to an `Offset`.
fn native_to_offset(native: Self::Native) -> Self::Offset;

/// True if the memory is 64-bit
fn is_64bit() -> bool;
}

/// Marker trait for 32-bit memories.
Expand All @@ -113,6 +116,9 @@ unsafe impl MemorySize for Memory32 {
fn native_to_offset(native: Self::Native) -> Self::Offset {
native as Self::Offset
}
fn is_64bit() -> bool {
false
}
}

/// Marker trait for 64-bit memories.
Expand All @@ -129,4 +135,7 @@ unsafe impl MemorySize for Memory64 {
fn native_to_offset(native: Self::Native) -> Self::Offset {
native as Self::Offset
}
fn is_64bit() -> bool {
true
}
}
8 changes: 4 additions & 4 deletions lib/wasi-types/src/wasi/wasix_manual.rs
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ unsafe impl ValueType for StackSnapshot {
#[repr(C)]
#[derive(Clone, Copy)]
pub union JoinStatusUnion {
pub nothing: u8,
pub nothing_errno: Errno,
pub exit_normal: Errno,
pub exit_signal: ErrnoSignal,
pub stopped: Signal,
Expand All @@ -196,7 +196,7 @@ impl core::fmt::Debug for JoinStatus {
let mut f = binding.field("tag", &self.tag);
f = unsafe {
match self.tag {
JoinStatusType::Nothing => f.field("pid", &self.u.nothing),
JoinStatusType::Nothing => f.field("nothing_errno", &self.u.nothing_errno),
JoinStatusType::ExitNormal => f.field("exit_normal", &self.u.exit_normal),
JoinStatusType::ExitSignal => f.field("exit_signal", &self.u.exit_signal),
JoinStatusType::Stopped => f.field("stopped", &self.u.stopped),
Expand All @@ -214,7 +214,7 @@ unsafe impl ValueType for JoinStatus {
#[repr(C)]
#[derive(Copy, Clone)]
pub struct ThreadStart<M: MemorySize> {
pub stack_start: M::Offset,
pub stack_upper: M::Offset,
pub tls_base: M::Offset,
pub start_funct: M::Offset,
pub start_args: M::Offset,
Expand All @@ -225,7 +225,7 @@ pub struct ThreadStart<M: MemorySize> {
impl<M: MemorySize> core::fmt::Debug for ThreadStart<M> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("ThreadStart")
.field("stack_start", &self.stack_start)
.field("stack_upper", &self.stack_upper)
.field("tls-base", &self.tls_base)
.field("start-funct", &self.start_funct)
.field("start-args", &self.start_args)
Expand Down
226 changes: 160 additions & 66 deletions lib/wasi/src/bin_factory/exec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ use std::{pin::Pin, sync::Arc};

use crate::{
os::task::{thread::WasiThreadRunGuard, TaskJoinHandle},
VirtualBusError, WasiRuntimeError,
syscalls::rewind,
RewindState, VirtualBusError, WasiError, WasiRuntimeError,
};
use futures::Future;
use tracing::*;
use wasmer::{FunctionEnvMut, Instance, Memory, Module, Store};
use wasmer::{Function, FunctionEnvMut, Instance, Memory, Memory32, Memory64, Module, Store};
use wasmer_wasix_types::wasi::Errno;

use super::{BinFactory, BinaryPackage, ModuleCache};
Expand Down Expand Up @@ -97,90 +98,81 @@ pub fn spawn_exec_module(
let tasks_outer = tasks.clone();

let task = {
move |mut store, module, memory| {
move |mut store: Store, module, memory| {
// Create the WasiFunctionEnv
let mut wasi_env = env;
wasi_env.runtime = runtime;
wasi_env.enable_deep_sleep = wasi_env.capable_of_deep_sleep();
let thread = WasiThreadRunGuard::new(wasi_env.thread.clone());

let mut wasi_env = WasiFunctionEnv::new(&mut store, wasi_env);
// Perform the initialization
let (start, ctx) = {
let mut ctx = WasiFunctionEnv::new(&mut store, wasi_env);

// Let's instantiate the module with the imports.
let (mut import_object, init) =
import_object_for_all_wasi_versions(&module, &mut store, &wasi_env.env);
let imported_memory = if let Some(memory) = memory {
let imported_memory = Memory::new_from_existing(&mut store, memory);
import_object.define("env", "memory", imported_memory.clone());
Some(imported_memory)
} else {
None
};
// Let's instantiate the module with the imports.
let (mut import_object, init) =
import_object_for_all_wasi_versions(&module, &mut store, &ctx.env);
let imported_memory = if let Some(memory) = memory {
let imported_memory = Memory::new_from_existing(&mut store, memory);
import_object.define("env", "memory", imported_memory.clone());
Some(imported_memory)
} else {
None
};

let instance = match Instance::new(&mut store, &module, &import_object) {
Ok(a) => a,
Err(err) => {
error!("wasi[{}]::wasm instantiate error ({})", pid, err);
wasi_env
.data(&store)
.blocking_cleanup(Some(Errno::Noexec.into()));
return;
}
};
let instance = match Instance::new(&mut store, &module, &import_object) {
Ok(a) => a,
Err(err) => {
error!("wasi[{}]::wasm instantiate error ({})", pid, err);
ctx.data(&store)
.blocking_cleanup(Some(Errno::Noexec.into()));
return;
}
};

init(&instance, &store).unwrap();
init(&instance, &store).unwrap();

// Initialize the WASI environment
if let Err(err) =
wasi_env.initialize_with_memory(&mut store, instance.clone(), imported_memory)
{
error!("wasi[{}]::wasi initialize error ({})", pid, err);
wasi_env
.data(&store)
.blocking_cleanup(Some(Errno::Noexec.into()));
return;
}

// If this module exports an _initialize function, run that first.
if let Ok(initialize) = instance.exports.get_function("_initialize") {
if let Err(err) = initialize.call(&mut store, &[]) {
thread.thread.set_status_finished(Err(err.into()));
wasi_env
.data(&store)
// Initialize the WASI environment
if let Err(err) =
ctx.initialize_with_memory(&mut store, instance.clone(), imported_memory)
{
error!("wasi[{}]::wasi initialize error ({})", pid, err);
ctx.data(&store)
.blocking_cleanup(Some(Errno::Noexec.into()));
return;
}
}

// Let's call the `_start` function, which is our `main` function in Rust.
let start = instance.exports.get_function("_start").ok();
// If this module exports an _initialize function, run that first.
if let Ok(initialize) = instance.exports.get_function("_initialize") {
if let Err(err) = initialize.call(&mut store, &[]) {
thread.thread.set_status_finished(Err(err.into()));
finish_module(ctx, store, Errno::Noexec);
return;
}
}

// Let's call the `_start` function, which is our `main` function in Rust.
let start = instance
.exports
.get_function("_start")
.map(|a| a.clone())
.ok();
let ctx = WasiFunctionEnv { env: ctx.env };

(start, ctx)
};

// If there is a start function
debug!("wasi[{}]::called main()", pid);
// TODO: rewrite to use crate::run_wasi_func

thread.thread.set_status_running();

let ret = if let Some(start) = start {
start
.call(&mut store, &[])
.map_err(WasiRuntimeError::from)
.map(|_| Errno::Success)
// Call the module
if let Some(start) = start {
call_module(ctx, store, module, start, None);
} else {
debug!("wasi[{}]::exec-failed: missing _start function", pid);
Ok(Errno::Noexec)
};

let code = if let Err(err) = &ret {
err.as_exit_code().unwrap_or_else(|| Errno::Noexec.into())
} else {
Errno::Success.into()
};

// Cleanup the environment
wasi_env.data(&store).blocking_cleanup(Some(code));

debug!("wasi[{pid}]::main() has exited with {code}");
thread.thread.set_status_finished(ret.map(|a| a.into()));
finish_module(ctx, store, Errno::Noexec);
}
}
};

Expand All @@ -195,6 +187,108 @@ pub fn spawn_exec_module(
Ok(join_handle)
}

/// Finishes with a module and notifies an errcode
john-sharratt marked this conversation as resolved.
Show resolved Hide resolved
fn finish_module(ctx: WasiFunctionEnv, store: Store, err: Errno) {
ctx.data(&store).blocking_cleanup(Some(err.into()));
}

/// Calls the module
fn call_module(
ctx: WasiFunctionEnv,
mut store: Store,
module: Module,
start: Function,
rewind_state: Option<RewindState>,
) {
let env = ctx.data(&store);
let pid = env.pid();
let thread = env.thread.clone();
let tasks = env.tasks().clone();
thread.set_status_running();

// If we need to rewind then do so
if let Some(rewind_state) = rewind_state {
let res = if rewind_state.is_64bit {
rewind::<Memory64>(
ctx.env.clone().into_mut(&mut store),
rewind_state.memory_stack.freeze(),
rewind_state.rewind_stack.freeze(),
rewind_state.store_data,
)
} else {
rewind::<Memory32>(
ctx.env.clone().into_mut(&mut store),
rewind_state.memory_stack.freeze(),
rewind_state.rewind_stack.freeze(),
rewind_state.store_data,
)
};
if res != Errno::Success {
finish_module(ctx, store, res);
return;
}
}

// Invoke the start function
let ret = {
let call_ret = start.call(&mut store, &[]);

if let Err(err) = call_ret {
match err.downcast::<WasiError>() {
Ok(WasiError::Exit(code)) => {
if code.is_success() {
Ok(Errno::Success)
} else {
Ok(Errno::Noexec)
}
}
Ok(WasiError::DeepSleep(deep)) => {
// Create the callback that will be invoked when the thread respawns after a deep sleep
let rewind = deep.rewind;
let respawn = {
let ctx = ctx.clone();
move |store, module| {
// Call the thread
call_module(ctx, store, module, start, Some(rewind));
}
};

// Spawns the WASM process after a trigger
if let Err(err) = tasks.resume_wasm_after_poller(
Box::new(respawn),
store,
module,
ctx,
deep.work,
) {
debug!("failed to go into deep sleep - {}", err);
}
return;
}
Ok(WasiError::UnknownWasiVersion) => {
debug!("failed as wasi version is unknown",);
Ok(Errno::Noexec)
}
Err(err) => Err(WasiRuntimeError::from(err)),
}
} else {
Ok(Errno::Success)
}
};

let code = if let Err(err) = &ret {
err.as_exit_code().unwrap_or_else(|| Errno::Noexec.into())
} else {
Errno::Success.into()
};

// Cleanup the environment
ctx.data(&store).blocking_cleanup(Some(code));

debug!("wasi[{pid}]::main() has exited with {code}");
thread.set_status_finished(ret.map(|a| a.into()));
}

impl BinFactory {
pub fn spawn<'a>(
&'a self,
Expand Down
Loading