From 00b41ab7def23cb956badd4d9035444732707c57 Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Tue, 21 Mar 2023 16:32:34 +1100 Subject: [PATCH 01/52] Implemented the draft asyncify implementation for deep sleeping - a.k.a. asynchronous threading --- lib/api/src/externals/memory.rs | 2 +- lib/api/src/externals/memory_view.rs | 2 +- lib/api/src/js/externals/memory_view.rs | 2 +- lib/api/src/ptr.rs | 2 +- lib/api/src/sys/externals/memory_view.rs | 2 +- lib/cli/src/commands/run/wasi.rs | 4 +- lib/types/src/memory.rs | 9 + lib/wasi-types/src/wasi/wasix_manual.rs | 8 +- lib/wasi/src/bin_factory/exec.rs | 226 +++++++--- lib/wasi/src/fs/inode_guard.rs | 63 ++- lib/wasi/src/lib.rs | 103 ++--- lib/wasi/src/os/task/thread.rs | 30 ++ lib/wasi/src/runtime/task_manager/mod.rs | 93 +++- lib/wasi/src/runtime/task_manager/tokio.rs | 30 +- lib/wasi/src/state/builder.rs | 12 +- lib/wasi/src/state/env.rs | 49 ++- lib/wasi/src/state/func_env.rs | 20 +- lib/wasi/src/state/mod.rs | 41 +- lib/wasi/src/syscalls/legacy/snapshot0.rs | 74 ++-- lib/wasi/src/syscalls/mod.rs | 408 +++++++++++++----- lib/wasi/src/syscalls/wasi/poll_oneoff.rs | 191 ++++---- lib/wasi/src/syscalls/wasi/proc_exit.rs | 10 +- lib/wasi/src/syscalls/wasix/futex_wait.rs | 76 ++-- lib/wasi/src/syscalls/wasix/futex_wake_all.rs | 7 +- lib/wasi/src/syscalls/wasix/proc_exec.rs | 162 +++---- lib/wasi/src/syscalls/wasix/proc_fork.rs | 108 +++-- lib/wasi/src/syscalls/wasix/proc_join.rs | 208 ++++++--- lib/wasi/src/syscalls/wasix/sched_yield.rs | 6 +- .../src/syscalls/wasix/stack_checkpoint.rs | 6 +- lib/wasi/src/syscalls/wasix/stack_restore.rs | 8 +- lib/wasi/src/syscalls/wasix/thread_join.rs | 17 +- .../src/syscalls/wasix/thread_local_get.rs | 2 +- lib/wasi/src/syscalls/wasix/thread_sleep.rs | 31 +- lib/wasi/src/syscalls/wasix/thread_spawn.rs | 314 ++++++++------ 34 files changed, 1501 insertions(+), 825 deletions(-) diff --git a/lib/api/src/externals/memory.rs b/lib/api/src/externals/memory.rs index 0d29b0ecbc0..39d34cd15c1 100644 --- a/lib/api/src/externals/memory.rs +++ b/lib/api/src/externals/memory.rs @@ -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) } diff --git a/lib/api/src/externals/memory_view.rs b/lib/api/src/externals/memory_view.rs index 77bcaa70664..23a3fe2d5a7 100644 --- a/lib/api/src/externals/memory_view.rs +++ b/lib/api/src/externals/memory_view.rs @@ -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)) } diff --git a/lib/api/src/js/externals/memory_view.rs b/lib/api/src/js/externals/memory_view.rs index 79161575c57..fccd385ba88 100644 --- a/lib/api/src/js/externals/memory_view.rs +++ b/lib/api/src/js/externals/memory_view.rs @@ -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) } diff --git a/lib/api/src/ptr.rs b/lib/api/src/ptr.rs index 206cb806315..93afcf46944 100644 --- a/lib/api/src/ptr.rs +++ b/lib/api/src/ptr.rs @@ -60,7 +60,7 @@ pub type WasmPtr64 = WasmPtr; #[repr(transparent)] pub struct WasmPtr { offset: M::Offset, - _phantom: PhantomData<*mut T>, + _phantom: PhantomData, } impl WasmPtr { diff --git a/lib/api/src/sys/externals/memory_view.rs b/lib/api/src/sys/externals/memory_view.rs index 41ea4e70674..a506abd9dc2 100644 --- a/lib/api/src/sys/externals/memory_view.rs +++ b/lib/api/src/sys/externals/memory_view.rs @@ -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(); diff --git a/lib/cli/src/commands/run/wasi.rs b/lib/cli/src/commands/run/wasi.rs index 8f4d27b6b02..444c62a94bc 100644 --- a/lib/cli/src/commands/run/wasi.rs +++ b/lib/cli/src/commands/run/wasi.rs @@ -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}, @@ -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, module: &Module, program_name: String, args: Vec, diff --git a/lib/types/src/memory.rs b/lib/types/src/memory.rs index 87132363523..36644e8b742 100644 --- a/lib/types/src/memory.rs +++ b/lib/types/src/memory.rs @@ -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. @@ -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. @@ -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 + } } diff --git a/lib/wasi-types/src/wasi/wasix_manual.rs b/lib/wasi-types/src/wasi/wasix_manual.rs index bd96ac690a4..c33465221eb 100644 --- a/lib/wasi-types/src/wasi/wasix_manual.rs +++ b/lib/wasi-types/src/wasi/wasix_manual.rs @@ -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, @@ -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), @@ -214,7 +214,7 @@ unsafe impl ValueType for JoinStatus { #[repr(C)] #[derive(Copy, Clone)] pub struct ThreadStart { - 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, @@ -225,7 +225,7 @@ pub struct ThreadStart { impl core::fmt::Debug for ThreadStart { 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) diff --git a/lib/wasi/src/bin_factory/exec.rs b/lib/wasi/src/bin_factory/exec.rs index 9b3020f7266..7f19303ee87 100644 --- a/lib/wasi/src/bin_factory/exec.rs +++ b/lib/wasi/src/bin_factory/exec.rs @@ -2,13 +2,14 @@ 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::*; #[cfg(feature = "sys")] use wasmer::NativeEngineExt; -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}; @@ -99,90 +100,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); + } } }; @@ -197,6 +189,108 @@ pub fn spawn_exec_module( Ok(join_handle) } +/// Finishes with a module and notifies an errcode +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, +) { + 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::( + ctx.env.clone().into_mut(&mut store), + rewind_state.memory_stack.freeze(), + rewind_state.rewind_stack.freeze(), + rewind_state.store_data, + ) + } else { + rewind::( + 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::() { + 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, diff --git a/lib/wasi/src/fs/inode_guard.rs b/lib/wasi/src/fs/inode_guard.rs index 100843ac793..d6c4d64c264 100644 --- a/lib/wasi/src/fs/inode_guard.rs +++ b/lib/wasi/src/fs/inode_guard.rs @@ -72,41 +72,53 @@ impl InodeValFilePollGuard { impl std::fmt::Debug for InodeValFilePollGuard { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match &self.mode { - InodeValFilePollGuardMode::File(..) => write!(f, "guard-file"), + InodeValFilePollGuardMode::File(..) => { + write!(f, "guard-file(fd={}, peb={})", self.fd, self.peb) + } InodeValFilePollGuardMode::EventNotifications { .. } => { - write!(f, "guard-notifications") + write!(f, "guard-notifications(fd={}, peb={})", self.fd, self.peb) } InodeValFilePollGuardMode::Socket { inner } => { let inner = inner.protected.read().unwrap(); match inner.kind { - InodeSocketKind::TcpListener { .. } => write!(f, "guard-tcp-listener"), + InodeSocketKind::TcpListener { .. } => { + write!(f, "guard-tcp-listener(fd={}, peb={})", self.fd, self.peb) + } InodeSocketKind::TcpStream { ref socket, .. } => { if socket.is_closed() { - write!(f, "guard-tcp-stream (closed)") + write!( + f, + "guard-tcp-stream (closed, fd={}, peb={})", + self.fd, self.peb + ) } else { - write!(f, "guard-tcp-stream") + write!(f, "guard-tcp-stream(fd={}, peb={})", self.fd, self.peb) } } - InodeSocketKind::UdpSocket { .. } => write!(f, "guard-udp-socket"), - InodeSocketKind::Raw(..) => write!(f, "guard-raw-socket"), - _ => write!(f, "guard-socket"), + InodeSocketKind::UdpSocket { .. } => { + write!(f, "guard-udp-socket(fd={}, peb={})", self.fd, self.peb) + } + InodeSocketKind::Raw(..) => { + write!(f, "guard-raw-socket(fd={}, peb={})", self.fd, self.peb) + } + _ => write!(f, "guard-socket(fd={}), peb={})", self.fd, self.peb), } } } } } -pub(crate) struct InodeValFilePollGuardJoin<'a> { - mode: &'a mut InodeValFilePollGuardMode, +pub(crate) struct InodeValFilePollGuardJoin { + mode: InodeValFilePollGuardMode, fd: u32, peb: PollEventSet, subscription: Subscription, } -impl<'a> InodeValFilePollGuardJoin<'a> { - pub(crate) fn new(guard: &'a mut InodeValFilePollGuard) -> Self { +impl InodeValFilePollGuardJoin { + pub(crate) fn new(guard: InodeValFilePollGuard) -> Self { Self { - mode: &mut guard.mode, + mode: guard.mode, fd: guard.fd, peb: guard.peb, subscription: guard.subscription, @@ -115,9 +127,12 @@ impl<'a> InodeValFilePollGuardJoin<'a> { pub(crate) fn fd(&self) -> u32 { self.fd } + pub(crate) fn peb(&self) -> PollEventSet { + self.peb + } } -impl<'a> Future for InodeValFilePollGuardJoin<'a> { +impl Future for InodeValFilePollGuardJoin { type Output = heapless::Vec; fn poll(mut self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll { @@ -232,7 +247,14 @@ impl<'a> Future for InodeValFilePollGuardJoin<'a> { Poll::Pending } } - res => res, + Poll::Ready(Ok(amt)) => { + if guard.notifications.closed == true { + Poll::Pending + } else { + Poll::Ready(Ok(amt)) + } + } + Poll::Pending => Poll::Pending, } } }; @@ -303,7 +325,7 @@ impl<'a> Future for InodeValFilePollGuardJoin<'a> { let res = guard.poll_write_ready(cx).map_err(net_error_into_io_err); match res { Poll::Ready(Err(err)) if is_err_closed(&err) => { - tracing::trace!("socket write ready error (fd={}) - {}", fd, err); + tracing::trace!("socket write ready error (fd={}) - err={}", fd, err); if !replace(&mut guard.notifications.closed, true) { Poll::Ready(Ok(0)) } else { @@ -318,7 +340,14 @@ impl<'a> Future for InodeValFilePollGuardJoin<'a> { Poll::Pending } } - res => res, + Poll::Ready(Ok(amt)) => { + if guard.notifications.closed == true { + Poll::Pending + } else { + Poll::Ready(Ok(amt)) + } + } + Poll::Pending => Poll::Pending, } } }; diff --git a/lib/wasi/src/lib.rs b/lib/wasi/src/lib.rs index 85549d39f6e..9ac4601d571 100644 --- a/lib/wasi/src/lib.rs +++ b/lib/wasi/src/lib.rs @@ -53,14 +53,12 @@ pub mod capabilities; mod bindings; use std::sync::Arc; -use std::{ - cell::RefCell, - sync::atomic::{AtomicU32, Ordering}, -}; +use std::{cell::RefCell, sync::atomic::AtomicU32}; #[allow(unused_imports)] use bytes::{Bytes, BytesMut}; use os::task::control_plane::ControlPlaneError; +use syscalls::AsyncifyFuture; use thiserror::Error; use tracing::error; // re-exports needed for OS @@ -115,46 +113,50 @@ pub use crate::{ utils::{get_wasi_version, get_wasi_versions, is_wasi_module, WasiVersion}, }; +/// The rewind state after a deep sleep +pub struct RewindState { + /// Memory stack used to restore the stack trace back to where it was + pub memory_stack: BytesMut, + /// Call stack used to restore the stack trace back to where it was + pub rewind_stack: BytesMut, + /// All the global data stored in the store + pub store_data: Bytes, + /// Flag that indicates if this rewind is 64-bit or 32-bit memory based + pub is_64bit: bool, +} + +/// Represents the work that will be done when a thread goes to deep sleep and +/// includes the things needed to restore it again +pub struct DeepSleepWork { + /// This is the work that will be performed before the thread is rewoken + pub work: Box> + Send + Sync + 'static>, + /// State that the thread will be rewound to + pub rewind: RewindState, +} +impl std::fmt::Debug for DeepSleepWork { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "deep-sleep-work(memory_stack_len={}, rewind_stack_len={}, store_size={})", + self.rewind.memory_stack.len(), + self.rewind.rewind_stack.len(), + self.rewind.store_data.len() + ) + } +} + /// This is returned in `RuntimeError`. /// Use `downcast` or `downcast_ref` to retrieve the `ExitCode`. -#[derive(Error, Debug, PartialEq, Eq, PartialOrd, Ord)] +#[derive(Error, Debug)] pub enum WasiError { #[error("WASI exited with code: {0}")] Exit(ExitCode), + #[error("WASI deep sleep: {0:?}")] + DeepSleep(DeepSleepWork), #[error("The WASI version could not be determined")] UnknownWasiVersion, } -/// Represents the ID of a WASI calling thread -#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct WasiCallingId(u32); - -impl WasiCallingId { - pub fn raw(&self) -> u32 { - self.0 - } - - pub fn inc(&mut self) -> WasiCallingId { - self.0 += 1; - *self - } -} - -impl From for WasiCallingId { - fn from(id: u32) -> Self { - Self(id) - } -} -impl From for u32 { - fn from(t: WasiCallingId) -> u32 { - t.0 as u32 - } -} - -/// The default stack size for WASIX -pub const DEFAULT_STACK_SIZE: u64 = 1_048_576u64; -pub const DEFAULT_STACK_BASE: u64 = DEFAULT_STACK_SIZE; - // TODO: remove, this is a leftover from an old vbus crate and should be folded // into WasiRuntimeError. #[derive(Error, Copy, Clone, Debug, PartialEq, Eq)] @@ -335,19 +337,6 @@ lazy_static::lazy_static! { static ref CALLER_ID_SEED: Arc = Arc::new(AtomicU32::new(1)); } -/// Returns the current thread ID -pub fn current_caller_id() -> WasiCallingId { - CALLER_ID - .with(|f| { - let mut caller_id = f.borrow_mut(); - if *caller_id == 0 { - *caller_id = CALLER_ID_SEED.fetch_add(1, Ordering::AcqRel); - } - *caller_id - }) - .into() -} - /// Create an [`Imports`] with an existing [`WasiEnv`]. `WasiEnv` /// needs a [`WasiState`], that can be constructed from a /// [`WasiEnvBuilder`](state::WasiEnvBuilder). @@ -406,11 +395,11 @@ fn wasi_unstable_exports(mut store: &mut impl AsStoreMut, env: &FunctionEnv Function::new_typed_with_env(&mut store, env, path_rename::), "path_symlink" => Function::new_typed_with_env(&mut store, env, path_symlink::), "path_unlink_file" => Function::new_typed_with_env(&mut store, env, path_unlink_file::), - "poll_oneoff" => Function::new_typed_with_env(&mut store, env, legacy::snapshot0::poll_oneoff), + "poll_oneoff" => Function::new_typed_with_env(&mut store, env, legacy::snapshot0::poll_oneoff::), "proc_exit" => Function::new_typed_with_env(&mut store, env, proc_exit::), "proc_raise" => Function::new_typed_with_env(&mut store, env, proc_raise), "random_get" => Function::new_typed_with_env(&mut store, env, random_get::), - "sched_yield" => Function::new_typed_with_env(&mut store, env, sched_yield), + "sched_yield" => Function::new_typed_with_env(&mut store, env, sched_yield::), "sock_recv" => Function::new_typed_with_env(&mut store, env, sock_recv::), "sock_send" => Function::new_typed_with_env(&mut store, env, sock_send::), "sock_shutdown" => Function::new_typed_with_env(&mut store, env, sock_shutdown), @@ -465,7 +454,7 @@ fn wasi_snapshot_preview1_exports( "proc_exit" => Function::new_typed_with_env(&mut store, env, proc_exit::), "proc_raise" => Function::new_typed_with_env(&mut store, env, proc_raise), "random_get" => Function::new_typed_with_env(&mut store, env, random_get::), - "sched_yield" => Function::new_typed_with_env(&mut store, env, sched_yield), + "sched_yield" => Function::new_typed_with_env(&mut store, env, sched_yield::), "sock_recv" => Function::new_typed_with_env(&mut store, env, sock_recv::), "sock_send" => Function::new_typed_with_env(&mut store, env, sock_send::), "sock_shutdown" => Function::new_typed_with_env(&mut store, env, sock_shutdown), @@ -542,13 +531,13 @@ fn wasix_exports_32(mut store: &mut impl AsStoreMut, env: &FunctionEnv) "thread_local_destroy" => Function::new_typed_with_env(&mut store, env, thread_local_destroy), "thread_local_set" => Function::new_typed_with_env(&mut store, env, thread_local_set), "thread_local_get" => Function::new_typed_with_env(&mut store, env, thread_local_get::), - "thread_sleep" => Function::new_typed_with_env(&mut store, env, thread_sleep), + "thread_sleep" => Function::new_typed_with_env(&mut store, env, thread_sleep::), "thread_id" => Function::new_typed_with_env(&mut store, env, thread_id::), "thread_signal" => Function::new_typed_with_env(&mut store, env, thread_signal), - "thread_join" => Function::new_typed_with_env(&mut store, env, thread_join), + "thread_join" => Function::new_typed_with_env(&mut store, env, thread_join::), "thread_parallelism" => Function::new_typed_with_env(&mut store, env, thread_parallelism::), "thread_exit" => Function::new_typed_with_env(&mut store, env, thread_exit), - "sched_yield" => Function::new_typed_with_env(&mut store, env, sched_yield), + "sched_yield" => Function::new_typed_with_env(&mut store, env, sched_yield::), "stack_checkpoint" => Function::new_typed_with_env(&mut store, env, stack_checkpoint::), "stack_restore" => Function::new_typed_with_env(&mut store, env, stack_restore::), "futex_wait" => Function::new_typed_with_env(&mut store, env, futex_wait::), @@ -665,13 +654,13 @@ fn wasix_exports_64(mut store: &mut impl AsStoreMut, env: &FunctionEnv) "thread_local_destroy" => Function::new_typed_with_env(&mut store, env, thread_local_destroy), "thread_local_set" => Function::new_typed_with_env(&mut store, env, thread_local_set), "thread_local_get" => Function::new_typed_with_env(&mut store, env, thread_local_get::), - "thread_sleep" => Function::new_typed_with_env(&mut store, env, thread_sleep), + "thread_sleep" => Function::new_typed_with_env(&mut store, env, thread_sleep::), "thread_id" => Function::new_typed_with_env(&mut store, env, thread_id::), "thread_signal" => Function::new_typed_with_env(&mut store, env, thread_signal), - "thread_join" => Function::new_typed_with_env(&mut store, env, thread_join), + "thread_join" => Function::new_typed_with_env(&mut store, env, thread_join::), "thread_parallelism" => Function::new_typed_with_env(&mut store, env, thread_parallelism::), "thread_exit" => Function::new_typed_with_env(&mut store, env, thread_exit), - "sched_yield" => Function::new_typed_with_env(&mut store, env, sched_yield), + "sched_yield" => Function::new_typed_with_env(&mut store, env, sched_yield::), "stack_checkpoint" => Function::new_typed_with_env(&mut store, env, stack_checkpoint::), "stack_restore" => Function::new_typed_with_env(&mut store, env, stack_restore::), "futex_wait" => Function::new_typed_with_env(&mut store, env, futex_wait::), diff --git a/lib/wasi/src/os/task/thread.rs b/lib/wasi/src/os/task/thread.rs index 4a0d265d659..f289445b7bf 100644 --- a/lib/wasi/src/os/task/thread.rs +++ b/lib/wasi/src/os/task/thread.rs @@ -121,6 +121,36 @@ impl Drop for WasiThreadRunGuard { } } +/// Represents the memory layout of the parts that the thread itself uses +#[derive(Debug, Clone)] +pub struct WasiMemoryLayout { + /// This is the top part of the stack (stacks go backwards) + pub stack_upper: u64, + /// This is the bottom part of the stack (anything more below this is a stack overflow) + pub stack_lower: u64, + /// Piece of memory that is marked as none readable/writable so stack overflows cause an exception + /// TODO: This field will need to be used to mark the guard memory as inaccessible + #[allow(dead_code)] + pub guard_size: u64, + /// Total size of the stack + pub stack_size: u64, +} + +/// The default stack size for WASIX +pub const DEFAULT_STACK_SIZE: u64 = 1_048_576u64; +pub const DEFAULT_STACK_BASE: u64 = DEFAULT_STACK_SIZE; + +impl Default for WasiMemoryLayout { + fn default() -> Self { + Self { + stack_lower: 0, + stack_upper: DEFAULT_STACK_SIZE, + guard_size: 0, + stack_size: DEFAULT_STACK_SIZE, + } + } +} + #[derive(Debug)] struct WasiThreadState { is_main: bool, diff --git a/lib/wasi/src/runtime/task_manager/mod.rs b/lib/wasi/src/runtime/task_manager/mod.rs index bb848e42227..5997c702d69 100644 --- a/lib/wasi/src/runtime/task_manager/mod.rs +++ b/lib/wasi/src/runtime/task_manager/mod.rs @@ -2,6 +2,8 @@ #[cfg(feature = "sys-thread")] pub mod tokio; +use std::cell::RefCell; +use std::task::{Context, Poll}; use std::{pin::Pin, time::Duration}; use ::tokio::runtime::Handle; @@ -11,8 +13,11 @@ use wasmer::{MemoryType, Module, Store}; #[cfg(feature = "sys")] use wasmer_types::MemoryStyle; +use wasmer_wasix_types::wasi::{Errno, ExitCode}; use crate::os::task::thread::WasiThreadError; +use crate::syscalls::AsyncifyFuture; +use crate::WasiFunctionEnv; #[derive(Debug)] pub struct SpawnedMemory { @@ -29,6 +34,15 @@ pub enum SpawnType { NewThread(VMMemory, MemoryType), } +/// Indicates if the task should run with the supplied store +/// or if it should abort and exit the thread +pub enum TaskResumeAction { + // The task will run with the following store + Run(Store), + /// The task has been aborted + Abort, +} + /// An implementation of task management #[async_trait::async_trait] #[allow(unused_variables)] @@ -59,9 +73,8 @@ pub trait VirtualTaskManager: std::fmt::Debug + Send + Sync + 'static { #[allow(dyn_drop)] fn runtime_enter<'g>(&'g self) -> Box; - /// Starts an asynchronous task will will run on a dedicated thread + /// Starts an WebAssembly task will will run on a dedicated thread /// pulled from the worker pool that has a stateful thread local variable - /// It is ok for this task to block execution and any async futures within its scope fn task_wasm( &self, task: Box) + Send + 'static>, @@ -70,6 +83,21 @@ pub trait VirtualTaskManager: std::fmt::Debug + Send + Sync + 'static { spawn_type: SpawnType, ) -> 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 + /// After the trigger has successfully completed + fn resume_wasm_after_trigger( + &self, + task: Box, + store: Store, + module: Module, + trigger: Box< + dyn FnOnce(Store) -> Pin + Send + 'static>> + + Send + + 'static, + >, + ) -> 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 @@ -89,6 +117,67 @@ impl dyn VirtualTaskManager { pub fn block_on<'a, A>(&self, task: impl Future + 'a) -> A { self.runtime().block_on(task) } + + /// Starts an WebAssembly task will will run on a dedicated thread + /// pulled from the worker pool that has a stateful thread local variable + /// After the poller has successed + pub fn resume_wasm_after_poller( + &self, + task: Box, + store: Store, + module: Module, + env: WasiFunctionEnv, + work: Box> + Send + Sync + 'static>, + ) -> Result<(), WasiThreadError> { + // This poller will process any signals when the main working function is idle + struct AsyncifyPollerOwned { + env: WasiFunctionEnv, + store: Store, + work: + RefCell> + Send + Sync + 'static>>, + } + impl Future for AsyncifyPollerOwned { + type Output = Result, ExitCode>; + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let mut work = self.work.borrow_mut(); + let store = &self.store; + let env = self.env.data(store); + if let Poll::Ready(res) = work.poll(env, &self.store, cx) { + return Poll::Ready(Ok(res)); + } + if let Some(exit_code) = env.should_exit() { + return Poll::Ready(Err(exit_code)); + } + if env.thread.has_signals_or_subscribe(cx.waker()) { + return Poll::Ready(Ok(Err(Errno::Intr))); + } + Poll::Pending + } + } + + self.resume_wasm_after_trigger( + task, + store, + module, + Box::new(move |store| { + Box::pin(async move { + let mut poller = AsyncifyPollerOwned { + env, + store, + work: RefCell::new(work), + }; + let res = Pin::new(&mut poller).await; + if let Err(exit_code) = res { + let env = poller.env.data(&poller.store); + env.thread.set_status_finished(Ok(exit_code)); + return TaskResumeAction::Abort; + } + tracing::trace!("deep sleep woken - {:?}", res); + TaskResumeAction::Run(poller.store) + }) + }), + ) + } } /// Generic utility methods for VirtualTaskManager diff --git a/lib/wasi/src/runtime/task_manager/tokio.rs b/lib/wasi/src/runtime/task_manager/tokio.rs index b54f7314c5c..6364e2506c7 100644 --- a/lib/wasi/src/runtime/task_manager/tokio.rs +++ b/lib/wasi/src/runtime/task_manager/tokio.rs @@ -13,7 +13,7 @@ use wasmer::{ use crate::os::task::thread::WasiThreadError; -use super::{SpawnType, VirtualTaskManager}; +use super::{SpawnType, TaskResumeAction, VirtualTaskManager}; /// A task manager that uses tokio to spawn tasks. #[derive(Clone, Debug)] @@ -130,7 +130,7 @@ impl VirtualTaskManager for TokioTaskManager { }) } - /// See [`VirtualTaskManager::enter`]. + /// See [`VirtualTaskManager::task_wasm`]. fn task_wasm( &self, task: Box) + Send + 'static>, @@ -146,6 +146,32 @@ impl VirtualTaskManager for TokioTaskManager { Ok(()) } + /// See [`VirtualTaskManager::task_wasm_with_trigger`]. + fn resume_wasm_after_trigger( + &self, + task: Box, + store: Store, + module: Module, + trigger: Box< + dyn FnOnce(Store) -> Pin + Send + 'static>> + + Send + + 'static, + >, + ) -> Result<(), WasiThreadError> { + let trigger = trigger(store); + let handle = self.0.clone(); + self.0.spawn(async move { + let action = trigger.await; + if let TaskResumeAction::Run(store) = action { + handle.spawn_blocking(move || { + // Invoke the callback + task(store, module); + }); + } + }); + Ok(()) + } + /// See [`VirtualTaskManager::task_dedicated`]. fn task_dedicated( &self, diff --git a/lib/wasi/src/state/builder.rs b/lib/wasi/src/state/builder.rs index 68bc42ab058..7856faa9701 100644 --- a/lib/wasi/src/state/builder.rs +++ b/lib/wasi/src/state/builder.rs @@ -9,7 +9,7 @@ use std::{ use rand::Rng; use thiserror::Error; use virtual_fs::{ArcFile, FsError, TmpFileSystem, VirtualFile}; -use wasmer::{AsStoreMut, Instance, Module}; +use wasmer::{AsStoreMut, Instance, Module, Store}; use wasmer_wasix_types::wasi::Errno; use crate::{ @@ -693,7 +693,6 @@ impl WasiEnvBuilder { inodes, args: self.args.clone(), preopen: self.vfs_preopens.clone(), - threading: Default::default(), futexs: Default::default(), clock_offset: Default::default(), envs, @@ -759,6 +758,7 @@ impl WasiEnvBuilder { process: None, thread: None, call_initialize: true, + can_deep_sleep: false, }; Ok(init) @@ -792,7 +792,7 @@ impl WasiEnvBuilder { pub fn instantiate( self, module: Module, - store: &mut impl AsStoreMut, + store: &mut Store, ) -> Result<(Instance, WasiFunctionEnv), WasiRuntimeError> { let init = self.build_init()?; WasiEnv::instantiate(init, module, store) @@ -803,11 +803,7 @@ impl WasiEnvBuilder { self.run_with_store(module, &mut store) } - pub fn run_with_store( - self, - module: Module, - store: &mut impl AsStoreMut, - ) -> Result<(), WasiRuntimeError> { + pub fn run_with_store(self, module: Module, store: &mut Store) -> Result<(), WasiRuntimeError> { let (instance, env) = self.instantiate(module, store)?; let start = instance.exports.get_function("_start")?; diff --git a/lib/wasi/src/state/env.rs b/lib/wasi/src/state/env.rs index f0911bea6fb..e93de84ea4d 100644 --- a/lib/wasi/src/state/env.rs +++ b/lib/wasi/src/state/env.rs @@ -26,14 +26,13 @@ use crate::{ task::{ control_plane::ControlPlaneError, process::{WasiProcess, WasiProcessId}, - thread::{WasiThread, WasiThreadHandle, WasiThreadId}, + thread::{WasiMemoryLayout, WasiThread, WasiThreadHandle, WasiThreadId}, }, }, runtime::SpawnType, syscalls::{__asyncify_light, platform_clock_time_get}, SpawnedMemory, VirtualTaskManager, WasiControlPlane, WasiEnvBuilder, WasiError, WasiFunctionEnv, WasiRuntime, WasiRuntimeError, WasiStateCreationError, WasiVFork, - DEFAULT_STACK_SIZE, }; use super::WasiState; @@ -56,8 +55,6 @@ pub struct WasiInstanceHandles { /// Main function that will be invoked (name = "_start") #[derivative(Debug = "ignore")] - // TODO: review allow... - #[allow(dead_code)] pub(crate) start: Option>, /// Function thats invoked to initialize the WASM module (name = "_initialize") @@ -220,6 +217,9 @@ pub struct WasiEnvInit { /// Whether to call the `_initialize` function in the WASI module. /// Will be true for regular new instances, but false for threads. pub call_initialize: bool, + + /// Indicates if the calling environment is capable of deep sleeping + pub can_deep_sleep: bool, } impl WasiEnvInit { @@ -236,7 +236,6 @@ impl WasiEnvInit { secret: rand::thread_rng().gen::<[u8; 32]>(), inodes, fs, - threading: Default::default(), futexs: Default::default(), clock_offset: std::sync::Mutex::new( self.state.clock_offset.lock().unwrap().clone(), @@ -256,6 +255,7 @@ impl WasiEnvInit { process: None, thread: None, call_initialize: self.call_initialize, + can_deep_sleep: self.can_deep_sleep, } } } @@ -267,12 +267,10 @@ pub struct WasiEnv { pub process: WasiProcess, /// Represents the thread this environment is attached to pub thread: WasiThread, + /// Represents the layout of the memory + pub layout: WasiMemoryLayout, /// Represents a fork of the process that is currently in play pub vfork: Option, - /// End of the stack memory that is allocated for this thread - pub stack_end: u64, - /// Start of the stack memory that is allocated for this thread - pub stack_start: u64, /// Seed used to rotate around the events returned by `poll_oneoff` pub poll_seed: u64, /// Shared state of the WASI system. Manages all the data that the @@ -290,6 +288,9 @@ pub struct WasiEnv { pub module_cache: Arc, pub capabilities: Capabilities, + + /// Is this environment capable and setup for deep sleeping + pub enable_deep_sleep: bool, } impl std::fmt::Debug for WasiEnv { @@ -325,9 +326,8 @@ impl WasiEnv { process: self.process.clone(), poll_seed: self.poll_seed, thread: self.thread.clone(), + layout: self.layout.clone(), vfork: self.vfork.as_ref().map(|v| v.duplicate()), - stack_end: self.stack_end, - stack_start: self.stack_start, state: self.state.clone(), bin_factory: self.bin_factory.clone(), inner: self.inner.clone(), @@ -335,6 +335,7 @@ impl WasiEnv { runtime: self.runtime.clone(), module_cache: self.module_cache.clone(), capabilities: self.capabilities.clone(), + enable_deep_sleep: self.enable_deep_sleep, } } @@ -354,10 +355,9 @@ impl WasiEnv { control_plane: self.control_plane.clone(), process, thread, + layout: self.layout.clone(), vfork: None, poll_seed: 0, - stack_end: self.stack_end, - stack_start: self.stack_start, bin_factory, state, inner: None, @@ -365,6 +365,7 @@ impl WasiEnv { runtime: self.runtime.clone(), capabilities: self.capabilities.clone(), module_cache: self.module_cache.clone(), + enable_deep_sleep: false, }; Ok((new_env, handle)) } @@ -377,6 +378,20 @@ impl WasiEnv { self.thread.tid() } + /// Returns true if this module is capable of deep sleep + /// (needs asyncify to unwind and rewin) + pub fn capable_of_deep_sleep(&self) -> bool { + let inner = self.inner(); + inner.asyncify_get_state.is_some() + && inner.asyncify_start_rewind.is_some() + && inner.asyncify_start_unwind.is_some() + } + + /// Returns true if this thread can go into a deep sleep + pub fn layout(&self) -> &WasiMemoryLayout { + &self.layout + } + pub(crate) fn from_init(init: WasiEnvInit) -> Result { let process = if let Some(p) = init.process { p @@ -393,10 +408,9 @@ impl WasiEnv { control_plane: init.control_plane, process, thread: thread.as_thread(), + layout: WasiMemoryLayout::default(), vfork: None, poll_seed: 0, - stack_end: DEFAULT_STACK_SIZE, - stack_start: 0, state: Arc::new(init.state), inner: None, owned_handles: Vec::new(), @@ -404,6 +418,7 @@ impl WasiEnv { bin_factory: init.bin_factory, module_cache: init.module_cache.clone(), capabilities: init.capabilities, + enable_deep_sleep: false, }; env.owned_handles.push(thread); @@ -695,7 +710,7 @@ impl WasiEnv { /// Providers safe access to the memory /// (it must be initialized before it can be used) - pub fn memory_view<'a>(&'a self, store: &'a impl AsStoreRef) -> MemoryView<'a> { + pub fn memory_view<'a>(&'a self, store: &'a (impl AsStoreRef + ?Sized)) -> MemoryView<'a> { self.memory().view(store) } @@ -897,7 +912,7 @@ impl WasiEnv { /// Cleans up all the open files (if this is the main thread) #[allow(clippy::await_holding_lock)] - pub fn blocking_cleanup(&self, exit_code: Option) { + pub fn blocking_cleanup<'a>(&self, exit_code: Option) { __asyncify_light(self, None, async { self.cleanup(exit_code).await; Ok(()) diff --git a/lib/wasi/src/state/func_env.rs b/lib/wasi/src/state/func_env.rs index 9c784a8c53a..67d45a5ed96 100644 --- a/lib/wasi/src/state/func_env.rs +++ b/lib/wasi/src/state/func_env.rs @@ -3,11 +3,13 @@ use wasmer::{AsStoreMut, AsStoreRef, ExportError, FunctionEnv, Imports, Instance use wasmer_wasix_types::wasi::ExitCode; use crate::{ + os::task::thread::DEFAULT_STACK_SIZE, state::WasiInstanceHandles, utils::{get_wasi_version, get_wasi_versions}, - WasiEnv, WasiError, DEFAULT_STACK_SIZE, + WasiEnv, WasiError, }; +#[derive(Clone)] pub struct WasiFunctionEnv { pub env: FunctionEnv, } @@ -97,16 +99,24 @@ impl WasiFunctionEnv { env.state.fs.set_is_wasix(is_wasix_module); // Set the base stack - let stack_base = if let Some(stack_pointer) = env.inner().stack_pointer.clone() { + let mut stack_base = if let Some(stack_pointer) = env.inner().stack_pointer.clone() { match stack_pointer.get(store) { wasmer::Value::I32(a) => a as u64, wasmer::Value::I64(a) => a as u64, - _ => DEFAULT_STACK_SIZE, + _ => 0, } } else { - DEFAULT_STACK_SIZE + 0 }; - self.data_mut(store).stack_end = stack_base; + if stack_base == 0 { + stack_base = DEFAULT_STACK_SIZE; + } + + // Update the stack layout which is need for asyncify + let env = self.data_mut(store); + let layout = &mut env.layout; + layout.stack_upper = stack_base; + layout.stack_size = layout.stack_upper - layout.stack_lower; Ok(()) } diff --git a/lib/wasi/src/state/mod.rs b/lib/wasi/src/state/mod.rs index ef221e3035f..810a9a3f816 100644 --- a/lib/wasi/src/state/mod.rs +++ b/lib/wasi/src/state/mod.rs @@ -20,20 +20,11 @@ mod env; mod func_env; mod types; -use std::{ - cell::RefCell, - collections::HashMap, - path::Path, - sync::{Arc, Mutex, RwLock}, - task::Waker, - time::Duration, -}; +use std::{collections::HashMap, path::Path, sync::Mutex, task::Waker, time::Duration}; -use derivative::Derivative; #[cfg(feature = "enable-serde")] use serde::{Deserialize, Serialize}; use virtual_fs::{FileOpener, FileSystem, FsError, OpenOptions, VirtualFile}; -use wasmer::Store; use wasmer_wasix_types::wasi::{Errno, Fd as WasiFd, Rights, Snapshot0Clockid}; pub use self::{ @@ -47,7 +38,6 @@ use crate::{ fs::{fs_error_into_wasi_err, WasiFs, WasiFsRoot, WasiInodes, WasiStateFileGuard}, syscalls::types::*, utils::WasiParkingLot, - WasiCallingId, }; /// all the rights enabled @@ -69,33 +59,6 @@ impl FileOpener for WasiStateOpener { } } -// TODO: review allow... -#[allow(dead_code)] -pub(crate) struct WasiThreadContext { - pub ctx: WasiFunctionEnv, - pub store: RefCell, -} - -/// The code itself makes safe use of the struct so multiple threads don't access -/// it (without this the JS code prevents the reference to the module from being stored -/// which is needed for the multithreading mode) -unsafe impl Send for WasiThreadContext {} -unsafe impl Sync for WasiThreadContext {} - -/// Structures used for the threading and sub-processes -/// -/// These internal implementation details are hidden away from the -/// consumer who should instead implement the vbus trait on the runtime -#[derive(Derivative, Default)] -// TODO: review allow... -#[allow(dead_code)] -#[derivative(Debug)] -#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] -pub(crate) struct WasiStateThreading { - #[derivative(Debug = "ignore")] - pub thread_ctx: HashMap>, -} - /// Represents a futex which will make threads wait for completion in a more /// CPU efficient manner #[derive(Debug)] @@ -149,7 +112,6 @@ pub(crate) struct WasiState { pub fs: WasiFs, pub inodes: WasiInodes, - pub threading: RwLock, pub futexs: Mutex>, pub clock_offset: Mutex>, pub args: Vec, @@ -270,7 +232,6 @@ impl WasiState { fs: self.fs.fork(), secret: self.secret, inodes: self.inodes.clone(), - threading: Default::default(), futexs: Default::default(), clock_offset: Mutex::new(self.clock_offset.lock().unwrap().clone()), args: self.args.clone(), diff --git a/lib/wasi/src/syscalls/legacy/snapshot0.rs b/lib/wasi/src/syscalls/legacy/snapshot0.rs index e239fe26227..bb236a9ec2f 100644 --- a/lib/wasi/src/syscalls/legacy/snapshot0.rs +++ b/lib/wasi/src/syscalls/legacy/snapshot0.rs @@ -1,5 +1,5 @@ use tracing::{field, instrument, trace_span}; -use wasmer::{AsStoreMut, FunctionEnvMut, WasmPtr}; +use wasmer::{AsStoreMut, AsStoreRef, FunctionEnvMut, WasmPtr}; use wasmer_wasix_types::wasi::{ Errno, Event, EventFdReadwrite, Eventrwflags, Eventtype, Fd, Filesize, Filestat, Filetype, Snapshot0Event, Snapshot0Filestat, Snapshot0Subscription, Snapshot0Whence, Subscription, @@ -10,8 +10,8 @@ use crate::{ mem_error_to_wasi, os::task::thread::WasiThread, state::{PollEventBuilder, PollEventSet}, - syscalls, syscalls::types, + syscalls::{self, handle_rewind}, Memory32, MemorySize, WasiEnv, WasiError, }; @@ -137,13 +137,18 @@ pub fn fd_seek( /// Wrapper around `syscalls::poll_oneoff` with extra logic to add the removed /// userdata field back #[instrument(level = "trace", skip_all, fields(timeout_ns = field::Empty, fd_guards = field::Empty, seen = field::Empty), ret, err)] -pub fn poll_oneoff( +pub fn poll_oneoff( mut ctx: FunctionEnvMut, in_: WasmPtr, out_: WasmPtr, nsubscriptions: u32, nevents: WasmPtr, ) -> Result { + wasi_try_ok!(WasiEnv::process_signals_and_exit(&mut ctx)?); + if handle_rewind::(&mut ctx) { + return Ok(Errno::Success); + } + let env = ctx.data(); let memory = env.memory_view(&ctx); let mut subscriptions = Vec::new(); @@ -157,39 +162,38 @@ pub fn poll_oneoff( )); } - // make the call - let triggered_events = syscalls::poll_oneoff_internal(&mut ctx, subscriptions)?; - let triggered_events = match triggered_events { - Ok(a) => a, - Err(err) => { - tracing::trace!(err = err as u16); - return Ok(err); + // We clear the number of events + wasi_try_mem_ok!(nevents.write(&memory, 0)); + + // Function to invoke once the poll is finished + let process_events = move |triggered_events, env: &'_ WasiEnv, store: &'_ dyn AsStoreRef| { + // Process all the events that were triggered + let mut memory = env.memory_view(store); + let mut events_seen: u32 = 0; + let event_array = wasi_try_mem!(out_.slice(&memory, nsubscriptions)); + for event in triggered_events { + let event: Event = event; + let event = Snapshot0Event { + userdata: event.userdata, + error: event.error, + type_: Eventtype::FdRead, + fd_readwrite: match event.type_ { + Eventtype::FdRead => unsafe { event.u.fd_readwrite }, + Eventtype::FdWrite => unsafe { event.u.fd_readwrite }, + Eventtype::Clock => EventFdReadwrite { + nbytes: 0, + flags: Eventrwflags::empty(), + }, + }, + }; + wasi_try_mem!(event_array.index(events_seen as u64).write(event)); + events_seen += 1; } + let out_ptr = nevents.deref(&memory); + wasi_try_mem!(out_ptr.write(events_seen)); + Errno::Success }; - // Process all the events that were triggered - let mut env = ctx.data(); - let mut memory = env.memory_view(&ctx); - let mut events_seen: u32 = 0; - let event_array = wasi_try_mem_ok!(out_.slice(&memory, nsubscriptions)); - for event in triggered_events { - let event = Snapshot0Event { - userdata: event.userdata, - error: event.error, - type_: Eventtype::FdRead, - fd_readwrite: match event.type_ { - Eventtype::FdRead => unsafe { event.u.fd_readwrite }, - Eventtype::FdWrite => unsafe { event.u.fd_readwrite }, - Eventtype::Clock => EventFdReadwrite { - nbytes: 0, - flags: Eventrwflags::empty(), - }, - }, - }; - wasi_try_mem_ok!(event_array.index(events_seen as u64).write(event)); - events_seen += 1; - } - let out_ptr = nevents.deref(&memory); - wasi_try_mem_ok!(out_ptr.write(events_seen)); - Ok(Errno::Success) + // Poll and receive all the events that triggered + syscalls::poll_oneoff_internal::(ctx, subscriptions, process_events) } diff --git a/lib/wasi/src/syscalls/mod.rs b/lib/wasi/src/syscalls/mod.rs index f76e6b5370f..eb76a92b2fd 100644 --- a/lib/wasi/src/syscalls/mod.rs +++ b/lib/wasi/src/syscalls/mod.rs @@ -94,7 +94,7 @@ pub(crate) use crate::os::task::{ }; pub(crate) use crate::{ bin_factory::spawn_exec_module, - current_caller_id, import_object_for_all_wasi_versions, mem_error_to_wasi, + import_object_for_all_wasi_versions, mem_error_to_wasi, net::{ read_ip_port, socket::{InodeHttpSocketType, InodeSocket, InodeSocketKind}, @@ -104,11 +104,10 @@ pub(crate) use crate::{ state::{ self, bus_errno_into_vbus_error, iterate_poll_events, vbus_error_into_bus_errno, InodeGuard, InodeWeakGuard, PollEvent, PollEventBuilder, WasiFutex, WasiState, - WasiThreadContext, }, utils::{self, map_io_err}, VirtualTaskManager, WasiEnv, WasiError, WasiFunctionEnv, WasiInstanceHandles, WasiRuntime, - WasiVFork, DEFAULT_STACK_SIZE, + WasiVFork, }; use crate::{ fs::{ @@ -116,7 +115,7 @@ use crate::{ MAX_SYMLINKS, }, utils::store::InstanceSnapshot, - VirtualBusError, WasiInodes, + DeepSleepWork, RewindState, VirtualBusError, WasiInodes, }; pub(crate) use crate::{net::net_error_into_wasi_err, utils::WasiParkingLot}; @@ -227,6 +226,55 @@ pub async fn stderr_write(ctx: &FunctionEnvMut<'_, WasiEnv>, buf: &[u8]) -> Resu stderr.write_all(buf).await.map_err(map_io_err) } +fn block_on_with_timeout( + tasks: &Arc, + timeout: Option, + work: Fut, +) -> Result, WasiError> +where + Fut: Future, WasiError>>, +{ + let mut nonblocking = false; + if timeout == Some(Duration::ZERO) { + nonblocking = true; + } + let timeout = async { + if let Some(timeout) = timeout { + if !nonblocking { + tasks.sleep_now(timeout).await + } else { + InfiniteSleep::default().await + } + } else { + InfiniteSleep::default().await + } + }; + + let work = async move { + tokio::select! { + // The main work we are doing + res = work => res, + // Optional timeout + _ = timeout => Ok(Err(Errno::Timedout)), + } + }; + + // Fast path + if nonblocking { + let waker = WasiDummyWaker.into_waker(); + let mut cx = Context::from_waker(&waker); + let _guard = tasks.runtime_enter(); + let mut pinned_work = Box::pin(work); + if let Poll::Ready(res) = pinned_work.as_mut().poll(&mut cx) { + return res; + } + return Ok(Err(Errno::Again)); + } + + // Slow path, block on the work and process process + tasks.block_on(work) +} + /// Asyncify takes the current thread and blocks on the async runtime associated with it /// thus allowed for asynchronous operations to execute. It has built in functionality /// to (optionally) timeout the IO, force exit the process, callback signals and pump @@ -247,35 +295,15 @@ where return Err(WasiError::Exit(exit_code)); } - // Create the timeout - let mut nonblocking = false; - if timeout == Some(Duration::ZERO) { - nonblocking = true; - } - let timeout = { - let tasks_inner = env.tasks().clone(); - async move { - if let Some(timeout) = timeout { - if !nonblocking { - tasks_inner.sleep_now(timeout).await - } else { - InfiniteSleep::default().await - } - } else { - InfiniteSleep::default().await - } - } - }; - // This poller will process any signals when the main working function is idle - struct WorkWithSignalPoller<'a, 'b, Fut, T> + struct Poller<'a, 'b, Fut, T> where Fut: Future>, { ctx: &'a mut FunctionEnvMut<'b, WasiEnv>, pinned_work: Pin>, } - impl<'a, 'b, Fut, T> Future for WorkWithSignalPoller<'a, 'b, Fut, T> + impl<'a, 'b, Fut, T> Future for Poller<'a, 'b, Fut, T> where Fut: Future>, { @@ -297,75 +325,202 @@ where } } - // Define the work function - let tasks = env.tasks().clone(); + // Block on the work let mut pinned_work = Box::pin(work); - let work = async { - Ok(tokio::select! { - // The main work we are doing - res = WorkWithSignalPoller { ctx, pinned_work } => res?, - // Optional timeout - _ = timeout => Err(Errno::Timedout), - }) - }; + let tasks = env.tasks().clone(); + let poller = Poller { ctx, pinned_work }; + block_on_with_timeout(&tasks, timeout, poller) +} - // Fast path - if nonblocking { - let waker = WasiDummyWaker.into_waker(); - let mut cx = Context::from_waker(&waker); - let _guard = tasks.runtime_enter(); - let mut pinned_work = Box::pin(work); - if let Poll::Ready(res) = pinned_work.as_mut().poll(&mut cx) { - return res; +/// Future that will be polled by asyncify methods +pub trait AsyncifyFuture { + type Output; + + fn poll( + &mut self, + env: &WasiEnv, + store: &dyn AsStoreRef, + cx: &mut Context<'_>, + ) -> Poll; +} + +// This poller will process any signals when the main working function is idle +struct AsyncifyPoller<'a, 'b, 'c, Fut, T> +where + Fut: AsyncifyFuture>, +{ + env: &'a WasiEnv, + store: &'b dyn AsStoreRef, + work: &'c mut Fut, +} +impl<'a, 'b, 'c, Fut, T> Future for AsyncifyPoller<'a, 'b, 'c, Fut, T> +where + Fut: AsyncifyFuture>, +{ + type Output = Result; + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let env = self.env; + let store = self.store; + if let Poll::Ready(res) = self.work.poll(env, store, cx) { + return Poll::Ready(Ok(res)); } - return Ok(Err(Errno::Again)); + if let Some(exit_code) = self.env.should_exit() { + return Poll::Ready(Err(WasiError::Exit(exit_code))); + } + if let Some(signals) = self.env.thread.pop_signals_or_subscribe(cx.waker()) { + return Poll::Ready(Ok(Err(Errno::Intr))); + } + Poll::Pending } +} - // Slow path, block on the work and process process - tasks.block_on(work) +pub enum AsyncifyAction<'a> { + /// Indicates that asyncify callback finished and the + /// caller now has ownership of the ctx again + Finish(FunctionEnvMut<'a, WasiEnv>), + /// Indicates that asyncify should unwind by immediately exiting + /// the current function + Unwind, } /// Asyncify takes the current thread and blocks on the async runtime associated with it /// thus allowed for asynchronous operations to execute. It has built in functionality /// to (optionally) timeout the IO, force exit the process, callback signals and pump /// synchronous IO engine -pub(crate) fn __asyncify_light( - env: &WasiEnv, +/// +/// This will either return the `ctx` as the asyncify has completed successfully +/// or it will return an WasiError which will exit the WASM call using asyncify +/// and instead process it on a shared task +/// +pub(crate) fn __asyncify_with_deep_sleep<'a, M: MemorySize, Fut>( + ctx: FunctionEnvMut<'_, WasiEnv>, + timeout: Option, + mut work: Fut, +) -> Result, WasiError> +where + Fut: AsyncifyFuture> + Send + Sync + 'static, +{ + // Define the work + let tasks = ctx.data().tasks().clone(); + let work = async move { + // Create the timeout + let res = { + let env = ctx.data(); + let deep_sleep_wait = { + async { + if env.enable_deep_sleep { + env.tasks() + .sleep_now(std::time::Duration::from_millis(50)) + .await + } else { + InfiniteSleep::default().await + } + } + }; + + tokio::select! { + // The main work we are doing + res = AsyncifyPoller { + env: ctx.data(), + store: &ctx, + work: &mut work, + } => res?, + // Determines when and if we should go into a deep sleep + _ = deep_sleep_wait => { + tracing::trace!("thread entering deep sleep"); + return deep_sleep::(ctx, work) + .map(|err| Err(err)); + }, + } + }; + Ok(res.map(|()| ctx)) + }; + + // Block on the work + let res = block_on_with_timeout(&tasks, timeout, work)?; + Ok(match res { + Ok(ctx) => AsyncifyAction::Finish(ctx), + Err(err) if err == Errno::Success => AsyncifyAction::Unwind, + Err(err) => return Err(WasiError::Exit(err.into())), + }) +} + +/// Asyncify takes the current thread and blocks on the async runtime associated with it +/// thus allowed for asynchronous operations to execute. It has built in functionality +/// to (optionally) timeout the IO, force exit the process, callback signals and pump +/// synchronous IO engine +/// +/// This will either return the `ctx` as the asyncify has completed successfully +/// or it will return an WasiError which will exit the WASM call using asyncify +/// and instead process it on a shared task +/// +pub(crate) fn __asyncify_with_deep_sleep_ext<'a, M: MemorySize, T, Fut, After>( + ctx: FunctionEnvMut<'_, WasiEnv>, timeout: Option, work: Fut, -) -> Result, WasiError> + after: After, +) -> Result, WasiError> where T: 'static, - Fut: std::future::Future>, + Fut: Future + Send + Sync + 'static, + After: FnOnce(T, &WasiEnv, &dyn AsStoreRef) + Send + Sync + 'static, { - // Create the timeout - let mut nonblocking = false; - if timeout == Some(Duration::ZERO) { - nonblocking = true; + struct Poller { + work: Pin + Send + Sync + 'static>>, + after: Option>, } - let timeout = { - async { - if let Some(timeout) = timeout { - if !nonblocking { - env.tasks().sleep_now(timeout).await - } else { - InfiniteSleep::default().await + impl AsyncifyFuture for Poller { + type Output = Result<(), Errno>; + fn poll( + &mut self, + env: &WasiEnv, + store: &dyn AsStoreRef, + cx: &mut Context<'_>, + ) -> Poll { + match self.work.as_mut().poll(cx) { + Poll::Ready(ret) => { + if let Some(after) = self.after.take() { + after(ret, env, store); + } + Poll::Ready(Ok(())) } - } else { - InfiniteSleep::default().await + Poll::Pending => Poll::Pending, } } - }; + } + __asyncify_with_deep_sleep::( + ctx, + timeout, + Poller:: { + work: Box::pin(work), + after: Some(Box::new(after)), + }, + ) +} + +/// Asyncify takes the current thread and blocks on the async runtime associated with it +/// thus allowed for asynchronous operations to execute. It has built in functionality +/// to (optionally) timeout the IO, force exit the process, callback signals and pump +/// synchronous IO engine +pub(crate) fn __asyncify_light<'a, T, Fut>( + env: &WasiEnv, + timeout: Option, + work: Fut, +) -> Result, WasiError> +where + T: 'static, + Fut: Future>, +{ // This poller will process any signals when the main working function is idle - struct WorkWithSignalPoller<'a, Fut, T> + struct Poller<'a, Fut, T> where Fut: Future>, { env: &'a WasiEnv, pinned_work: Pin>, } - impl<'a, Fut, T> Future for WorkWithSignalPoller<'a, Fut, T> + impl<'a, Fut, T> Future for Poller<'a, Fut, T> where Fut: Future>, { @@ -384,31 +539,10 @@ where } } - // Define the work function + // Block on the work let mut pinned_work = Box::pin(work); - let work = async move { - Ok(tokio::select! { - // The main work we are doing - res = WorkWithSignalPoller { env, pinned_work } => res?, - // Optional timeout - _ = timeout => Err(Errno::Timedout), - }) - }; - - // Fast path - if nonblocking { - let waker = WasiDummyWaker.into_waker(); - let mut cx = Context::from_waker(&waker); - let _guard = env.tasks().runtime_enter(); - let mut pinned_work = Box::pin(work); - if let Poll::Ready(res) = pinned_work.as_mut().poll(&mut cx) { - return res; - } - return Ok(Err(Errno::Again)); - } - - // Slow path, block on the work and process process - env.tasks().block_on(work) + let poller = Poller { env, pinned_work }; + block_on_with_timeout(env.tasks(), timeout, poller) } // This should be compiled away, it will simply wait forever however its never @@ -689,12 +823,12 @@ pub(crate) fn get_current_time_in_nanos() -> Result { Ok(now as Timestamp) } -pub(crate) fn get_stack_base(mut ctx: &mut FunctionEnvMut<'_, WasiEnv>) -> u64 { - ctx.data().stack_end +pub(crate) fn get_stack_lower(mut ctx: &mut FunctionEnvMut<'_, WasiEnv>) -> u64 { + ctx.data().layout.stack_lower } -pub(crate) fn get_stack_start(mut ctx: &mut FunctionEnvMut<'_, WasiEnv>) -> u64 { - ctx.data().stack_start +pub(crate) fn get_stack_upper(mut ctx: &mut FunctionEnvMut<'_, WasiEnv>) -> u64 { + ctx.data().layout.stack_upper } pub(crate) fn get_memory_stack_pointer( @@ -702,12 +836,12 @@ pub(crate) fn get_memory_stack_pointer( ) -> Result { // Get the current value of the stack pointer (which we will use // to save all of the stack) - let stack_base = get_stack_base(ctx); + let stack_upper = get_stack_upper(ctx); let stack_pointer = if let Some(stack_pointer) = ctx.data().inner().stack_pointer.clone() { match stack_pointer.get(ctx) { Value::I32(a) => a as u64, Value::I64(a) => a as u64, - _ => stack_base, + _ => stack_upper, } } else { return Err("failed to save stack: not exported __stack_pointer global".to_string()); @@ -718,9 +852,9 @@ pub(crate) fn get_memory_stack_pointer( pub(crate) fn get_memory_stack_offset( ctx: &mut FunctionEnvMut<'_, WasiEnv>, ) -> Result { - let stack_base = get_stack_base(ctx); + let stack_upper = get_stack_upper(ctx); let stack_pointer = get_memory_stack_pointer(ctx)?; - Ok(stack_base - stack_pointer) + Ok(stack_upper - stack_pointer) } pub(crate) fn set_memory_stack_offset( @@ -728,8 +862,8 @@ pub(crate) fn set_memory_stack_offset( offset: u64, ) -> Result<(), String> { // Sets the stack pointer - let stack_base = get_stack_base(ctx); - let stack_pointer = stack_base - offset; + let stack_upper = get_stack_upper(ctx); + let stack_pointer = stack_upper - offset; if let Some(stack_pointer_ptr) = ctx.data().inner().stack_pointer.clone() { match stack_pointer_ptr.get(ctx) { Value::I32(_) => { @@ -757,7 +891,7 @@ pub(crate) fn get_memory_stack( ) -> Result { // Get the current value of the stack pointer (which we will use // to save all of the stack) - let stack_base = get_stack_base(ctx); + let stack_base = get_stack_upper(ctx); let stack_pointer = if let Some(stack_pointer) = ctx.data().inner().stack_pointer.clone() { match stack_pointer.get(ctx) { Value::I32(a) => a as u64, @@ -769,7 +903,7 @@ pub(crate) fn get_memory_stack( }; let env = ctx.data(); let memory = env.memory_view(&ctx); - let stack_offset = env.stack_end - stack_pointer; + let stack_offset = env.layout.stack_upper - stack_pointer; // Read the memory stack into a vector let memory_stack_ptr = WasmPtr::::new( @@ -795,9 +929,9 @@ pub(crate) fn set_memory_stack( stack: Bytes, ) -> Result<(), String> { // First we restore the memory stack - let stack_base = get_stack_base(ctx); + let stack_upper = get_stack_upper(ctx); let stack_offset = stack.len() as u64; - let stack_pointer = stack_base - stack_offset; + let stack_pointer = stack_upper - stack_offset; let stack_ptr = WasmPtr::::new( stack_pointer .try_into() @@ -821,6 +955,40 @@ pub(crate) fn set_memory_stack( Ok(()) } +/// Puts the process to deep sleep and wakes it again when +/// the supplied future completes +#[must_use = "you must return the result immediately so the stack can unwind"] +pub(crate) fn deep_sleep( + mut ctx: FunctionEnvMut<'_, WasiEnv>, + work: Fut, +) -> Result +where + Fut: AsyncifyFuture> + Send + Sync + 'static, +{ + // Grab all the globals and serialize them + let store_data = crate::utils::store::capture_snapshot(&mut ctx.as_store_mut()) + .serialize() + .unwrap(); + let store_data = Bytes::from(store_data); + + // Perform the unwind action + let tasks = ctx.data().tasks().clone(); + return unwind::(ctx, move |_ctx, memory_stack, rewind_stack| { + // Schedule the process on the stack so that it can be resumed + return OnCalledAction::Trap(Box::new(RuntimeError::user(Box::new( + WasiError::DeepSleep(DeepSleepWork { + work: Box::new(work), + rewind: RewindState { + memory_stack, + rewind_stack, + store_data, + is_64bit: M::is_64bit(), + }, + }), + )))); + }); +} + #[must_use = "you must return the result immediately so the stack can unwind"] pub(crate) fn unwind( mut ctx: FunctionEnvMut<'_, WasiEnv>, @@ -847,12 +1015,16 @@ where let memory = env.memory_view(&ctx); // Write the addresses to the start of the stack space - let unwind_pointer = env.stack_start; + let unwind_pointer = env.layout.stack_lower; let unwind_data_start = unwind_pointer + (std::mem::size_of::<__wasi_asyncify_t>() as u64); let unwind_data = __wasi_asyncify_t:: { start: wasi_try_ok!(unwind_data_start.try_into().map_err(|_| Errno::Overflow)), - end: wasi_try_ok!(env.stack_end.try_into().map_err(|_| Errno::Overflow)), + end: wasi_try_ok!(env + .layout + .stack_upper + .try_into() + .map_err(|_| Errno::Overflow)), }; let unwind_data_ptr: WasmPtr<__wasi_asyncify_t, M> = WasmPtr::new(wasi_try_ok!(unwind_pointer @@ -873,11 +1045,11 @@ where // Set callback that will be invoked when this process finishes let env = ctx.data(); let unwind_stack_begin: u64 = unwind_data.start.into(); - let total_stack_space = env.stack_end - env.stack_start; + let total_stack_space = env.layout.stack_size; let func = ctx.as_ref(); trace!( - stack_end = env.stack_end, - stack_start = env.stack_start, + stack_upper = env.layout.stack_upper, + stack_lower = env.layout.stack_lower, "wasi[{}:{}]::unwinding (used_stack_space={} total_stack_space={})", ctx.data().pid(), ctx.data().tid(), @@ -940,7 +1112,7 @@ where #[instrument(level = "debug", skip_all, fields(memory_stack_len = memory_stack.len(), rewind_stack_len = rewind_stack.len(), store_data_len = store_data.len()))] #[must_use = "the action must be passed to the call loop"] pub(crate) fn rewind( - mut ctx: FunctionEnvMut<'_, WasiEnv>, + mut ctx: FunctionEnvMut, memory_stack: Bytes, rewind_stack: Bytes, store_data: Bytes, @@ -956,25 +1128,29 @@ pub(crate) fn rewind( return Errno::Unknown; } }; - crate::utils::store::restore_snapshot(&mut ctx.as_store_mut(), &store_snapshot); + crate::utils::store::restore_snapshot(&mut ctx, &store_snapshot); let env = ctx.data(); let memory = env.memory_view(&ctx); // Write the addresses to the start of the stack space - let rewind_pointer = env.stack_start; + let rewind_pointer = env.layout.stack_lower; let rewind_data_start = rewind_pointer + (std::mem::size_of::<__wasi_asyncify_t>() as u64); let rewind_data_end = rewind_data_start + (rewind_stack.len() as u64); - if rewind_data_end > env.stack_end { + if rewind_data_end > env.layout.stack_upper { warn!( "attempting to rewind a stack bigger than the allocated stack space ({} > {})", - rewind_data_end, env.stack_end + rewind_data_end, env.layout.stack_upper ); return Errno::Overflow; } let rewind_data = __wasi_asyncify_t:: { start: wasi_try!(rewind_data_end.try_into().map_err(|_| Errno::Overflow)), - end: wasi_try!(env.stack_end.try_into().map_err(|_| Errno::Overflow)), + end: wasi_try!(env + .layout + .stack_upper + .try_into() + .map_err(|_| Errno::Overflow)), }; let rewind_data_ptr: WasmPtr<__wasi_asyncify_t, M> = WasmPtr::new(wasi_try!(rewind_pointer diff --git a/lib/wasi/src/syscalls/wasi/poll_oneoff.rs b/lib/wasi/src/syscalls/wasi/poll_oneoff.rs index 479051ea6fe..05c7a07723d 100644 --- a/lib/wasi/src/syscalls/wasi/poll_oneoff.rs +++ b/lib/wasi/src/syscalls/wasi/poll_oneoff.rs @@ -23,7 +23,7 @@ use crate::{ /// - `u32 nevents` /// The number of events seen #[instrument(level = "trace", skip_all, fields(timeout_ns = field::Empty, fd_guards = field::Empty, seen = field::Empty), ret, err)] -pub fn poll_oneoff( +pub fn poll_oneoff( mut ctx: FunctionEnvMut<'_, WasiEnv>, in_: WasmPtr, out_: WasmPtr, @@ -45,47 +45,49 @@ pub fn poll_oneoff( subscriptions.push((None, PollEventSet::default(), s)); } - // Poll and receive all the events that triggered - let triggered_events = poll_oneoff_internal(&mut ctx, subscriptions)?; - let triggered_events = match triggered_events { - Ok(a) => a, - Err(err) => { - return Ok(err); + // We clear the number of events + wasi_try_mem_ok!(nevents.write(&memory, M::ZERO)); + + // Function to invoke once the poll is finished + let process_events = move |triggered_events, env: &'_ WasiEnv, store: &'_ dyn AsStoreRef| { + // Process all the events that were triggered + let mut memory = env.memory_view(store); + let mut events_seen: u32 = 0; + let event_array = wasi_try_mem!(out_.slice(&memory, nsubscriptions)); + for event in triggered_events { + wasi_try_mem!(event_array.index(events_seen as u64).write(event)); + events_seen += 1; } + let events_seen: M::Offset = wasi_try!(events_seen.try_into().map_err(|_| Errno::Overflow)); + let out_ptr = nevents.deref(&memory); + wasi_try_mem!(out_ptr.write(events_seen)); + Errno::Success }; - // Process all the events that were triggered - let mut env = ctx.data(); - let mut memory = env.memory_view(&ctx); - let mut events_seen: u32 = 0; - let event_array = wasi_try_mem_ok!(out_.slice(&memory, nsubscriptions)); - for event in triggered_events { - wasi_try_mem_ok!(event_array.index(events_seen as u64).write(event)); - events_seen += 1; - } - let events_seen: M::Offset = wasi_try_ok!(events_seen.try_into().map_err(|_| Errno::Overflow)); - let out_ptr = nevents.deref(&memory); - wasi_try_mem_ok!(out_ptr.write(events_seen)); - Ok(Errno::Success) + // Poll and receive all the events that triggered + poll_oneoff_internal::(ctx, subscriptions, process_events) } -struct PollBatch<'a> { +struct PollBatch { pid: WasiProcessId, tid: WasiThreadId, evts: Vec, - joins: Vec>, + joins: Vec, } -impl<'a> PollBatch<'a> { - fn new(pid: WasiProcessId, tid: WasiThreadId, fds: &'a mut [InodeValFilePollGuard]) -> Self { +impl PollBatch { + fn new(pid: WasiProcessId, tid: WasiThreadId, fds: Vec) -> Self { Self { pid, tid, evts: Vec::new(), - joins: fds.iter_mut().map(InodeValFilePollGuardJoin::new).collect(), + joins: fds + .into_iter() + .map(InodeValFilePollGuardJoin::new) + .collect(), } } } -impl<'a> Future for PollBatch<'a> { +impl Future for PollBatch { type Output = Result, Errno>; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let pid = self.pid; @@ -95,12 +97,19 @@ impl<'a> Future for PollBatch<'a> { let mut evts = Vec::new(); for mut join in self.joins.iter_mut() { let fd = join.fd(); + let peb = join.peb(); let mut guard = Pin::new(join); match guard.poll(cx) { Poll::Pending => {} Poll::Ready(e) => { for evt in e { - tracing::trace!(fd, userdata = evt.userdata, ty = evt.type_ as u8,); + tracing::trace!( + fd, + userdata = evt.userdata, + ty = evt.type_ as u8, + peb, + "triggered" + ); evts.push(evt); } } @@ -127,10 +136,19 @@ impl<'a> Future for PollBatch<'a> { /// Output: /// - `u32 nevents` /// The number of events seen -pub(crate) fn poll_oneoff_internal( - ctx: &mut FunctionEnvMut<'_, WasiEnv>, +pub(crate) fn poll_oneoff_internal( + mut ctx: FunctionEnvMut<'_, WasiEnv>, mut subs: Vec<(Option, PollEventSet, Subscription)>, -) -> Result, Errno>, WasiError> { + process_events: After, +) -> Result +where + After: FnOnce(Vec, &'_ WasiEnv, &'_ dyn AsStoreRef) -> Errno + Send + Sync + 'static, +{ + wasi_try_ok!(WasiEnv::process_signals_and_exit(&mut ctx)?); + if handle_rewind::(&mut ctx) { + return Ok(Errno::Success); + } + let pid = ctx.data().pid(); let tid = ctx.data().tid(); @@ -159,10 +177,10 @@ pub(crate) fn poll_oneoff_internal( fd => { let fd_entry = match state.fs.get_fd(fd) { Ok(a) => a, - Err(err) => return Ok(Err(err)), + Err(err) => return Ok(err), }; if !fd_entry.rights.contains(Rights::POLL_FD_READWRITE) { - return Ok(Err(Errno::Access)); + return Ok(Errno::Access); } } } @@ -177,10 +195,10 @@ pub(crate) fn poll_oneoff_internal( fd => { let fd_entry = match state.fs.get_fd(fd) { Ok(a) => a, - Err(err) => return Ok(Err(err)), + Err(err) => return Ok(err), }; if !fd_entry.rights.contains(Rights::POLL_FD_READWRITE) { - return Ok(Err(Errno::Access)); + return Ok(Errno::Access); } } } @@ -212,7 +230,7 @@ pub(crate) fn poll_oneoff_internal( continue; } else { error!("polling not implemented for these clocks yet"); - return Ok(Err(Errno::Inval)); + return Ok(Errno::Inval); } } }; @@ -220,7 +238,7 @@ pub(crate) fn poll_oneoff_internal( let mut events_seen: u32 = 0; - let ret = { + let batch = { // Build the batch of things we are going to poll let state = ctx.data().state.clone(); let tasks = ctx.data().tasks().clone(); @@ -234,19 +252,19 @@ pub(crate) fn poll_oneoff_internal( if let Some(fd) = fd { let wasi_file_ref = match fd { __WASI_STDERR_FILENO => { - wasi_try_ok_ok!(WasiInodes::stderr(&state.fs.fd_map) + wasi_try_ok!(WasiInodes::stderr(&state.fs.fd_map) .map(|g| g.into_poll_guard(fd, peb, s)) .map_err(fs_error_into_wasi_err)) } __WASI_STDOUT_FILENO => { - wasi_try_ok_ok!(WasiInodes::stdout(&state.fs.fd_map) + wasi_try_ok!(WasiInodes::stdout(&state.fs.fd_map) .map(|g| g.into_poll_guard(fd, peb, s)) .map_err(fs_error_into_wasi_err)) } _ => { - let fd_entry = wasi_try_ok_ok!(state.fs.get_fd(fd)); + let fd_entry = wasi_try_ok!(state.fs.get_fd(fd)); if !fd_entry.rights.contains(Rights::POLL_FD_READWRITE) { - return Ok(Err(Errno::Access)); + return Ok(Errno::Access); } let inode = fd_entry.inode; @@ -257,7 +275,7 @@ pub(crate) fn poll_oneoff_internal( { guard } else { - return Ok(Err(Errno::Badf)); + return Ok(Errno::Badf); } } } @@ -287,47 +305,60 @@ pub(crate) fn poll_oneoff_internal( } // Block polling the file descriptors - let batch = PollBatch::new(pid, tid, &mut guards); - __asyncify(ctx, time_to_sleep, batch)? + PollBatch::new(pid, tid, guards) }; - let mut env = ctx.data(); - memory = env.memory_view(&ctx); + // We use asyncify with a deep sleep to wait on new IO events + let res = __asyncify_with_deep_sleep_ext::( + ctx, + time_to_sleep, + batch, + move |events, env, store| { + // Process the result + match events { + Ok(evts) => { + // If its a timeout then return an event for it + Span::current().record("seen", evts.len()); - // Process the result - match ret { - Ok(evts) => { - // If its a timeout then return an event for it - Span::current().record("seen", evts.len()); - Ok(Ok(evts)) - } - Err(Errno::Timedout) => { - // The timeout has triggerred so lets add that event - if clock_subs.is_empty() && time_to_sleep != Some(Duration::ZERO) { - tracing::warn!("triggered_timeout (without any clock subscriptions)",); - } - let mut evts = Vec::new(); - for (clock_info, userdata) in clock_subs { - let evt = Event { - userdata, - error: Errno::Success, - type_: Eventtype::Clock, - u: EventUnion { clock: 0 }, - }; - Span::current().record( - "seen", - &format!( - "clock(id={},userdata={})", - clock_info.clock_id as u32, evt.userdata - ), - ); - evts.push(evt); + // Process the events + process_events(evts, env, store); + } + Err(Errno::Timedout) => { + // The timeout has triggerred so lets add that event + if clock_subs.is_empty() && time_to_sleep != Some(Duration::ZERO) { + tracing::warn!("triggered_timeout (without any clock subscriptions)",); + } + let mut evts = Vec::new(); + for (clock_info, userdata) in clock_subs { + let evt = Event { + userdata, + error: Errno::Success, + type_: Eventtype::Clock, + u: EventUnion { clock: 0 }, + }; + Span::current().record( + "seen", + &format!( + "clock(id={},userdata={})", + clock_info.clock_id as u32, evt.userdata + ), + ); + evts.push(evt); + } + + // Process the events + process_events(evts, env, store); + } + // If nonblocking the Errno::Again needs to be turned into an empty list + Err(Errno::Again) if time_to_sleep == Some(Duration::ZERO) => { + process_events(Default::default(), env, store); + } + // Otherwise process the error + Err(err) => { + env.thread.set_status_finished(Ok(err.into())); + } } - Ok(Ok(evts)) - } - // If nonblocking the Errno::Again needs to be turned into an empty list - Err(Errno::Again) if time_to_sleep == Some(Duration::ZERO) => Ok(Ok(Default::default())), - // Otherwise process the rror - Err(err) => Ok(Err(err)), - } + }, + )?; + Ok(Errno::Success) } diff --git a/lib/wasi/src/syscalls/wasi/proc_exit.rs b/lib/wasi/src/syscalls/wasi/proc_exit.rs index e0a2d885f21..e434227d838 100644 --- a/lib/wasi/src/syscalls/wasi/proc_exit.rs +++ b/lib/wasi/src/syscalls/wasi/proc_exit.rs @@ -32,11 +32,14 @@ pub fn proc_exit( // If the return value offset is within the memory stack then we need // to update it here rather than in the real memory + let val_bytes = pid.raw().to_ne_bytes(); let pid_offset: u64 = vfork.pid_offset; - if pid_offset >= wasi_env.stack_start && pid_offset < wasi_env.stack_end { + if pid_offset >= wasi_env.layout.stack_lower + && (pid_offset + val_bytes.len() as u64) <= wasi_env.layout.stack_upper + { // Make sure its within the "active" part of the memory stack - let offset = wasi_env.stack_end - pid_offset; - if offset as usize > memory_stack.len() { + let offset = wasi_env.layout().stack_upper - pid_offset; + if (offset as usize + val_bytes.len()) > memory_stack.len() { warn!( "fork failed - the return value (pid) is outside of the active part of the memory stack ({} vs {})", offset, @@ -46,7 +49,6 @@ pub fn proc_exit( } // Update the memory stack with the new PID - let val_bytes = pid.raw().to_ne_bytes(); let pstart = memory_stack.len() - offset as usize; let pend = pstart + val_bytes.len(); let pbytes = &mut memory_stack[pstart..pend]; diff --git a/lib/wasi/src/syscalls/wasix/futex_wait.rs b/lib/wasi/src/syscalls/wasix/futex_wait.rs index dc806195fdf..d9944e1cf7c 100644 --- a/lib/wasi/src/syscalls/wasix/futex_wait.rs +++ b/lib/wasi/src/syscalls/wasix/futex_wait.rs @@ -3,32 +3,47 @@ use std::task::Waker; use super::*; use crate::syscalls::*; -struct FutexPoller<'a, M> +#[derive(Clone)] +struct FutexPoller where M: MemorySize, { - env: &'a WasiEnv, - view: MemoryView<'a>, + state: Arc, futex_idx: u64, futex_ptr: WasmPtr, expected: u32, + ret_woken: WasmPtr, } -impl<'a, M> Future for FutexPoller<'a, M> +impl AsyncifyFuture for FutexPoller where M: MemorySize, { type Output = Result<(), Errno>; - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + fn poll( + &mut self, + env: &WasiEnv, + store: &dyn AsStoreRef, + cx: &mut Context<'_>, + ) -> Poll { let waker = cx.waker(); - let mut guard = self.env.state.futexs.lock().unwrap(); + let view = env.memory_view(store); + let mut guard = env.state.futexs.lock().unwrap(); { - let val = match self.futex_ptr.read(&self.view) { + let val = match self.futex_ptr.read(&view) { Ok(a) => a, - Err(err) => return Poll::Ready(Err(mem_error_to_wasi(err))), + Err(err) => { + self.ret_woken.write(&view, Bool::True).ok(); + return Poll::Ready(Err(mem_error_to_wasi(err))); + } }; if val != self.expected { - return Poll::Ready(Ok(())); + // If we are triggered then we should update the return value + let ret = self + .ret_woken + .write(&view, Bool::True) + .map_err(mem_error_to_wasi); + return Poll::Ready(ret); } } @@ -42,13 +57,13 @@ where Poll::Pending } } -impl<'a, M> Drop for FutexPoller<'a, M> +impl Drop for FutexPoller where M: MemorySize, { fn drop(&mut self) { let futex = { - let mut guard = self.env.state.futexs.lock().unwrap(); + let mut guard = self.state.futexs.lock().unwrap(); guard.remove(&self.futex_idx) }; if let Some(futex) = futex { @@ -67,7 +82,7 @@ where /// * `expected` - Expected value that should be currently held at the memory location /// * `timeout` - Timeout should the futex not be triggered in the allocated time #[instrument(level = "trace", skip_all, fields(futex_idx = field::Empty, expected, timeout = field::Empty, woken = field::Empty), err)] -pub fn futex_wait( +pub fn futex_wait( mut ctx: FunctionEnvMut<'_, WasiEnv>, futex_ptr: WasmPtr, expected: u32, @@ -76,6 +91,11 @@ pub fn futex_wait( ) -> Result { wasi_try_ok!(WasiEnv::process_signals_and_exit(&mut ctx)?); + // If we were just restored then we were woken after a deep sleep + if handle_rewind::(&mut ctx) { + return Ok(Errno::Success); + } + // Determine the timeout let mut env = ctx.data(); let timeout = { @@ -92,34 +112,22 @@ pub fn futex_wait( let futex_idx: u64 = wasi_try_ok!(futex_ptr.offset().try_into().map_err(|_| Errno::Overflow)); Span::current().record("futex_idx", futex_idx); + // We clear the woken flag (so if the poller fails to trigger + // then the value is not set) - the poller will set it to true + let memory = env.memory_view(&ctx); + wasi_try_mem_ok!(ret_woken.write(&memory, Bool::False)); + // Create a poller which will register ourselves against // this futex event and check when it has changed - let view = env.memory_view(&ctx); let poller = FutexPoller { - env, - view, + state: env.state.clone(), futex_idx, futex_ptr, expected, + ret_woken, }; - // Wait for the futex to trigger or a timeout to occur - let res = __asyncify_light(env, timeout, poller)?; - - // Process it and return the result - let mut ret = Errno::Success; - let woken = match res { - Err(Errno::Timedout) => Bool::False, - Err(err) => { - ret = err; - Bool::True - } - Ok(_) => Bool::True, - }; - Span::current().record("woken", woken as u8); - - let memory = env.memory_view(&ctx); - let mut env = ctx.data(); - wasi_try_mem_ok!(ret_woken.write(&memory, woken)); - Ok(ret) + // We use asyncify on the poller and potentially go into deep sleep + __asyncify_with_deep_sleep::(ctx, timeout, poller)?; + Ok(Errno::Success) } diff --git a/lib/wasi/src/syscalls/wasix/futex_wake_all.rs b/lib/wasi/src/syscalls/wasix/futex_wake_all.rs index a0990aaf4ec..7c03c8d5029 100644 --- a/lib/wasi/src/syscalls/wasix/futex_wake_all.rs +++ b/lib/wasi/src/syscalls/wasix/futex_wake_all.rs @@ -6,7 +6,7 @@ use crate::syscalls::*; /// ## Parameters /// /// * `futex` - Memory location that holds a futex that others may be waiting on -#[instrument(level = "trace", skip_all, fields(futex_idx = field::Empty, woken = field::Empty), ret)] +//#[instrument(level = "trace", skip_all, fields(futex_idx = field::Empty, woken = field::Empty), ret)] pub fn futex_wake_all( ctx: FunctionEnvMut<'_, WasiEnv>, futex_ptr: WasmPtr, @@ -17,7 +17,7 @@ pub fn futex_wake_all( let state = env.state.deref(); let pointer: u64 = wasi_try!(futex_ptr.offset().try_into().map_err(|_| Errno::Overflow)); - Span::current().record("futex_idx", pointer); + //Span::current().record("futex_idx", pointer); let mut woken = false; let woken = { @@ -29,13 +29,12 @@ pub fn futex_wake_all( false } }; - Span::current().record("woken", woken); + //Span::current().record("woken", woken); let woken = match woken { false => Bool::False, true => Bool::True, }; wasi_try_mem!(ret_woken.write(&memory, woken)); - Errno::Success } diff --git a/lib/wasi/src/syscalls/wasix/proc_exec.rs b/lib/wasi/src/syscalls/wasix/proc_exec.rs index 40371d0c220..b4cbeeb127e 100644 --- a/lib/wasi/src/syscalls/wasix/proc_exec.rs +++ b/lib/wasi/src/syscalls/wasix/proc_exec.rs @@ -23,6 +23,15 @@ pub fn proc_exec( args: WasmPtr, args_len: M::Offset, ) -> Result<(), WasiError> { + WasiEnv::process_signals_and_exit(&mut ctx)?; + + // If we were just restored the stack then we were woken after a deep sleep + if handle_rewind::(&mut ctx) { + // We should never get here as the process will be termined + // in the `WasiEnv::process_signals_and_exit()` call + return Err(WasiError::Exit(Errno::Unknown.into())); + } + let memory = ctx.data().memory_view(&ctx); let mut name = name.read_utf8_string(&memory, name_len).map_err(|err| { warn!("failed to execve as the name could not be read - {}", err); @@ -78,8 +87,8 @@ pub fn proc_exec( _prepare_wasi(&mut wasi_env, Some(args)); // Recrod the stack offsets before we give up ownership of the wasi_env - let stack_base = wasi_env.stack_end; - let stack_start = wasi_env.stack_start; + let stack_lower = wasi_env.layout.stack_lower; + let stack_upper = wasi_env.layout.stack_upper; // Spawn a new process with this current execution environment let mut err_exit_code: ExitCode = Errno::Success.into(); @@ -141,11 +150,12 @@ pub fn proc_exec( // If the return value offset is within the memory stack then we need // to update it here rather than in the real memory + let val_bytes = child_pid.raw().to_ne_bytes(); let pid_offset: u64 = vfork.pid_offset; - if pid_offset >= stack_start && pid_offset < stack_base { + if pid_offset >= stack_lower && (pid_offset + val_bytes.len() as u64) <= stack_upper { // Make sure its within the "active" part of the memory stack - let offset = stack_base - pid_offset; - if offset as usize > memory_stack.len() { + let offset = stack_upper - pid_offset; + if (offset as usize + val_bytes.len()) > memory_stack.len() { warn!( "vfork failed - the return value (pid) is outside of the active part of the memory stack ({} vs {})", offset, @@ -153,7 +163,6 @@ pub fn proc_exec( ); } else { // Update the memory stack with the new PID - let val_bytes = child_pid.raw().to_ne_bytes(); let pstart = memory_stack.len() - offset as usize; let pend = pstart + val_bytes.len(); let pbytes = &mut memory_stack[pstart..pend]; @@ -181,87 +190,94 @@ pub fn proc_exec( } } })?; - return Ok(()); + Ok(()) } // Otherwise we need to unwind the stack to get out of the current executing // callstack, steal the memory/WasiEnv and switch it over to a new thread // on the new module else { - // We need to unwind out of this process and launch a new process in its place - unwind::(ctx, move |mut ctx, _, _| { - // Prepare the environment - let mut wasi_env = ctx.data_mut().duplicate(); - _prepare_wasi(&mut wasi_env, Some(args)); + // Prepare the environment + let mut wasi_env = ctx.data_mut().duplicate(); + _prepare_wasi(&mut wasi_env, Some(args)); - // Get a reference to the runtime - let bin_factory = ctx.data().bin_factory.clone(); - let tasks = wasi_env.tasks().clone(); + // Get a reference to the runtime + let bin_factory = ctx.data().bin_factory.clone(); + let tasks = wasi_env.tasks().clone(); - // Create the process and drop the context - let bin_factory = Box::new(ctx.data().bin_factory.clone()); + // Create the process and drop the context + let bin_factory = Box::new(ctx.data().bin_factory.clone()); - let mut new_store = Some(new_store); - let mut builder = Some(wasi_env); + let mut new_store = Some(new_store); + let mut builder = Some(wasi_env); - let process = match bin_factory.try_built_in( - name.clone(), - Some(&ctx), - &mut new_store, - &mut builder, - ) { - Ok(a) => Ok(Ok(a)), - Err(err) => { - if err != VirtualBusError::NotFound { - error!("builtin failed - {}", err); - } + let process = match bin_factory.try_built_in( + name.clone(), + Some(&ctx), + &mut new_store, + &mut builder, + ) { + Ok(a) => Ok(Ok(a)), + Err(err) => { + if err != VirtualBusError::NotFound { + error!("builtin failed - {}", err); + } - let new_store = new_store.take().unwrap(); - let env = builder.take().unwrap(); + let new_store = new_store.take().unwrap(); + let env = builder.take().unwrap(); - // Spawn a new process with this current execution environment - //let pid = wasi_env.process.pid(); - let (tx, rx) = std::sync::mpsc::channel(); - tasks.block_on(Box::pin(async move { - let ret = bin_factory.spawn(name, new_store, env).await; - tx.send(ret); - })); - rx.recv() - } - }; + // Spawn a new process with this current execution environment + //let pid = wasi_env.process.pid(); + let (tx, rx) = std::sync::mpsc::channel(); + tasks.block_on(Box::pin(async move { + let ret = bin_factory.spawn(name, new_store, env).await; + tx.send(ret); + })); + rx.recv() + } + }; + + match process { + Ok(Ok(mut process)) => { + // If we support deep sleeping then we switch to deep sleep mode + let env = ctx.data(); - match process { - Ok(Ok(mut process)) => { - // Wait for the sub-process to exit itself - then we will exit - let (tx, rx) = std::sync::mpsc::channel(); - let tasks_inner = tasks.clone(); - tasks.block_on(Box::pin(async move { - let code = process + // The poller will wait for the process to actually finish + let res = __asyncify_with_deep_sleep_ext::( + ctx, + None, + async move { + process .wait_finished() .await - .unwrap_or_else(|_| Errno::Child.into()); - tx.send(code); - })); - let exit_code = rx.recv().unwrap(); - OnCalledAction::Trap(Box::new(WasiError::Exit(exit_code))) - } - Ok(Err(err)) => { - warn!( - "failed to execve as the process could not be spawned (fork)[0] - {}", - err - ); - OnCalledAction::Trap(Box::new(WasiError::Exit(Errno::Noexec.into()))) - } - Err(err) => { - warn!( - "failed to execve as the process could not be spawned (fork)[1] - {}", - err - ); - OnCalledAction::Trap(Box::new(WasiError::Exit(Errno::Noexec.into()))) - } + .unwrap_or_else(|_| Errno::Child.into()) + }, + |exit_code, env, _| { + env.thread.set_status_finished(Ok(exit_code)); + }, + )?; + return match res { + AsyncifyAction::Finish(mut ctx) => { + // When we arrive here the process should already be terminated + WasiEnv::process_signals_and_exit(&mut ctx)?; + Err(WasiError::Exit(Errno::Unknown.into())) + } + AsyncifyAction::Unwind => Ok(()), + }; } - })?; + Ok(Err(err)) => { + warn!( + "failed to execve as the process could not be spawned (fork)[0] - {}", + err + ); + Err(WasiError::Exit(Errno::Noexec.into())) + } + Err(err) => { + warn!( + "failed to execve as the process could not be spawned (fork)[1] - {}", + err + ); + Err(WasiError::Exit(Errno::Noexec.into())) + } + } } - - // Success - Ok(()) } diff --git a/lib/wasi/src/syscalls/wasix/proc_fork.rs b/lib/wasi/src/syscalls/wasix/proc_fork.rs index aafa0bd41aa..d31c57b8095 100644 --- a/lib/wasi/src/syscalls/wasix/proc_fork.rs +++ b/lib/wasi/src/syscalls/wasix/proc_fork.rs @@ -1,7 +1,7 @@ use super::*; -use crate::{os::task::OwnedTaskStatus, syscalls::*}; +use crate::{os::task::OwnedTaskStatus, runtime::task_manager::TaskResumeAction, syscalls::*}; -use wasmer::vm::VMMemory; +use wasmer::{vm::VMMemory, MemoryType}; /// ### `proc_fork()` /// Forks the current process into a new subprocess. If the function @@ -32,6 +32,7 @@ pub fn proc_fork( // and associate a new context but otherwise shares things like the // file system interface. The handle to the forked process is stored // in the parent process context + let capable_of_deep_sleep = ctx.data().capable_of_deep_sleep(); let (mut child_env, mut child_handle) = match ctx.data().fork() { Ok(p) => p, Err(err) => { @@ -165,6 +166,7 @@ pub fn proc_fork( let pid = child_env.pid(); let tid = child_env.tid(); child_env.runtime = runtime.clone(); + child_env.enable_deep_sleep = capable_of_deep_sleep; let mut ctx = WasiFunctionEnv::new(&mut store, child_env); // fork_store, fork_module, @@ -196,7 +198,8 @@ pub fn proc_fork( // Rewind the stack and carry on { trace!("rewinding child"); - let ctx = ctx.env.clone().into_mut(&mut store); + let mut ctx = ctx.env.clone().into_mut(&mut store); + let (data, mut store) = ctx.data_and_store_mut(); match rewind::( ctx, child_memory_stack.freeze(), @@ -215,26 +218,9 @@ pub fn proc_fork( } // Invoke the start function - let mut ret: ExitCode = Errno::Success.into(); - let err = if ctx.data(&store).thread.is_main() { - trace!("re-invoking main"); - let start = ctx.data(&store).inner().start.clone().unwrap(); - start.call(&mut store) - } else { - trace!("re-invoking thread_spawn"); - let start = ctx.data(&store).inner().thread_spawn.clone().unwrap(); - start.call(&mut store, 0, 0) - }; - if let Err(err) = err { - if let Ok(WasiError::Exit(exit_code)) = err.downcast::() { - ret = exit_code; - } - } + let ret = run::(ctx, store, module, tasks, fork_memory_ty, None); trace!(%pid, %tid, "child exited (code = {})", ret); - // Clean up the environment - ctx.cleanup((&mut store), Some(ret)); - // Send the result drop(child_handle); }; @@ -253,10 +239,13 @@ pub fn proc_fork( // If the return value offset is within the memory stack then we need // to update it here rather than in the real memory + let val_bytes = child_pid.raw().to_ne_bytes(); let pid_offset: u64 = pid_offset.into(); - if pid_offset >= env.stack_start && pid_offset < env.stack_end { + if pid_offset >= env.layout.stack_lower + && (pid_offset + val_bytes.len() as u64) <= env.layout.stack_upper + { // Make sure its within the "active" part of the memory stack - let offset = env.stack_end - pid_offset; + let offset = env.layout.stack_upper - pid_offset; if offset as usize > memory_stack.len() { warn!( "failed - the return value (pid) is outside of the active part of the memory stack ({} vs {})", @@ -267,7 +256,6 @@ pub fn proc_fork( } // Update the memory stack with the new PID - let val_bytes = child_pid.raw().to_ne_bytes(); let pstart = memory_stack.len() - offset as usize; let pend = pstart + val_bytes.len(); let pbytes = &mut memory_stack[pstart..pend]; @@ -294,3 +282,75 @@ pub fn proc_fork( } }) } + +fn run( + ctx: WasiFunctionEnv, + mut store: Store, + module: Module, + tasks: Arc, + fork_memory_ty: MemoryType, + rewind_state: Option, +) -> ExitCode { + // If we need to rewind then do so + if let Some(rewind_state) = rewind_state { + let ctx = ctx.env.clone().into_mut(&mut store); + let res = rewind::( + ctx, + rewind_state.memory_stack.freeze(), + rewind_state.rewind_stack.freeze(), + rewind_state.store_data, + ); + if res != Errno::Success { + return res.into(); + } + } + + let mut ret: ExitCode = Errno::Success.into(); + let err = if ctx.data(&store).thread.is_main() { + trace!("re-invoking main"); + let start = ctx.data(&store).inner().start.clone().unwrap(); + start.call(&mut store) + } else { + trace!("re-invoking thread_spawn"); + let start = ctx.data(&store).inner().thread_spawn.clone().unwrap(); + start.call(&mut store, 0, 0) + }; + if let Err(err) = err { + match err.downcast::() { + Ok(WasiError::Exit(exit_code)) => { + ret = exit_code; + } + Ok(WasiError::DeepSleep(deep)) => { + trace!("entered a deep sleep"); + + // Extract the memory + let memory = match ctx.data(&store).memory().try_clone(&store) { + Some(m) => m, + None => { + tracing::error!("failed to clone memory before deep sleep"); + return Errno::Noexec.into(); + } + }; + + // Create the respawn function + let respawn = { + let ctx = ctx.clone(); + let tasks = tasks.clone(); + let rewind_state = Some(deep.rewind); + move |store, module| { + run::(ctx, store, module, tasks, fork_memory_ty, rewind_state); + } + }; + + /// Spawns the WASM process after a trigger + tasks.resume_wasm_after_poller(Box::new(respawn), store, module, ctx, deep.work); + return Errno::Unknown.into(); + } + _ => {} + } + } + + // Clean up the environment and return the result + ctx.cleanup((&mut store), Some(ret)); + ret +} diff --git a/lib/wasi/src/syscalls/wasix/proc_join.rs b/lib/wasi/src/syscalls/wasix/proc_join.rs index 7e551cc00c7..7bb1d91e916 100644 --- a/lib/wasi/src/syscalls/wasix/proc_join.rs +++ b/lib/wasi/src/syscalls/wasix/proc_join.rs @@ -1,7 +1,7 @@ use wasmer_wasix_types::wasi::{JoinFlags, JoinStatus, JoinStatusType, JoinStatusUnion, OptionPid}; use super::*; -use crate::syscalls::*; +use crate::{syscalls::*, WasiProcess}; /// ### `proc_join()` /// Joins the child process, blocking this one until the other finishes @@ -10,7 +10,7 @@ use crate::syscalls::*; /// /// * `pid` - Handle of the child process to wait on #[instrument(level = "trace", skip_all, fields(pid = ctx.data().process.pid().raw()), ret, err)] -pub fn proc_join( +pub fn proc_join( mut ctx: FunctionEnvMut<'_, WasiEnv>, pid_ptr: WasmPtr, _flags: JoinFlags, @@ -18,6 +18,37 @@ pub fn proc_join( ) -> Result { wasi_try_ok!(WasiEnv::process_signals_and_exit(&mut ctx)?); + // This lambda will look at what we wrote in the status variable + // and use this to determine the return code sent back to the caller + let ret_result = { + let status_ptr = status_ptr.clone(); + move |ctx: FunctionEnvMut<'_, WasiEnv>| { + let view = ctx.data().memory_view(&ctx); + let status = wasi_try_mem_ok!(status_ptr.read(&view)); + if status.tag == JoinStatusType::Nothing { + let ret = unsafe { status.u.nothing_errno }; + wasi_try_mem_ok!(status_ptr.write( + &view, + JoinStatus { + tag: JoinStatusType::Nothing, + u: JoinStatusUnion { + nothing_errno: Errno::Success + }, + } + )); + return Ok(ret); + } else { + return Ok(Errno::Success); + } + } + }; + + // If we were just restored the stack then we were woken after a deep sleep + // and the return calues are already set + if handle_rewind::(&mut ctx) { + return ret_result(ctx); + } + let env = ctx.data(); let memory = env.memory_view(&ctx); let option_pid = wasi_try_mem_ok!(pid_ptr.read(&memory)); @@ -27,45 +58,79 @@ pub fn proc_join( }; tracing::trace!("filter_pid = {:?}", option_pid); + // Clear the existing values (in case something goes wrong) + wasi_try_mem_ok!(pid_ptr.write( + &memory, + OptionPid { + tag: OptionTag::None, + pid: 0, + } + )); + wasi_try_mem_ok!(status_ptr.write( + &memory, + JoinStatus { + tag: JoinStatusType::Nothing, + u: JoinStatusUnion { + nothing_errno: Errno::Success + }, + } + )); + // If the ID is maximum then it means wait for any of the children let pid = match option_pid { None => { let mut process = ctx.data_mut().process.clone(); - let child_exit = wasi_try_ok!(__asyncify(&mut ctx, None, async move { - process.join_any_child().await - })?); - return match child_exit { - Some((pid, exit_code)) => { - trace!(ret_id = pid.raw(), exit_code = exit_code.raw()); - let env = ctx.data(); - let memory = env.memory_view(&ctx); - - let option_pid = OptionPid { - tag: OptionTag::Some, - pid: pid.raw() as Pid, - }; - wasi_try_mem_ok!(pid_ptr.write(&memory, option_pid)); - - let status = JoinStatus { - tag: JoinStatusType::ExitNormal, - u: JoinStatusUnion { - exit_normal: exit_code.into(), - }, - }; - wasi_try_mem_ok!(status_ptr.write(&memory, status)); - Ok(Errno::Success) - } - None => { - let env = ctx.data(); - let memory = env.memory_view(&ctx); - - let status = JoinStatus { - tag: JoinStatusType::Nothing, - u: JoinStatusUnion { nothing: 0 }, - }; - wasi_try_mem_ok!(status_ptr.write(&memory, status)); - Ok(Errno::Child) - } + let pid_ptr = pid_ptr.clone(); + let status_ptr = status_ptr.clone(); + + // We wait for any process to exit (if it takes too long + // then we go into a deep sleep) + let res = __asyncify_with_deep_sleep_ext::( + ctx, + None, + async move { process.join_any_child().await }, + move |child_exit, env, store| { + let memory = env.memory_view(store); + match child_exit { + Ok(Some((pid, exit_code))) => { + trace!(ret_id = pid.raw(), exit_code = exit_code.raw()); + + let option_pid = OptionPid { + tag: OptionTag::Some, + pid: pid.raw() as Pid, + }; + pid_ptr.write(&memory, option_pid).ok(); + + let status = JoinStatus { + tag: JoinStatusType::ExitNormal, + u: JoinStatusUnion { + exit_normal: exit_code.into(), + }, + }; + status_ptr.write(&memory, status).ok(); + } + Ok(None) => { + let status = JoinStatus { + tag: JoinStatusType::Nothing, + u: JoinStatusUnion { + nothing_errno: Errno::Child, + }, + }; + status_ptr.write(&memory, status).ok(); + } + Err(err) => { + let status = JoinStatus { + tag: JoinStatusType::Nothing, + u: JoinStatusUnion { nothing_errno: err }, + }; + status_ptr.write(&memory, status).ok(); + } + } + }, + )?; + return match res { + AsyncifyAction::Finish(ctx) => ret_result(ctx), + AsyncifyAction::Unwind => Ok(Errno::Success), }; } Some(pid) => pid, @@ -96,40 +161,41 @@ pub fn proc_join( } if let Some(process) = process { - let exit_code = wasi_try_ok!(__asyncify(&mut ctx, None, async move { - let code = process.join().await.unwrap_or_else(|_| Errno::Child.into()); - Ok(code) - }) - .map_err(|err| { - trace!( - %pid, - %err - ); - err - })?); - - trace!(ret_id = pid.raw(), exit_code = exit_code.raw()); - let env = ctx.data(); - { - let mut inner = env.process.inner.write().unwrap(); - inner.children.retain(|a| a.pid != pid); - } - - let memory = env.memory_view(&ctx); + // We can already set the process ID + wasi_try_mem_ok!(pid_ptr.write( + &memory, + OptionPid { + tag: OptionTag::Some, + pid: pid.raw(), + } + )); + + // Wait for the process to finish + let res = __asyncify_with_deep_sleep_ext::( + ctx, + None, + async move { process.join().await.unwrap_or_else(|_| Errno::Child.into()) }, + move |exit_code, env, store| { + trace!(ret_id = pid.raw(), exit_code = exit_code.raw()); + { + let mut inner = env.process.inner.write().unwrap(); + inner.children.retain(|a| a.pid != pid); + } - let option_pid = OptionPid { - tag: OptionTag::Some, - pid: pid.raw(), - }; - let status = JoinStatus { - tag: JoinStatusType::ExitNormal, - u: JoinStatusUnion { - exit_normal: exit_code.into(), + let memory = env.memory_view(store); + let status = JoinStatus { + tag: JoinStatusType::ExitNormal, + u: JoinStatusUnion { + exit_normal: exit_code.into(), + }, + }; + status_ptr.write(&memory, status).ok(); }, + )?; + return match res { + AsyncifyAction::Finish(ctx) => ret_result(ctx), + AsyncifyAction::Unwind => Ok(Errno::Success), }; - wasi_try_mem_ok!(pid_ptr.write(&memory, option_pid)); - wasi_try_mem_ok!(status_ptr.write(&memory, status)); - return Ok(Errno::Success); } trace!(ret_id = pid.raw(), "status=nothing"); @@ -139,8 +205,10 @@ pub fn proc_join( let status = JoinStatus { tag: JoinStatusType::Nothing, - u: JoinStatusUnion { nothing: 0 }, + u: JoinStatusUnion { + nothing_errno: Errno::Success, + }, }; wasi_try_mem_ok!(status_ptr.write(&memory, status)); - Ok(Errno::Child) + return ret_result(ctx); } diff --git a/lib/wasi/src/syscalls/wasix/sched_yield.rs b/lib/wasi/src/syscalls/wasix/sched_yield.rs index 9ae99a0a4d4..d6745761c6f 100644 --- a/lib/wasi/src/syscalls/wasix/sched_yield.rs +++ b/lib/wasi/src/syscalls/wasix/sched_yield.rs @@ -4,7 +4,9 @@ use crate::syscalls::*; /// ### `sched_yield()` /// Yields execution of the thread #[instrument(level = "trace", skip_all, ret, err)] -pub fn sched_yield(mut ctx: FunctionEnvMut<'_, WasiEnv>) -> Result { +pub fn sched_yield( + mut ctx: FunctionEnvMut<'_, WasiEnv>, +) -> Result { //trace!("wasi[{}:{}]::sched_yield", ctx.data().pid(), ctx.data().tid()); - thread_sleep_internal(ctx, 0) + thread_sleep_internal::(ctx, 0) } diff --git a/lib/wasi/src/syscalls/wasix/stack_checkpoint.rs b/lib/wasi/src/syscalls/wasix/stack_checkpoint.rs index 7de1e9afc77..85023fe30cf 100644 --- a/lib/wasi/src/syscalls/wasix/stack_checkpoint.rs +++ b/lib/wasi/src/syscalls/wasix/stack_checkpoint.rs @@ -93,10 +93,12 @@ pub fn stack_checkpoint( let mut memory_stack_corrected = memory_stack.clone(); { let snapshot_offset: u64 = snapshot_offset.into(); - if snapshot_offset >= env.stack_start && snapshot_offset < env.stack_end { + if snapshot_offset >= env.layout.stack_lower + && (snapshot_offset + val_bytes.len() as u64) <= env.layout.stack_upper + { // Make sure its within the "active" part of the memory stack // (note - the area being written to might not go past the memory pointer) - let offset = env.stack_end - snapshot_offset; + let offset = env.layout.stack_upper - snapshot_offset; if (offset as usize) < memory_stack_corrected.len() { let left = memory_stack_corrected.len() - (offset as usize); let end = offset + (val_bytes.len().min(left) as u64); diff --git a/lib/wasi/src/syscalls/wasix/stack_restore.rs b/lib/wasi/src/syscalls/wasix/stack_restore.rs index ec978c57492..66c7ea753d8 100644 --- a/lib/wasi/src/syscalls/wasix/stack_restore.rs +++ b/lib/wasi/src/syscalls/wasix/stack_restore.rs @@ -40,11 +40,13 @@ pub fn stack_restore( // If the return value offset is within the memory stack then we need // to update it here rather than in the real memory + let val_bytes = val.to_ne_bytes(); let ret_val_offset = snapshot.user; - if ret_val_offset >= env.stack_start && ret_val_offset < env.stack_end { + if ret_val_offset >= env.layout.stack_lower + && (ret_val_offset + val_bytes.len() as u64) <= env.layout.stack_upper + { // Make sure its within the "active" part of the memory stack - let val_bytes = val.to_ne_bytes(); - let offset = env.stack_end - ret_val_offset; + let offset = env.layout.stack_upper - ret_val_offset; let end = offset + (val_bytes.len() as u64); if end as usize > memory_stack.len() { warn!( diff --git a/lib/wasi/src/syscalls/wasix/thread_join.rs b/lib/wasi/src/syscalls/wasix/thread_join.rs index db2015936bc..fe806bc7dd0 100644 --- a/lib/wasi/src/syscalls/wasix/thread_join.rs +++ b/lib/wasi/src/syscalls/wasix/thread_join.rs @@ -9,20 +9,27 @@ use crate::syscalls::*; /// /// * `tid` - Handle of the thread to wait on #[instrument(level = "debug", skip_all, fields(join_tid), ret, err)] -pub fn thread_join( +pub fn thread_join( mut ctx: FunctionEnvMut<'_, WasiEnv>, join_tid: Tid, ) -> Result { wasi_try_ok!(WasiEnv::process_signals_and_exit(&mut ctx)?); + if handle_rewind::(&mut ctx) { + return Ok(Errno::Success); + } let env = ctx.data(); let tid: WasiThreadId = join_tid.into(); let other_thread = env.process.get_thread(&tid); if let Some(other_thread) = other_thread { - wasi_try_ok!(__asyncify(&mut ctx, None, async move { - other_thread.join().await; - Ok(()) - })?); + let res = __asyncify_with_deep_sleep_ext::( + ctx, + None, + async move { + other_thread.join().await; + }, + move |_, _, _| {}, + )?; Ok(Errno::Success) } else { Ok(Errno::Success) diff --git a/lib/wasi/src/syscalls/wasix/thread_local_get.rs b/lib/wasi/src/syscalls/wasix/thread_local_get.rs index cf971bf4373..e6713a60279 100644 --- a/lib/wasi/src/syscalls/wasix/thread_local_get.rs +++ b/lib/wasi/src/syscalls/wasix/thread_local_get.rs @@ -9,7 +9,7 @@ use crate::syscalls::*; /// * `key` - Thread key that this local variable that was previous set #[instrument(level = "trace", skip_all, fields(key, val = field::Empty), ret)] pub fn thread_local_get( - ctx: FunctionEnvMut<'_, WasiEnv>, + mut ctx: FunctionEnvMut<'_, WasiEnv>, key: TlKey, ret_val: WasmPtr, ) -> Errno { diff --git a/lib/wasi/src/syscalls/wasix/thread_sleep.rs b/lib/wasi/src/syscalls/wasix/thread_sleep.rs index d4ae2da2d84..627ed57c8e6 100644 --- a/lib/wasi/src/syscalls/wasix/thread_sleep.rs +++ b/lib/wasi/src/syscalls/wasix/thread_sleep.rs @@ -8,18 +8,21 @@ use crate::syscalls::*; /// /// * `duration` - Amount of time that the thread should sleep #[instrument(level = "debug", skip_all, fields(duration), ret, err)] -pub fn thread_sleep( +pub fn thread_sleep( mut ctx: FunctionEnvMut<'_, WasiEnv>, duration: Timestamp, ) -> Result { - thread_sleep_internal(ctx, duration) + thread_sleep_internal::(ctx, duration) } -pub(crate) fn thread_sleep_internal( +pub(crate) fn thread_sleep_internal( mut ctx: FunctionEnvMut<'_, WasiEnv>, duration: Timestamp, ) -> Result { wasi_try_ok!(WasiEnv::process_signals_and_exit(&mut ctx)?); + if handle_rewind::(&mut ctx) { + return Ok(Errno::Success); + } let env = ctx.data(); @@ -31,14 +34,20 @@ pub(crate) fn thread_sleep_internal( if duration > 0 { let duration = Duration::from_nanos(duration as u64); let tasks = env.tasks().clone(); - wasi_try_ok!(__asyncify(&mut ctx, Some(duration), async move { - // using an infinite async sleep here means we don't have to write the same event - // handling loop code for signals and timeouts - InfiniteSleep::default().await; - unreachable!( - "the timeout or signals will wake up this thread even though it waits forever" - ) - })?); + + __asyncify_with_deep_sleep_ext::( + ctx, + Some(duration), + async move { + // using an infinite async sleep here means we don't have to write the same event + // handling loop code for signals and timeouts + InfiniteSleep::default().await; + unreachable!( + "the timeout or signals will wake up this thread even though it waits forever" + ) + }, + move |_, _, _| {}, + )?; } Ok(Errno::Success) } diff --git a/lib/wasi/src/syscalls/wasix/thread_spawn.rs b/lib/wasi/src/syscalls/wasix/thread_spawn.rs index 144dc9013c7..857a88e41c4 100644 --- a/lib/wasi/src/syscalls/wasix/thread_spawn.rs +++ b/lib/wasi/src/syscalls/wasix/thread_spawn.rs @@ -1,7 +1,10 @@ use super::*; -use crate::syscalls::*; +use crate::{ + os::task::thread::WasiMemoryLayout, runtime::task_manager::TaskResumeAction, syscalls::*, + WasiThreadHandle, +}; -use wasmer::vm::VMMemory; +use wasmer::{vm::VMMemory, MemoryType}; use wasmer_wasix_types::wasi::ThreadStart; /// ### `thread_spawn()` @@ -22,7 +25,7 @@ use wasmer_wasix_types::wasi::ThreadStart; /// /// 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)] +#[instrument(level = "debug", skip_all, fields(user_data, reactor, tid = field::Empty), ret)] pub fn thread_spawn( mut ctx: FunctionEnvMut<'_, WasiEnv>, start_ptr: WasmPtr, M>, @@ -33,20 +36,33 @@ pub fn thread_spawn( let memory = env.memory_view(&ctx); let runtime = env.runtime.clone(); let tasks = env.tasks().clone(); + let start_ptr_offset = start_ptr.offset(); // 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 stack_base = stack_start - stack_size; + let layout = { + let start = wasi_try_mem!(start_ptr.read(&memory)); + let stack_upper: u64 = wasi_try!(start.stack_upper.try_into().map_err(|_| Errno::Overflow)); + let stack_size: u64 = wasi_try!(start.stack_size.try_into().map_err(|_| Errno::Overflow)); + let guard_size: u64 = wasi_try!(start.guard_size.try_into().map_err(|_| Errno::Overflow)); + let tls_base: u64 = wasi_try!(start.tls_base.try_into().map_err(|_| Errno::Overflow)); + let stack_lower = stack_upper - stack_size; + + tracing::trace!(%stack_upper, %stack_lower, %stack_size, %guard_size, %tls_base); + + WasiMemoryLayout { + stack_upper, + stack_lower, + guard_size, + stack_size, + } + }; // Create the handle that represents this thread let mut thread_handle = match env.process.new_thread() { - Ok(h) => h, + Ok(h) => Arc::new(h), Err(err) => { error!( - %stack_base, - caller_id = current_caller_id().raw(), + stack_base = layout.stack_lower, "failed to create thread handle", ); // TODO: evaluate the appropriate error code, document it in the spec. @@ -66,57 +82,89 @@ pub fn thread_spawn( let mut store = ctx.data().runtime.new_store(); - // This function takes in memory and a store and creates a context that - // can be used to call back into the process - let create_ctx = { - let state = env.state.clone(); - let wasi_env = env.duplicate(); - let thread = thread_handle.as_thread(); - move |mut store: Store, module: Module, memory: VMMemory| { - // We need to reconstruct some things - let module = module; - let memory = Memory::new_from_existing(&mut store, memory); - - // Build the context object and import the memory - let mut ctx = WasiFunctionEnv::new(&mut store, wasi_env.duplicate()); - { - let env = ctx.data_mut(&mut store); - env.thread = thread.clone(); - env.stack_end = stack_base; - env.stack_start = stack_start; - } + // We capture some local variables + let state = env.state.clone(); + let mut wasi_env = env.duplicate(); + wasi_env.thread = thread_handle.as_thread(); + wasi_env.layout = layout; - let (mut import_object, init) = - import_object_for_all_wasi_versions(&module, &mut store, &ctx.env); - import_object.define("env", "memory", memory.clone()); + // If the environment is capable of doing it then we support deep sleeping + wasi_env.enable_deep_sleep = env.capable_of_deep_sleep(); + tracing::trace!(enable_deep_sleep = wasi_env.enable_deep_sleep); - let instance = match Instance::new(&mut store, &module, &import_object) { - Ok(a) => a, + // This next function gets a context for the local thread and then + // calls into the process + let mut execute_module = { + let state = env.state.clone(); + let tasks = tasks.clone(); + let wasi_env = wasi_env.duplicate(); + let thread_handle = thread_handle.clone(); + move |mut store: Store, module: Module, mut memory: Option| { + // Now create the context and hook it up + let ctx = match create_ctx(&mut store, &module, memory, wasi_env) { + Ok(c) => c, Err(err) => { - error!("failed - create instance failed: {}", err); - return Err(Errno::Noexec as u32); + return err as u32; } }; - init(&instance, &store).unwrap(); - - // Set the current thread ID - ctx.data_mut(&mut store).inner = - Some(WasiInstanceHandles::new(memory, &store, instance)); - Ok(WasiThreadContext { + // Call the thread + call_module::( ctx, - store: RefCell::new(store), - }) + store, + module, + tasks, + start_ptr_offset, + thread_memory_ty, + thread_handle, + None, + ) } }; + // If the process does not export a thread spawn function then obviously + // 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; + } + let spawn_type = crate::runtime::SpawnType::NewThread(thread_memory, thread_memory_ty); + + // Write the thread ID to the return value + let memory = ctx.data().memory_view(&ctx); + wasi_try_mem!(ret_tid.write(&memory, thread_id)); + + // Now spawn a thread + trace!("threading: spawning background thread"); + let thread_module = env.inner().instance.module().clone(); + let task = move |store, thread_module, thread_memory| { + execute_module(store, thread_module, thread_memory); + }; + wasi_try!(tasks + .task_wasm(Box::new(task), store, thread_module, spawn_type) + .map_err(|err| { Into::::into(err) })); + + // Success + Errno::Success +} + +/// Calls the module +fn call_module( + env: WasiFunctionEnv, + mut store: Store, + module: Module, + tasks: Arc, + start_ptr_offset: M::Offset, + thread_memory_ty: MemoryType, + thread_handle: Arc, + rewind_state: Option, +) -> u32 { // This function calls into the module - let start_ptr_offset = start_ptr.offset(); - let call_module = move |ctx: &WasiFunctionEnv, store: &mut Store| { + let call_module_internal = move |env: &WasiFunctionEnv, store: &mut Store| { // We either call the reactor callback or the thread spawn callback //trace!("threading: invoking thread callback (reactor={})", reactor); - let spawn = ctx.data(&store).inner().thread_spawn.clone().unwrap(); - let tid = ctx.data(&store).tid(); + let spawn = env.data(&store).inner().thread_spawn.clone().unwrap(); + let tid = env.data(&store).tid(); let call_ret = spawn.call( store, tid.raw().try_into().map_err(|_| Errno::Overflow).unwrap(), @@ -135,6 +183,10 @@ pub fn thread_spawn( Errno::Noexec }; } + Ok(WasiError::DeepSleep(deep)) => { + trace!("entered a deep sleep"); + return Err(deep); + } Ok(WasiError::UnknownWasiVersion) => { debug!("failed as wasi version is unknown",); ret = Errno::Noexec; @@ -148,106 +200,96 @@ pub fn thread_spawn( trace!("callback finished (ret={})", ret); // Clean up the environment - ctx.cleanup(store, Some(ret.into())); + env.cleanup(store, Some(ret.into())); // Return the result - ret as u32 + Ok(ret as u32) }; - // This next function gets a context for the local thread and then - // calls into the process - let mut execute_module = { - let state = env.state.clone(); - move |store: &mut Option, module: Module, memory: &mut Option| { - // We capture the thread handle here, it is used to notify - // anyone that is interested when this thread has terminated - let _captured_handle = Box::new(&mut thread_handle); - - // Given that it is not safe to assume this delegate will run on the - // same thread we need to capture a simple process that will create - // context objects on demand and reuse them - let caller_id = current_caller_id(); - - // We loop because read locks are held while functions run which need - // to be relocked in the case of a miss hit. - loop { - let thread = { - let guard = state.threading.read().unwrap(); - guard.thread_ctx.get(&caller_id).cloned() - }; - if let Some(thread) = thread { - let mut store = thread.store.borrow_mut(); - let ret = call_module(&thread.ctx, store.deref_mut()); - - { - let mut guard = state.threading.write().unwrap(); - guard.thread_ctx.remove(&caller_id); - } - - return ret; + // If we need to rewind then do so + if let Some(rewind_state) = rewind_state { + let ctx = env.env.clone().into_mut(&mut store); + let res = rewind::( + ctx, + rewind_state.memory_stack.freeze(), + rewind_state.rewind_stack.freeze(), + rewind_state.store_data, + ); + if res != Errno::Success { + return res as u32; + } + } + + // Now invoke the module + let ret = call_module_internal(&env, &mut store); + + // If it went to deep sleep then we need to handle that + match ret { + Ok(ret) => ret, + Err(deep) => { + // Create the callback that will be invoked when the thread respawns after a deep sleep + let rewind = deep.rewind; + let respawn = { + let env = env.clone(); + let tasks = tasks.clone(); + move |store, module| { + // Call the thread + call_module::( + env, + store, + module, + tasks, + start_ptr_offset, + thread_memory_ty, + thread_handle, + Some(rewind), + ); } + }; - // Otherwise we need to create a new context under a write lock - debug!( - "encountered a new caller (ref={}) - creating WASM execution context...", - caller_id.raw() - ); - - // We can only create the context once per thread - let memory = match memory.take() { - Some(m) => m, - None => { - debug!("failed - memory can only be consumed once per context creation"); - return Errno::Noexec as u32; - } - }; - let store = match store.take() { - Some(s) => s, - None => { - debug!("failed - store can only be consumed once per context creation"); - return Errno::Noexec as u32; - } - }; - - // Now create the context and hook it up - let mut guard = state.threading.write().unwrap(); - let ctx = match create_ctx(store, module.clone(), memory) { - Ok(c) => c, - Err(err) => { - return err; - } - }; - guard.thread_ctx.insert(caller_id, Arc::new(ctx)); - } + /// Spawns the WASM process after a trigger + tasks.resume_wasm_after_poller(Box::new(respawn), store, module, env, deep.work); + Errno::Unknown as u32 } - }; - - // If the process does not export a thread spawn function then obviously - // 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; } +} - let spawn_type = crate::runtime::SpawnType::NewThread(thread_memory, thread_memory_ty); +// This function takes in memory and a store and creates a context that +// can be used to call back into the process +fn create_ctx( + store: &mut Store, + module: &Module, + mut memory: Option, + wasi_env: WasiEnv, +) -> Result { + // Otherwise we need to create a new context under a write lock + debug!("encountered a new caller - creating WASM execution context..."); - // Now spawn a thread - trace!("threading: spawning background thread"); - let thread_module = env.inner().instance.module().clone(); - let tasks2 = tasks.clone(); + let memory = match memory.take() { + Some(m) => m, + None => { + debug!("failed - memory can only be consumed once per context creation"); + return Err(Errno::Noexec); + } + }; + let memory = Memory::new_from_existing(store, memory); + + // Build the context object and import the memory + let mut ctx = WasiFunctionEnv::new(store, wasi_env); + let (mut import_object, init) = import_object_for_all_wasi_versions(module, store, &ctx.env); + import_object.define("env", "memory", memory.clone()); - let task = move |store, thread_module, mut thread_memory| { - // FIXME: should not use unwrap() here! (initializiation refactor) - let mut store = Some(store); - execute_module(&mut store, thread_module, &mut thread_memory); + let instance = match Instance::new(store, module, &import_object) { + Ok(a) => a, + Err(err) => { + error!("failed - create instance failed: {}", err); + return Err(Errno::Noexec); + } }; - wasi_try!(tasks - .task_wasm(Box::new(task), store, thread_module, spawn_type) - .map_err(|err| { Into::::into(err) })); + init(&instance, &store).unwrap(); - // Success - let memory = ctx.data().memory_view(&ctx); - wasi_try_mem!(ret_tid.write(&memory, thread_id)); - Errno::Success + // Set the current thread ID + ctx.data_mut(store).inner = Some(WasiInstanceHandles::new(memory, &store, instance)); + Ok(ctx) } From beceafe36c9fe0453203fd8057d24bbdc31b2fbf Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Tue, 21 Mar 2023 21:26:17 +1100 Subject: [PATCH 02/52] cargo clippy --fix --- lib/wasi/src/fs/inode_guard.rs | 4 ++-- lib/wasi/src/syscalls/mod.rs | 10 +++++----- lib/wasi/src/syscalls/wasix/proc_exec.rs | 4 ++-- lib/wasi/src/syscalls/wasix/proc_join.rs | 12 ++++++------ lib/wasi/src/syscalls/wasix/thread_spawn.rs | 2 +- 5 files changed, 16 insertions(+), 16 deletions(-) diff --git a/lib/wasi/src/fs/inode_guard.rs b/lib/wasi/src/fs/inode_guard.rs index d6c4d64c264..56a30d19d86 100644 --- a/lib/wasi/src/fs/inode_guard.rs +++ b/lib/wasi/src/fs/inode_guard.rs @@ -248,7 +248,7 @@ impl Future for InodeValFilePollGuardJoin { } } Poll::Ready(Ok(amt)) => { - if guard.notifications.closed == true { + if guard.notifications.closed { Poll::Pending } else { Poll::Ready(Ok(amt)) @@ -341,7 +341,7 @@ impl Future for InodeValFilePollGuardJoin { } } Poll::Ready(Ok(amt)) => { - if guard.notifications.closed == true { + if guard.notifications.closed { Poll::Pending } else { Poll::Ready(Ok(amt)) diff --git a/lib/wasi/src/syscalls/mod.rs b/lib/wasi/src/syscalls/mod.rs index eb76a92b2fd..54e9a2a9574 100644 --- a/lib/wasi/src/syscalls/mod.rs +++ b/lib/wasi/src/syscalls/mod.rs @@ -429,7 +429,7 @@ where _ = deep_sleep_wait => { tracing::trace!("thread entering deep sleep"); return deep_sleep::(ctx, work) - .map(|err| Err(err)); + .map(Err); }, } }; @@ -973,9 +973,9 @@ where // Perform the unwind action let tasks = ctx.data().tasks().clone(); - return unwind::(ctx, move |_ctx, memory_stack, rewind_stack| { + unwind::(ctx, move |_ctx, memory_stack, rewind_stack| { // Schedule the process on the stack so that it can be resumed - return OnCalledAction::Trap(Box::new(RuntimeError::user(Box::new( + OnCalledAction::Trap(Box::new(RuntimeError::user(Box::new( WasiError::DeepSleep(DeepSleepWork { work: Box::new(work), rewind: RewindState { @@ -985,8 +985,8 @@ where is_64bit: M::is_64bit(), }, }), - )))); - }); + )))) + }) } #[must_use = "you must return the result immediately so the stack can unwind"] diff --git a/lib/wasi/src/syscalls/wasix/proc_exec.rs b/lib/wasi/src/syscalls/wasix/proc_exec.rs index 0d3e3cc5b2d..947505eeb5e 100644 --- a/lib/wasi/src/syscalls/wasix/proc_exec.rs +++ b/lib/wasi/src/syscalls/wasix/proc_exec.rs @@ -255,14 +255,14 @@ pub fn proc_exec( env.thread.set_status_finished(Ok(exit_code)); }, )?; - return match res { + match res { AsyncifyAction::Finish(mut ctx) => { // When we arrive here the process should already be terminated WasiEnv::process_signals_and_exit(&mut ctx)?; Err(WasiError::Exit(Errno::Unknown.into())) } AsyncifyAction::Unwind => Ok(()), - }; + } } Ok(Err(err)) => { warn!( diff --git a/lib/wasi/src/syscalls/wasix/proc_join.rs b/lib/wasi/src/syscalls/wasix/proc_join.rs index 7bb1d91e916..38a0278957e 100644 --- a/lib/wasi/src/syscalls/wasix/proc_join.rs +++ b/lib/wasi/src/syscalls/wasix/proc_join.rs @@ -21,7 +21,7 @@ pub fn proc_join( // This lambda will look at what we wrote in the status variable // and use this to determine the return code sent back to the caller let ret_result = { - let status_ptr = status_ptr.clone(); + let status_ptr = status_ptr; move |ctx: FunctionEnvMut<'_, WasiEnv>| { let view = ctx.data().memory_view(&ctx); let status = wasi_try_mem_ok!(status_ptr.read(&view)); @@ -36,9 +36,9 @@ pub fn proc_join( }, } )); - return Ok(ret); + Ok(ret) } else { - return Ok(Errno::Success); + Ok(Errno::Success) } } }; @@ -80,8 +80,8 @@ pub fn proc_join( let pid = match option_pid { None => { let mut process = ctx.data_mut().process.clone(); - let pid_ptr = pid_ptr.clone(); - let status_ptr = status_ptr.clone(); + let pid_ptr = pid_ptr; + let status_ptr = status_ptr; // We wait for any process to exit (if it takes too long // then we go into a deep sleep) @@ -210,5 +210,5 @@ pub fn proc_join( }, }; wasi_try_mem_ok!(status_ptr.write(&memory, status)); - return ret_result(ctx); + ret_result(ctx) } diff --git a/lib/wasi/src/syscalls/wasix/thread_spawn.rs b/lib/wasi/src/syscalls/wasix/thread_spawn.rs index 857a88e41c4..473dee0c7cc 100644 --- a/lib/wasi/src/syscalls/wasix/thread_spawn.rs +++ b/lib/wasi/src/syscalls/wasix/thread_spawn.rs @@ -98,7 +98,7 @@ pub fn thread_spawn( let state = env.state.clone(); let tasks = tasks.clone(); let wasi_env = wasi_env.duplicate(); - let thread_handle = thread_handle.clone(); + let thread_handle = thread_handle; move |mut store: Store, module: Module, mut memory: Option| { // Now create the context and hook it up let ctx = match create_ctx(&mut store, &module, memory, wasi_env) { From 4958ad4af9b74965d05511da6e681b3700bc93d1 Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Tue, 21 Mar 2023 21:33:46 +1100 Subject: [PATCH 03/52] More clippy fixes --- lib/wasi/src/runtime/task_manager/mod.rs | 2 +- lib/wasi/src/state/env.rs | 2 +- lib/wasi/src/syscalls/mod.rs | 7 ++++--- lib/wasi/src/syscalls/wasix/proc_fork.rs | 2 +- lib/wasi/src/syscalls/wasix/thread_spawn.rs | 2 +- 5 files changed, 8 insertions(+), 7 deletions(-) diff --git a/lib/wasi/src/runtime/task_manager/mod.rs b/lib/wasi/src/runtime/task_manager/mod.rs index 5997c702d69..0c0d0c4d18c 100644 --- a/lib/wasi/src/runtime/task_manager/mod.rs +++ b/lib/wasi/src/runtime/task_manager/mod.rs @@ -91,7 +91,7 @@ pub trait VirtualTaskManager: std::fmt::Debug + Send + Sync + 'static { task: Box, store: Store, module: Module, - trigger: Box< + #[allow(clippy::all)] trigger: Box< dyn FnOnce(Store) -> Pin + Send + 'static>> + Send + 'static, diff --git a/lib/wasi/src/state/env.rs b/lib/wasi/src/state/env.rs index 4b92d0a4fb7..f59180e942f 100644 --- a/lib/wasi/src/state/env.rs +++ b/lib/wasi/src/state/env.rs @@ -912,7 +912,7 @@ impl WasiEnv { /// Cleans up all the open files (if this is the main thread) #[allow(clippy::await_holding_lock)] - pub fn blocking_cleanup<'a>(&self, exit_code: Option) { + pub fn blocking_cleanup(&self, exit_code: Option) { __asyncify_light(self, None, async { self.cleanup(exit_code).await; Ok(()) diff --git a/lib/wasi/src/syscalls/mod.rs b/lib/wasi/src/syscalls/mod.rs index 54e9a2a9574..4c5de8dcf4e 100644 --- a/lib/wasi/src/syscalls/mod.rs +++ b/lib/wasi/src/syscalls/mod.rs @@ -392,7 +392,7 @@ pub enum AsyncifyAction<'a> { /// or it will return an WasiError which will exit the WASM call using asyncify /// and instead process it on a shared task /// -pub(crate) fn __asyncify_with_deep_sleep<'a, M: MemorySize, Fut>( +pub(crate) fn __asyncify_with_deep_sleep( ctx: FunctionEnvMut<'_, WasiEnv>, timeout: Option, mut work: Fut, @@ -454,7 +454,7 @@ where /// or it will return an WasiError which will exit the WASM call using asyncify /// and instead process it on a shared task /// -pub(crate) fn __asyncify_with_deep_sleep_ext<'a, M: MemorySize, T, Fut, After>( +pub(crate) fn __asyncify_with_deep_sleep_ext( ctx: FunctionEnvMut<'_, WasiEnv>, timeout: Option, work: Fut, @@ -467,6 +467,7 @@ where { struct Poller { work: Pin + Send + Sync + 'static>>, + #[allow(clippy::all)] after: Option>, } impl AsyncifyFuture for Poller { @@ -503,7 +504,7 @@ where /// thus allowed for asynchronous operations to execute. It has built in functionality /// to (optionally) timeout the IO, force exit the process, callback signals and pump /// synchronous IO engine -pub(crate) fn __asyncify_light<'a, T, Fut>( +pub(crate) fn __asyncify_light( env: &WasiEnv, timeout: Option, work: Fut, diff --git a/lib/wasi/src/syscalls/wasix/proc_fork.rs b/lib/wasi/src/syscalls/wasix/proc_fork.rs index d31c57b8095..9aaaa8fd0b8 100644 --- a/lib/wasi/src/syscalls/wasix/proc_fork.rs +++ b/lib/wasi/src/syscalls/wasix/proc_fork.rs @@ -288,7 +288,7 @@ fn run( mut store: Store, module: Module, tasks: Arc, - fork_memory_ty: MemoryType, + #[allow(clippy::all)] fork_memory_ty: MemoryType, rewind_state: Option, ) -> ExitCode { // If we need to rewind then do so diff --git a/lib/wasi/src/syscalls/wasix/thread_spawn.rs b/lib/wasi/src/syscalls/wasix/thread_spawn.rs index 473dee0c7cc..5bd12c3df18 100644 --- a/lib/wasi/src/syscalls/wasix/thread_spawn.rs +++ b/lib/wasi/src/syscalls/wasix/thread_spawn.rs @@ -155,7 +155,7 @@ fn call_module( module: Module, tasks: Arc, start_ptr_offset: M::Offset, - thread_memory_ty: MemoryType, + #[allow(clippy::all)] thread_memory_ty: MemoryType, thread_handle: Arc, rewind_state: Option, ) -> u32 { From 69bb9ff9be6baa937fbd558b6ceaa802a40c918b Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Wed, 22 Mar 2023 14:09:53 +1100 Subject: [PATCH 04/52] Fixed the linting errors --- lib/wasi/src/runtime/task_manager/mod.rs | 10 +++++----- lib/wasi/src/runtime/task_manager/tokio.rs | 8 ++------ lib/wasi/src/syscalls/mod.rs | 6 ++++-- lib/wasi/src/syscalls/wasix/proc_fork.rs | 5 ++--- lib/wasi/src/syscalls/wasix/thread_spawn.rs | 8 ++++++-- 5 files changed, 19 insertions(+), 18 deletions(-) diff --git a/lib/wasi/src/runtime/task_manager/mod.rs b/lib/wasi/src/runtime/task_manager/mod.rs index 0c0d0c4d18c..44a4942101b 100644 --- a/lib/wasi/src/runtime/task_manager/mod.rs +++ b/lib/wasi/src/runtime/task_manager/mod.rs @@ -43,6 +43,10 @@ pub enum TaskResumeAction { Abort, } +pub type WasmResumeTrigger = dyn FnOnce(Store) -> Pin + Send + 'static>> + + Send + + Sync; + /// An implementation of task management #[async_trait::async_trait] #[allow(unused_variables)] @@ -91,11 +95,7 @@ pub trait VirtualTaskManager: std::fmt::Debug + Send + Sync + 'static { task: Box, store: Store, module: Module, - #[allow(clippy::all)] trigger: Box< - dyn FnOnce(Store) -> Pin + Send + 'static>> - + Send - + 'static, - >, + trigger: Box, ) -> Result<(), WasiThreadError>; /// Starts an asynchronous task will will run on a dedicated thread diff --git a/lib/wasi/src/runtime/task_manager/tokio.rs b/lib/wasi/src/runtime/task_manager/tokio.rs index 6364e2506c7..ddaf85dd3c2 100644 --- a/lib/wasi/src/runtime/task_manager/tokio.rs +++ b/lib/wasi/src/runtime/task_manager/tokio.rs @@ -13,7 +13,7 @@ use wasmer::{ use crate::os::task::thread::WasiThreadError; -use super::{SpawnType, TaskResumeAction, VirtualTaskManager}; +use super::{SpawnType, TaskResumeAction, VirtualTaskManager, WasmResumeTrigger}; /// A task manager that uses tokio to spawn tasks. #[derive(Clone, Debug)] @@ -152,11 +152,7 @@ impl VirtualTaskManager for TokioTaskManager { task: Box, store: Store, module: Module, - trigger: Box< - dyn FnOnce(Store) -> Pin + Send + 'static>> - + Send - + 'static, - >, + trigger: Box, ) -> Result<(), WasiThreadError> { let trigger = trigger(store); let handle = self.0.clone(); diff --git a/lib/wasi/src/syscalls/mod.rs b/lib/wasi/src/syscalls/mod.rs index 4c5de8dcf4e..0ad78071288 100644 --- a/lib/wasi/src/syscalls/mod.rs +++ b/lib/wasi/src/syscalls/mod.rs @@ -445,6 +445,9 @@ where }) } +pub(crate) type AsyncifyWorkAfter = + dyn FnOnce(T, &WasiEnv, &dyn AsStoreRef) + Send + Sync + 'static; + /// Asyncify takes the current thread and blocks on the async runtime associated with it /// thus allowed for asynchronous operations to execute. It has built in functionality /// to (optionally) timeout the IO, force exit the process, callback signals and pump @@ -467,8 +470,7 @@ where { struct Poller { work: Pin + Send + Sync + 'static>>, - #[allow(clippy::all)] - after: Option>, + after: Option>>, } impl AsyncifyFuture for Poller { type Output = Result<(), Errno>; diff --git a/lib/wasi/src/syscalls/wasix/proc_fork.rs b/lib/wasi/src/syscalls/wasix/proc_fork.rs index 9aaaa8fd0b8..bf8e5507214 100644 --- a/lib/wasi/src/syscalls/wasix/proc_fork.rs +++ b/lib/wasi/src/syscalls/wasix/proc_fork.rs @@ -218,7 +218,7 @@ pub fn proc_fork( } // Invoke the start function - let ret = run::(ctx, store, module, tasks, fork_memory_ty, None); + let ret = run::(ctx, store, module, tasks, None); trace!(%pid, %tid, "child exited (code = {})", ret); // Send the result @@ -288,7 +288,6 @@ fn run( mut store: Store, module: Module, tasks: Arc, - #[allow(clippy::all)] fork_memory_ty: MemoryType, rewind_state: Option, ) -> ExitCode { // If we need to rewind then do so @@ -338,7 +337,7 @@ fn run( let tasks = tasks.clone(); let rewind_state = Some(deep.rewind); move |store, module| { - run::(ctx, store, module, tasks, fork_memory_ty, rewind_state); + run::(ctx, store, module, tasks, rewind_state); } }; diff --git a/lib/wasi/src/syscalls/wasix/thread_spawn.rs b/lib/wasi/src/syscalls/wasix/thread_spawn.rs index 5bd12c3df18..123b7c01675 100644 --- a/lib/wasi/src/syscalls/wasix/thread_spawn.rs +++ b/lib/wasi/src/syscalls/wasix/thread_spawn.rs @@ -155,7 +155,7 @@ fn call_module( module: Module, tasks: Arc, start_ptr_offset: M::Offset, - #[allow(clippy::all)] thread_memory_ty: MemoryType, + thread_memory_ty: MemoryType, thread_handle: Arc, rewind_state: Option, ) -> u32 { @@ -225,7 +225,11 @@ fn call_module( // If it went to deep sleep then we need to handle that match ret { - Ok(ret) => ret, + Ok(ret) => { + // Frees the handle so that it closes + drop(thread_handle); + ret + } Err(deep) => { // Create the callback that will be invoked when the thread respawns after a deep sleep let rewind = deep.rewind; From 8dbdac2fdffc3a8154ab3edebc46ad16a1adbec2 Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Wed, 22 Mar 2023 14:19:19 +1100 Subject: [PATCH 05/52] Fixed some more clippy linting issues --- lib/wasi/src/syscalls/wasix/thread_spawn.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/lib/wasi/src/syscalls/wasix/thread_spawn.rs b/lib/wasi/src/syscalls/wasix/thread_spawn.rs index 123b7c01675..70b46107d80 100644 --- a/lib/wasi/src/syscalls/wasix/thread_spawn.rs +++ b/lib/wasi/src/syscalls/wasix/thread_spawn.rs @@ -115,7 +115,6 @@ pub fn thread_spawn( module, tasks, start_ptr_offset, - thread_memory_ty, thread_handle, None, ) @@ -155,7 +154,6 @@ fn call_module( module: Module, tasks: Arc, start_ptr_offset: M::Offset, - thread_memory_ty: MemoryType, thread_handle: Arc, rewind_state: Option, ) -> u32 { @@ -244,7 +242,6 @@ fn call_module( module, tasks, start_ptr_offset, - thread_memory_ty, thread_handle, Some(rewind), ); From 0eaed79d1f8ecb3cf7bfa58fe3abe5621e32420e Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Wed, 22 Mar 2023 15:58:07 +1100 Subject: [PATCH 06/52] Added an opt-out on asynchronous threading --- lib/wasi/src/capabilities.rs | 4 ++++ lib/wasi/src/os/task/control_plane.rs | 10 ++++++++++ lib/wasi/src/state/builder.rs | 1 + lib/wasi/src/state/env.rs | 3 +++ 4 files changed, 18 insertions(+) diff --git a/lib/wasi/src/capabilities.rs b/lib/wasi/src/capabilities.rs index bb4b9127c16..e031edcf848 100644 --- a/lib/wasi/src/capabilities.rs +++ b/lib/wasi/src/capabilities.rs @@ -31,4 +31,8 @@ pub struct CapabilityThreadingV1 { /// /// [`None`] means no limit. pub max_threads: Option, + + /// Flag that indicates if asynchronous threading is disabled + /// (default = false) + pub disable_asynchronous_threading: bool, } diff --git a/lib/wasi/src/os/task/control_plane.rs b/lib/wasi/src/os/task/control_plane.rs index 19fa8e87fbf..76acabba09a 100644 --- a/lib/wasi/src/os/task/control_plane.rs +++ b/lib/wasi/src/os/task/control_plane.rs @@ -39,12 +39,15 @@ impl WasiControlPlaneHandle { pub struct ControlPlaneConfig { /// Total number of tasks (processes + threads) that can be spawned. pub max_task_count: Option, + /// Flag that indicates if asynchronous threading is disable (opt-out) + pub disable_asynchronous_threading: bool, } impl ControlPlaneConfig { pub fn new() -> Self { Self { max_task_count: None, + disable_asynchronous_threading: false, } } } @@ -98,6 +101,11 @@ impl WasiControlPlane { self.state.task_count.load(Ordering::SeqCst) } + /// Returns the configuration for this control plane + pub(crate) fn config(&self) -> &ControlPlaneConfig { + &self.state.config + } + /// Register a new task. /// // Currently just increments the task counter. @@ -202,6 +210,7 @@ mod tests { fn test_control_plane_task_limits() { let p = WasiControlPlane::new(ControlPlaneConfig { max_task_count: Some(2), + disable_asynchronous_threading: false, }); let p1 = p.new_process().unwrap(); @@ -219,6 +228,7 @@ mod tests { fn test_control_plane_task_limits_with_dropped_threads() { let p = WasiControlPlane::new(ControlPlaneConfig { max_task_count: Some(2), + disable_asynchronous_threading: false, }); let p1 = p.new_process().unwrap(); diff --git a/lib/wasi/src/state/builder.rs b/lib/wasi/src/state/builder.rs index 1f7093e0062..a25f9459196 100644 --- a/lib/wasi/src/state/builder.rs +++ b/lib/wasi/src/state/builder.rs @@ -767,6 +767,7 @@ impl WasiEnvBuilder { let plane_config = ControlPlaneConfig { max_task_count: capabilities.threading.max_threads, + disable_asynchronous_threading: capabilities.threading.disable_asynchronous_threading, }; let control_plane = WasiControlPlane::new(plane_config); diff --git a/lib/wasi/src/state/env.rs b/lib/wasi/src/state/env.rs index f59180e942f..a560a75088a 100644 --- a/lib/wasi/src/state/env.rs +++ b/lib/wasi/src/state/env.rs @@ -379,6 +379,9 @@ impl WasiEnv { /// Returns true if this module is capable of deep sleep /// (needs asyncify to unwind and rewin) pub fn capable_of_deep_sleep(&self) -> bool { + if self.control_plane.config().disable_asynchronous_threading { + return false; + } let inner = self.inner(); inner.asyncify_get_state.is_some() && inner.asyncify_start_rewind.is_some() From 65a850cf04cce05b983b1b6e49691dc62353a08c Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Wed, 22 Mar 2023 16:06:43 +1100 Subject: [PATCH 07/52] Updated the version of spin used for heapless which had been yanked --- Cargo.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 958ce2c2cf1..499f1d6de0f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1661,7 +1661,7 @@ dependencies = [ "atomic-polyfill", "hash32", "rustc_version 0.4.0", - "spin 0.9.6", + "spin 0.9.7", "stable_deref_trait", ] @@ -3902,9 +3902,9 @@ checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" [[package]] name = "spin" -version = "0.9.6" +version = "0.9.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5d6e0250b93c8427a177b849d144a96d5acc57006149479403d7861ab721e34" +checksum = "c0959fd6f767df20b231736396e4f602171e00d95205676286e79d4a4eb67bef" dependencies = [ "lock_api", ] From ccd852bf2c64f9603bea10bc58b540831d5dd1f6 Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Thu, 23 Mar 2023 09:47:01 +1100 Subject: [PATCH 08/52] Refactored parts of asynchronous threading to fix snapshot tests --- lib/cli/src/commands/run.rs | 78 +++++--- lib/cli/src/commands/run/wasi.rs | 171 +++++++++++++++-- lib/wasi/src/fs/mod.rs | 6 +- lib/wasi/src/lib.rs | 4 +- lib/wasi/src/runtime/task_manager/mod.rs | 34 ++-- lib/wasi/src/syscalls/legacy/snapshot0.rs | 4 +- lib/wasi/src/syscalls/mod.rs | 203 ++++++++++++++------ lib/wasi/src/syscalls/wasi/poll_oneoff.rs | 25 +-- lib/wasi/src/syscalls/wasix/futex_wait.rs | 29 ++- lib/wasi/src/syscalls/wasix/proc_exec.rs | 4 +- lib/wasi/src/syscalls/wasix/proc_fork.rs | 31 +-- lib/wasi/src/syscalls/wasix/proc_join.rs | 31 ++- lib/wasi/src/syscalls/wasix/thread_join.rs | 2 +- lib/wasi/src/syscalls/wasix/thread_sleep.rs | 5 +- 14 files changed, 464 insertions(+), 163 deletions(-) diff --git a/lib/cli/src/commands/run.rs b/lib/cli/src/commands/run.rs index 7a795ef86ef..d9639645c52 100644 --- a/lib/cli/src/commands/run.rs +++ b/lib/cli/src/commands/run.rs @@ -8,7 +8,7 @@ use anyhow::{anyhow, Context, Result}; use clap::Parser; use std::io::Write; use std::ops::Deref; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use std::str::FromStr; use std::{fs::File, net::SocketAddr}; use wasmer::FunctionEnv; @@ -180,32 +180,49 @@ impl RunWithPathBuf { } } - fn inner_module_run(&self, store: &mut Store, instance: Instance) -> Result { + fn inner_module_init(&self, store: &mut Store, instance: &Instance) -> Result<()> { // If this module exports an _initialize function, run that first. if let Ok(initialize) = instance.exports.get_function("_initialize") { initialize .call(store, &[]) .with_context(|| "failed to run _initialize function")?; } + Ok(()) + } + + fn inner_module_invoke_function( + store: &mut Store, + instance: Instance, + path: &Path, + invoke: &str, + args: &Vec, + ) -> Result<()> { + let result = Self::invoke_function(store, &instance, path, invoke, args)?; + println!( + "{}", + result + .iter() + .map(|val| val.to_string()) + .collect::>() + .join(" ") + ); + Ok(()) + } + fn inner_module_run(&self, store: &mut Store, instance: Instance) -> Result { // Do we want to invoke a function? if let Some(ref invoke) = self.invoke { - let result = self.invoke_function(store, &instance, invoke, &self.args)?; - println!( - "{}", - result - .iter() - .map(|val| val.to_string()) - .collect::>() - .join(" ") - ); + Self::inner_module_invoke_function( + store, + instance, + self.path.as_path(), + invoke, + &self.args, + )?; } else { - let start: Function = self.try_find_function(&instance, "_start", &[])?; - let result = start.call(store, &[]); - #[cfg(feature = "wasi")] - return self.wasi.handle_result(result); - #[cfg(not(feature = "wasi"))] - return Ok(result?); + let start: Function = + Self::try_find_function(&instance, self.path.as_path(), "_start", &[])?; + start.call(store, &[])?; } Ok(0) @@ -311,19 +328,22 @@ impl RunWithPathBuf { .map(|f| f.to_string_lossy().to_string()) }) .unwrap_or_default(); - let (ctx, instance) = self + let (mut ctx, instance) = self .wasi .instantiate(&mut store, &module, program_name, self.args.clone()) .with_context(|| "failed to instantiate WASI module")?; - let res = self.inner_module_run(&mut store, instance); - ctx.cleanup(&mut store, None); + let capable_of_deep_sleep = ctx.data(&store).capable_of_deep_sleep(); + ctx.data_mut(&mut store) + .enable_deep_sleep = capable_of_deep_sleep; - res + self.inner_module_init(&mut store, &instance)?; + Wasi::run(ctx, store, instance, self.path.clone(), self.invoke.clone(), self.args.clone()) } // not WASI _ => { let instance = Instance::new(&mut store, &module, &imports! {})?; + self.inner_module_init(&mut store, &instance)?; self.inner_module_run(&mut store, instance) } } @@ -346,7 +366,8 @@ impl RunWithPathBuf { // Do we want to invoke a function? if let Some(ref invoke) = self.invoke { - let result = self.invoke_function(&instance, invoke, &self.args)?; + let result = + Self::invoke_function(&instance, self.path.as_path(), invoke, &self.args)?; println!( "{}", result @@ -356,7 +377,8 @@ impl RunWithPathBuf { .join(" ") ); } else { - let start: Function = self.try_find_function(&instance, "_start", &[])?; + let start: Function = + Self.try_find_function(&instance, self.path.as_path(), "_start", &[])?; let result = start.call(&[]); #[cfg(feature = "wasi")] self.wasi.handle_result(result)?; @@ -503,8 +525,8 @@ impl RunWithPathBuf { } fn try_find_function( - &self, instance: &Instance, + path: &Path, name: &str, args: &[String], ) -> Result { @@ -524,7 +546,7 @@ impl RunWithPathBuf { .join(", "); let suggested_command = format!( "wasmer {} -i {} {}", - self.path.display(), + path.display(), suggested_functions.get(0).unwrap_or(&String::new()), args.join(" ") ); @@ -552,13 +574,13 @@ impl RunWithPathBuf { } fn invoke_function( - &self, ctx: &mut impl AsStoreMut, instance: &Instance, + path: &Path, invoke: &str, args: &[String], ) -> Result> { - let func: Function = self.try_find_function(instance, invoke, args)?; + let func: Function = Self::try_find_function(instance, path, invoke, args)?; let func_ty = func.ty(ctx); let required_arguments = func_ty.params().len(); let provided_arguments = args.len(); @@ -567,7 +589,7 @@ impl RunWithPathBuf { "Function expected {} arguments, but received {}: \"{}\"", required_arguments, provided_arguments, - self.args.join(" ") + args.join(" ") ); } let invoke_args = args diff --git a/lib/cli/src/commands/run/wasi.rs b/lib/cli/src/commands/run/wasi.rs index 444c62a94bc..02d331bec40 100644 --- a/lib/cli/src/commands/run/wasi.rs +++ b/lib/cli/src/commands/run/wasi.rs @@ -3,21 +3,28 @@ use anyhow::Result; use std::{ collections::{BTreeSet, HashMap}, path::{Path, PathBuf}, - sync::Arc, + sync::{mpsc::Sender, Arc}, }; use virtual_fs::{DeviceFile, FileSystem, PassthruFileSystem, RootFileSystemBuilder}; -use wasmer::{AsStoreMut, Instance, Module, RuntimeError, Store, Value}; +use wasmer::{ + AsStoreMut, Function, Instance, Memory32, Memory64, Module, RuntimeError, Store, Value, +}; use wasmer_wasix::{ default_fs_backing, get_wasi_versions, os::{tty_sys::SysTty, TtyBridge}, + rewind, runners::MappedDirectory, runtime::task_manager::tokio::TokioTaskManager, types::__WASI_STDIN_FILENO, - PluggableRuntime, WasiEnv, WasiEnvBuilder, WasiError, WasiFunctionEnv, WasiVersion, + wasmer_wasix_types::wasi::Errno, + PluggableRuntime, RewindState, WasiEnv, WasiEnvBuilder, WasiError, WasiFunctionEnv, + WasiVersion, }; use clap::Parser; +use super::RunWithPathBuf; + #[derive(Debug, Parser, Clone, Default)] /// WASI Options pub struct Wasi { @@ -76,6 +83,10 @@ pub struct Wasi { #[clap(long = "no-tty")] pub no_tty: bool, + /// Disables asynchronous threading + #[clap(long = "no-async-threads")] + pub no_async_threads: bool, + /// Allow instances to send http requests. /// /// Access to domains is granted by default. @@ -201,6 +212,11 @@ impl Wasi { builder.capabilities_mut().http_client = caps; } + builder + .capabilities_mut() + .threading + .disable_asynchronous_threading = self.no_async_threads; + #[cfg(feature = "experimental-io-devices")] { if self.enable_experimental_io_devices { @@ -225,21 +241,150 @@ impl Wasi { Ok((wasi_env, instance)) } + // Runs the Wasi process + pub fn run( + ctx: WasiFunctionEnv, + store: Store, + instance: Instance, + path: PathBuf, + invoke: Option, + args: Vec, + ) -> Result { + let tasks = ctx.data(&store).tasks().clone(); + + // The return value is passed synchronously and will block until the result is returned + // this is because the main thread can go into a deep sleep and exit the dedicated thread + let (tx, rx) = std::sync::mpsc::channel(); + + // We run it in a blocking thread as the WASM function may otherwise hold + // up the IO operations + tasks.task_dedicated(Box::new(move || { + Self::run_with_deep_sleep(ctx, store, instance, path, invoke, args, tx, None); + }))?; + rx.recv() + .expect("main thread terminated without a result, this normally means a panic occurred within the main thread") + } + + // Runs the Wasi process (asynchronously) + pub fn run_with_deep_sleep( + ctx: WasiFunctionEnv, + mut store: Store, + instance: Instance, + path: PathBuf, + invoke: Option, + args: Vec, + tx: Sender>, + rewind_state: Option, + ) { + // If we need to rewind then do so + if let Some(rewind_state) = rewind_state { + let ctx = ctx.env.clone().into_mut(&mut store); + let res = if rewind_state.is_64bit { + rewind::( + ctx, + rewind_state.memory_stack.freeze(), + rewind_state.rewind_stack.freeze(), + rewind_state.store_data, + ) + } else { + rewind::( + ctx, + rewind_state.memory_stack.freeze(), + rewind_state.rewind_stack.freeze(), + rewind_state.store_data, + ) + }; + if res != Errno::Success { + tx.send(Ok(res as i32)).ok(); + return; + } + } + + // Do we want to invoke a function? + if let Some(ref invoke) = invoke { + let res = RunWithPathBuf::inner_module_invoke_function( + &mut store, + instance, + path.as_path(), + invoke, + &args, + ) + .map(|()| 0); + + ctx.cleanup(&mut store, None); + + tx.send(res).unwrap(); + } else { + let start: Function = + RunWithPathBuf::try_find_function(&instance, path.as_path(), "_start", &[]) + .unwrap(); + let result = start.call(&mut store, &[]); + Self::handle_result(ctx, store, instance, path, invoke, args, result, tx) + } + } + /// Helper function for handling the result of a Wasi _start function. - pub fn handle_result(&self, result: Result, RuntimeError>) -> Result { - match result { + pub fn handle_result( + ctx: WasiFunctionEnv, + mut store: Store, + instance: Instance, + path: PathBuf, + invoke: Option, + args: Vec, + result: Result, RuntimeError>, + tx: Sender>, + ) { + let ret: Result = match result { Ok(_) => Ok(0), Err(err) => { - let err: anyhow::Error = match err.downcast::() { - Ok(WasiError::Exit(exit_code)) => { - return Ok(exit_code.raw()); + match err.downcast::() { + Ok(WasiError::Exit(exit_code)) => Ok(exit_code.raw()), + Ok(WasiError::DeepSleep(deep)) => { + let pid = ctx.data(&store).pid(); + let tid = ctx.data(&store).tid(); + tracing::trace!(%pid, %tid, "entered a deep sleep"); + + // Create the respawn function + let module = instance.module().clone(); + let tasks = ctx.data(&store).tasks().clone(); + let rewind = deep.rewind; + let respawn = { + let ctx = ctx.clone(); + move |store, _module| { + Self::run_with_deep_sleep( + ctx, + store, + instance, + path, + invoke, + args, + tx, + Some(rewind), + ); + } + }; + + // Spawns the WASM process after a trigger + tasks + .resume_wasm_after_poller( + Box::new(respawn), + store, + module, + ctx, + deep.work, + ) + .unwrap(); + return; } - Ok(err) => err.into(), - Err(err) => err.into(), - }; - Err(err) + Ok(err) => Err(err.into()), + Err(err) => Err(err.into()), + } } - } + }; + + ctx.cleanup(&mut store, None); + + tx.send(ret).unwrap(); } pub fn for_binfmt_interpreter() -> Result { diff --git a/lib/wasi/src/fs/mod.rs b/lib/wasi/src/fs/mod.rs index f2d4fa93d85..444225c06ba 100644 --- a/lib/wasi/src/fs/mod.rs +++ b/lib/wasi/src/fs/mod.rs @@ -1748,7 +1748,11 @@ impl WasiFs { Ok(fd_ref) => { let inode = fd_ref.inode.ino().as_u64(); let ref_cnt = fd_ref.inode.ref_cnt(); - trace!(%fd, %inode, %ref_cnt, "closing file descriptor"); + if ref_cnt == 1 { + trace!(%fd, %inode, %ref_cnt, "closing file descriptor"); + } else { + trace!(%fd, %inode, %ref_cnt, "weakening file descriptor"); + } } Err(err) => { trace!(%fd, "closing file descriptor failed - {}", err); diff --git a/lib/wasi/src/lib.rs b/lib/wasi/src/lib.rs index 95b4e576b34..eb3069617b6 100644 --- a/lib/wasi/src/lib.rs +++ b/lib/wasi/src/lib.rs @@ -109,7 +109,7 @@ pub use crate::{ WasiEnv, WasiEnvBuilder, WasiEnvInit, WasiFunctionEnv, WasiInstanceHandles, WasiStateCreationError, ALL_RIGHTS, }, - syscalls::types, + syscalls::{types, rewind, unwind}, utils::{get_wasi_version, get_wasi_versions, is_wasi_module, WasiVersion}, }; @@ -129,7 +129,7 @@ pub struct RewindState { /// includes the things needed to restore it again pub struct DeepSleepWork { /// This is the work that will be performed before the thread is rewoken - pub work: Box> + Send + Sync + 'static>, + pub work: Box, /// State that the thread will be rewound to pub rewind: RewindState, } diff --git a/lib/wasi/src/runtime/task_manager/mod.rs b/lib/wasi/src/runtime/task_manager/mod.rs index 44a4942101b..77f7e368932 100644 --- a/lib/wasi/src/runtime/task_manager/mod.rs +++ b/lib/wasi/src/runtime/task_manager/mod.rs @@ -127,31 +127,33 @@ impl dyn VirtualTaskManager { store: Store, module: Module, env: WasiFunctionEnv, - work: Box> + Send + Sync + 'static>, + work: Box, ) -> Result<(), WasiThreadError> { // This poller will process any signals when the main working function is idle - struct AsyncifyPollerOwned { + struct AsyncifyPollerOwned { env: WasiFunctionEnv, store: Store, - work: - RefCell> + Send + Sync + 'static>>, + work: RefCell>, } - impl Future for AsyncifyPollerOwned { - type Output = Result, ExitCode>; + impl Future for AsyncifyPollerOwned { + type Output = Result, ExitCode>; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let mut work = self.work.borrow_mut(); let store = &self.store; let env = self.env.data(store); - if let Poll::Ready(res) = work.poll(env, &self.store, cx) { - return Poll::Ready(Ok(res)); - } - if let Some(exit_code) = env.should_exit() { - return Poll::Ready(Err(exit_code)); - } - if env.thread.has_signals_or_subscribe(cx.waker()) { - return Poll::Ready(Ok(Err(Errno::Intr))); - } - Poll::Pending + + let res = if let Poll::Ready(res) = work.poll(env, &self.store, cx) { + Ok(res) + } else if let Some(exit_code) = env.should_exit() { + Err(exit_code) + } else if env.thread.has_signals_or_subscribe(cx.waker()) { + Ok(Err(Errno::Intr)) + } else { + return Poll::Pending; + }?; + + work.finish(env, store, res)?; + Poll::Ready(Ok(Ok(()))) } } diff --git a/lib/wasi/src/syscalls/legacy/snapshot0.rs b/lib/wasi/src/syscalls/legacy/snapshot0.rs index bb236a9ec2f..62152581166 100644 --- a/lib/wasi/src/syscalls/legacy/snapshot0.rs +++ b/lib/wasi/src/syscalls/legacy/snapshot0.rs @@ -136,7 +136,7 @@ pub fn fd_seek( /// Wrapper around `syscalls::poll_oneoff` with extra logic to add the removed /// userdata field back -#[instrument(level = "trace", skip_all, fields(timeout_ns = field::Empty, fd_guards = field::Empty, seen = field::Empty), ret, err)] +#[instrument(level = "trace", skip_all, fields(timeout_ms = field::Empty, fd_guards = field::Empty, seen = field::Empty), ret, err)] pub fn poll_oneoff( mut ctx: FunctionEnvMut, in_: WasmPtr, @@ -166,7 +166,7 @@ pub fn poll_oneoff( wasi_try_mem_ok!(nevents.write(&memory, 0)); // Function to invoke once the poll is finished - let process_events = move |triggered_events, env: &'_ WasiEnv, store: &'_ dyn AsStoreRef| { + let process_events = move |env: &'_ WasiEnv, store: &'_ dyn AsStoreRef, triggered_events| { // Process all the events that were triggered let mut memory = env.memory_view(store); let mut events_seen: u32 = 0; diff --git a/lib/wasi/src/syscalls/mod.rs b/lib/wasi/src/syscalls/mod.rs index 0ad78071288..bdf8289e918 100644 --- a/lib/wasi/src/syscalls/mod.rs +++ b/lib/wasi/src/syscalls/mod.rs @@ -47,7 +47,7 @@ pub(crate) use std::{ thread::LocalKey, time::Duration, }; -use std::{io::IoSlice, mem::MaybeUninit}; +use std::{io::IoSlice, mem::MaybeUninit, time::Instant}; pub(crate) use bytes::{Bytes, BytesMut}; pub(crate) use cooked_waker::IntoWaker; @@ -334,30 +334,35 @@ where /// Future that will be polled by asyncify methods pub trait AsyncifyFuture { - type Output; - fn poll( &mut self, env: &WasiEnv, store: &dyn AsStoreRef, cx: &mut Context<'_>, - ) -> Poll; + ) -> Poll>; + + fn finish( + &mut self, + env: &WasiEnv, + store: &dyn AsStoreRef, + res: Result<(), Errno>, + ) -> Result<(), ExitCode>; } // This poller will process any signals when the main working function is idle -struct AsyncifyPoller<'a, 'b, 'c, Fut, T> +struct AsyncifyPoller<'a, 'b, 'c, Fut> where - Fut: AsyncifyFuture>, + Fut: AsyncifyFuture, { env: &'a WasiEnv, store: &'b dyn AsStoreRef, work: &'c mut Fut, } -impl<'a, 'b, 'c, Fut, T> Future for AsyncifyPoller<'a, 'b, 'c, Fut, T> +impl<'a, 'b, 'c, Fut> Future for AsyncifyPoller<'a, 'b, 'c, Fut> where - Fut: AsyncifyFuture>, + Fut: AsyncifyFuture, { - type Output = Result; + type Output = Result, WasiError>; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let env = self.env; let store = self.store; @@ -395,58 +400,111 @@ pub enum AsyncifyAction<'a> { pub(crate) fn __asyncify_with_deep_sleep( ctx: FunctionEnvMut<'_, WasiEnv>, timeout: Option, - mut work: Fut, + work: Fut, ) -> Result, WasiError> where - Fut: AsyncifyFuture> + Send + Sync + 'static, + Fut: AsyncifyFuture + Send + Sync + 'static, { + // We build a wrapper around the polling struct that will + // check for a timeout + struct WorkWithTimeout + where + Fut: AsyncifyFuture + Send + Sync + 'static, + { + inner: Box, + timeout: Option, + } + impl AsyncifyFuture for WorkWithTimeout + where + Fut: AsyncifyFuture + Send + Sync + 'static, + { + fn poll( + &mut self, + env: &WasiEnv, + store: &dyn AsStoreRef, + cx: &mut Context<'_>, + ) -> Poll> { + if let Poll::Ready(res) = self.inner.poll(env, store, cx) { + return Poll::Ready(self.finish(env, store, res).map_err(|err| err.into())); + } + + if let Some(end) = self.timeout.as_ref() { + let now = Instant::now(); + if now >= *end { + return Poll::Ready( + self.finish(env, store, Err(Errno::Timedout)) + .map_err(|err| err.into()), + ); + } + let diff = now - *end; + + let mut timeout = env.tasks().sleep_now(diff); + if timeout.as_mut().poll(cx).is_ready() { + return Poll::Ready( + self.finish(env, store, Err(Errno::Timedout)) + .map_err(|err| err.into()), + ); + } + } + + Poll::Pending + } + fn finish( + &mut self, + env: &WasiEnv, + store: &dyn AsStoreRef, + res: Result<(), Errno>, + ) -> Result<(), ExitCode> { + self.inner.finish(env, store, res) + } + } + let mut work = WorkWithTimeout { + inner: Box::new(work), + timeout: timeout.map(|timeout| Instant::now() + timeout), + }; + // Define the work let tasks = ctx.data().tasks().clone(); let work = async move { - // Create the timeout - let res = { - let env = ctx.data(); - let deep_sleep_wait = { - async { - if env.enable_deep_sleep { - env.tasks() - .sleep_now(std::time::Duration::from_millis(50)) - .await - } else { - InfiniteSleep::default().await - } - } - }; - - tokio::select! { - // The main work we are doing - res = AsyncifyPoller { - env: ctx.data(), - store: &ctx, - work: &mut work, - } => res?, - // Determines when and if we should go into a deep sleep - _ = deep_sleep_wait => { - tracing::trace!("thread entering deep sleep"); - return deep_sleep::(ctx, work) - .map(Err); - }, + let env = ctx.data(); + + // Create the deep sleeper + let deep_sleep_wait = async { + if env.enable_deep_sleep { + env.tasks() + .sleep_now(std::time::Duration::from_millis(50)) + .await + } else { + InfiniteSleep::default().await } }; - Ok(res.map(|()| ctx)) + + Ok(tokio::select! { + // Inner wait with finializer + res = AsyncifyPoller { + env: ctx.data(), + store: &ctx, + work: &mut work, + } => { + AsyncifyAction::Finish(ctx) + }, + // Determines when and if we should go into a deep sleep + _ = deep_sleep_wait => { + tracing::trace!("thread entering deep sleep"); + deep_sleep::(ctx, work)?; + AsyncifyAction::Unwind + }, + }) }; // Block on the work - let res = block_on_with_timeout(&tasks, timeout, work)?; - Ok(match res { - Ok(ctx) => AsyncifyAction::Finish(ctx), - Err(err) if err == Errno::Success => AsyncifyAction::Unwind, - Err(err) => return Err(WasiError::Exit(err.into())), - }) + tasks.block_on(work) } -pub(crate) type AsyncifyWorkAfter = - dyn FnOnce(T, &WasiEnv, &dyn AsStoreRef) + Send + Sync + 'static; +pub(crate) type AsyncifyWorkAfter = dyn FnOnce(&WasiEnv, &dyn AsStoreRef, Result) -> Result<(), ExitCode> + + Send + + Sync + + 'static; /// Asyncify takes the current thread and blocks on the async runtime associated with it /// thus allowed for asynchronous operations to execute. It has built in functionality @@ -464,32 +522,48 @@ pub(crate) fn __asyncify_with_deep_sleep_ext( after: After, ) -> Result, WasiError> where - T: 'static, + T: Send + Sync + 'static, Fut: Future + Send + Sync + 'static, - After: FnOnce(T, &WasiEnv, &dyn AsStoreRef) + Send + Sync + 'static, + After: FnOnce(&WasiEnv, &dyn AsStoreRef, Result) -> Result<(), ExitCode> + + Send + + Sync + + 'static, { struct Poller { work: Pin + Send + Sync + 'static>>, after: Option>>, + ret: Option, } impl AsyncifyFuture for Poller { - type Output = Result<(), Errno>; fn poll( &mut self, env: &WasiEnv, store: &dyn AsStoreRef, cx: &mut Context<'_>, - ) -> Poll { + ) -> Poll> { match self.work.as_mut().poll(cx) { Poll::Ready(ret) => { - if let Some(after) = self.after.take() { - after(ret, env, store); - } + self.ret.replace(ret); Poll::Ready(Ok(())) } Poll::Pending => Poll::Pending, } } + fn finish( + &mut self, + env: &WasiEnv, + store: &dyn AsStoreRef, + res: Result<(), Errno>, + ) -> Result<(), ExitCode> { + if let Some(after) = self.after.take() { + if let Some(ret) = self.ret.take() { + return after(env, store, Ok(ret)); + } else if let Err(err) = res { + return after(env, store, Err(err)); + } + } + Ok(()) + } } __asyncify_with_deep_sleep::( @@ -498,6 +572,7 @@ where Poller:: { work: Box::pin(work), after: Some(Box::new(after)), + ret: None, }, ) } @@ -964,9 +1039,9 @@ pub(crate) fn set_memory_stack( pub(crate) fn deep_sleep( mut ctx: FunctionEnvMut<'_, WasiEnv>, work: Fut, -) -> Result +) -> Result<(), WasiError> where - Fut: AsyncifyFuture> + Send + Sync + 'static, + Fut: AsyncifyFuture + Send + Sync + 'static, { // Grab all the globals and serialize them let store_data = crate::utils::store::capture_snapshot(&mut ctx.as_store_mut()) @@ -976,7 +1051,7 @@ where // Perform the unwind action let tasks = ctx.data().tasks().clone(); - unwind::(ctx, move |_ctx, memory_stack, rewind_stack| { + let res = unwind::(ctx, move |_ctx, memory_stack, rewind_stack| { // Schedule the process on the stack so that it can be resumed OnCalledAction::Trap(Box::new(RuntimeError::user(Box::new( WasiError::DeepSleep(DeepSleepWork { @@ -989,11 +1064,17 @@ where }, }), )))) - }) + })?; + + // If there is an error then exit the process, otherwise we are done + match res { + Errno::Success => Ok(()), + err => Err(WasiError::Exit(ExitCode::Errno(err))), + } } #[must_use = "you must return the result immediately so the stack can unwind"] -pub(crate) fn unwind( +pub fn unwind( mut ctx: FunctionEnvMut<'_, WasiEnv>, callback: F, ) -> Result @@ -1114,7 +1195,7 @@ where #[instrument(level = "debug", skip_all, fields(memory_stack_len = memory_stack.len(), rewind_stack_len = rewind_stack.len(), store_data_len = store_data.len()))] #[must_use = "the action must be passed to the call loop"] -pub(crate) fn rewind( +pub fn rewind( mut ctx: FunctionEnvMut, memory_stack: Bytes, rewind_stack: Bytes, diff --git a/lib/wasi/src/syscalls/wasi/poll_oneoff.rs b/lib/wasi/src/syscalls/wasi/poll_oneoff.rs index 05c7a07723d..f66b5b701bf 100644 --- a/lib/wasi/src/syscalls/wasi/poll_oneoff.rs +++ b/lib/wasi/src/syscalls/wasi/poll_oneoff.rs @@ -22,7 +22,7 @@ use crate::{ /// Output: /// - `u32 nevents` /// The number of events seen -#[instrument(level = "trace", skip_all, fields(timeout_ns = field::Empty, fd_guards = field::Empty, seen = field::Empty), ret, err)] +#[instrument(level = "trace", skip_all, fields(timeout_ms = field::Empty, fd_guards = field::Empty, seen = field::Empty), ret, err)] pub fn poll_oneoff( mut ctx: FunctionEnvMut<'_, WasiEnv>, in_: WasmPtr, @@ -49,7 +49,7 @@ pub fn poll_oneoff( wasi_try_mem_ok!(nevents.write(&memory, M::ZERO)); // Function to invoke once the poll is finished - let process_events = move |triggered_events, env: &'_ WasiEnv, store: &'_ dyn AsStoreRef| { + let process_events = move |env: &'_ WasiEnv, store: &'_ dyn AsStoreRef, triggered_events| { // Process all the events that were triggered let mut memory = env.memory_view(store); let mut events_seen: u32 = 0; @@ -142,7 +142,7 @@ pub(crate) fn poll_oneoff_internal( process_events: After, ) -> Result where - After: FnOnce(Vec, &'_ WasiEnv, &'_ dyn AsStoreRef) -> Errno + Send + Sync + 'static, + After: FnOnce(&'_ WasiEnv, &'_ dyn AsStoreRef, Vec) -> Errno + Send + Sync + 'static, { wasi_try_ok!(WasiEnv::process_signals_and_exit(&mut ctx)?); if handle_rewind::(&mut ctx) { @@ -296,12 +296,12 @@ where if let Some(time_to_sleep) = time_to_sleep.as_ref() { if *time_to_sleep == Duration::ZERO { - Span::current().record("timeout_ns", "nonblocking"); + Span::current().record("timeout_ms", "nonblocking"); } else { - Span::current().record("timeout_ns", time_to_sleep.as_millis()); + Span::current().record("timeout_ms", time_to_sleep.as_millis()); } } else { - Span::current().record("timeout_ns", "infinite"); + Span::current().record("timeout_ms", "infinite"); } // Block polling the file descriptors @@ -313,7 +313,9 @@ where ctx, time_to_sleep, batch, - move |events, env, store| { + move |env, store, res| { + let events = res.unwrap_or_else(Err); + // Process the result match events { Ok(evts) => { @@ -321,7 +323,7 @@ where Span::current().record("seen", evts.len()); // Process the events - process_events(evts, env, store); + process_events(env, store, evts); } Err(Errno::Timedout) => { // The timeout has triggerred so lets add that event @@ -347,17 +349,18 @@ where } // Process the events - process_events(evts, env, store); + process_events(env, store, evts); } // If nonblocking the Errno::Again needs to be turned into an empty list Err(Errno::Again) if time_to_sleep == Some(Duration::ZERO) => { - process_events(Default::default(), env, store); + process_events(env, store, Default::default()); } // Otherwise process the error Err(err) => { - env.thread.set_status_finished(Ok(err.into())); + tracing::warn!("triggered_timeout (without any clock subscriptions)"); } } + Ok(()) }, )?; Ok(Errno::Success) diff --git a/lib/wasi/src/syscalls/wasix/futex_wait.rs b/lib/wasi/src/syscalls/wasix/futex_wait.rs index 2ee15196851..09b1dd7d673 100644 --- a/lib/wasi/src/syscalls/wasix/futex_wait.rs +++ b/lib/wasi/src/syscalls/wasix/futex_wait.rs @@ -9,6 +9,7 @@ where M: MemorySize, { state: Arc, + woken: bool, futex_idx: u64, futex_ptr: WasmPtr, expected: u32, @@ -18,13 +19,12 @@ impl AsyncifyFuture for FutexPoller where M: MemorySize, { - type Output = Result<(), Errno>; fn poll( &mut self, env: &WasiEnv, store: &dyn AsStoreRef, cx: &mut Context<'_>, - ) -> Poll { + ) -> Poll> { let waker = cx.waker(); let view = env.memory_view(store); let mut guard = env.state.futexs.lock().unwrap(); @@ -39,11 +39,8 @@ where }; if val != self.expected { // If we are triggered then we should update the return value - let ret = self - .ret_woken - .write(&view, Bool::True) - .map_err(mem_error_to_wasi); - return Poll::Ready(ret); + self.woken = true; + return Poll::Ready(Ok(())); } } @@ -56,6 +53,23 @@ where Poll::Pending } + + fn finish( + &mut self, + env: &WasiEnv, + store: &dyn AsStoreRef, + res: Result<(), Errno>, + ) -> Result<(), ExitCode> { + if self.woken { + let view = env.memory_view(store); + self.ret_woken + .write(&view, Bool::True) + .map_err(mem_error_to_wasi) + .map_err(ExitCode::Errno) + } else { + Ok(()) + } + } } impl Drop for FutexPoller where @@ -121,6 +135,7 @@ pub fn futex_wait( // this futex event and check when it has changed let poller = FutexPoller { state: env.state.clone(), + woken: false, futex_idx, futex_ptr, expected, diff --git a/lib/wasi/src/syscalls/wasix/proc_exec.rs b/lib/wasi/src/syscalls/wasix/proc_exec.rs index 947505eeb5e..9f5f52642df 100644 --- a/lib/wasi/src/syscalls/wasix/proc_exec.rs +++ b/lib/wasi/src/syscalls/wasix/proc_exec.rs @@ -251,8 +251,10 @@ pub fn proc_exec( .await .unwrap_or_else(|_| Errno::Child.into()) }, - |exit_code, env, _| { + |env, _, res| { + let exit_code = res.unwrap_or_else(ExitCode::Errno); env.thread.set_status_finished(Ok(exit_code)); + Ok(()) }, )?; match res { diff --git a/lib/wasi/src/syscalls/wasix/proc_fork.rs b/lib/wasi/src/syscalls/wasix/proc_fork.rs index bf8e5507214..757a8648941 100644 --- a/lib/wasi/src/syscalls/wasix/proc_fork.rs +++ b/lib/wasi/src/syscalls/wasix/proc_fork.rs @@ -1,5 +1,8 @@ use super::*; -use crate::{os::task::OwnedTaskStatus, runtime::task_manager::TaskResumeAction, syscalls::*}; +use crate::{ + os::task::OwnedTaskStatus, runtime::task_manager::TaskResumeAction, syscalls::*, + WasiThreadHandle, +}; use wasmer::{vm::VMMemory, MemoryType}; @@ -218,11 +221,7 @@ pub fn proc_fork( } // Invoke the start function - let ret = run::(ctx, store, module, tasks, None); - trace!(%pid, %tid, "child exited (code = {})", ret); - - // Send the result - drop(child_handle); + let ret = run::(ctx, store, module, tasks, child_handle, None); }; tasks_outer @@ -288,8 +287,12 @@ fn run( mut store: Store, module: Module, tasks: Arc, + child_handle: WasiThreadHandle, rewind_state: Option, ) -> ExitCode { + let pid = ctx.data(&store).pid(); + let tid = ctx.data(&store).tid(); + // If we need to rewind then do so if let Some(rewind_state) = rewind_state { let ctx = ctx.env.clone().into_mut(&mut store); @@ -306,11 +309,11 @@ fn run( let mut ret: ExitCode = Errno::Success.into(); let err = if ctx.data(&store).thread.is_main() { - trace!("re-invoking main"); + trace!(%pid, %tid, "re-invoking main"); let start = ctx.data(&store).inner().start.clone().unwrap(); start.call(&mut store) } else { - trace!("re-invoking thread_spawn"); + trace!(%pid, %tid, "re-invoking thread_spawn"); let start = ctx.data(&store).inner().thread_spawn.clone().unwrap(); start.call(&mut store, 0, 0) }; @@ -320,13 +323,13 @@ fn run( ret = exit_code; } Ok(WasiError::DeepSleep(deep)) => { - trace!("entered a deep sleep"); + trace!(%pid, %tid, "entered a deep sleep"); // Extract the memory let memory = match ctx.data(&store).memory().try_clone(&store) { Some(m) => m, None => { - tracing::error!("failed to clone memory before deep sleep"); + tracing::error!(%pid, %tid, "failed to clone memory before deep sleep"); return Errno::Noexec.into(); } }; @@ -337,19 +340,23 @@ fn run( let tasks = tasks.clone(); let rewind_state = Some(deep.rewind); move |store, module| { - run::(ctx, store, module, tasks, rewind_state); + run::(ctx, store, module, tasks, child_handle, rewind_state); } }; /// Spawns the WASM process after a trigger tasks.resume_wasm_after_poller(Box::new(respawn), store, module, ctx, deep.work); - return Errno::Unknown.into(); + return Errno::Success.into(); } _ => {} } } + trace!(%pid, %tid, "child exited (code = {})", ret); // Clean up the environment and return the result ctx.cleanup((&mut store), Some(ret)); + + // We drop the handle at the last moment which will close the thread + drop(child_handle); ret } diff --git a/lib/wasi/src/syscalls/wasix/proc_join.rs b/lib/wasi/src/syscalls/wasix/proc_join.rs index 38a0278957e..293aa2877d7 100644 --- a/lib/wasi/src/syscalls/wasix/proc_join.rs +++ b/lib/wasi/src/syscalls/wasix/proc_join.rs @@ -46,7 +46,9 @@ pub fn proc_join( // If we were just restored the stack then we were woken after a deep sleep // and the return calues are already set if handle_rewind::(&mut ctx) { - return ret_result(ctx); + let ret = ret_result(ctx); + tracing::trace!("rewound join ret={:?}", ret); + return ret; } let env = ctx.data(); @@ -89,7 +91,9 @@ pub fn proc_join( ctx, None, async move { process.join_any_child().await }, - move |child_exit, env, store| { + move |env, store, res| { + let child_exit = res.unwrap_or_else(Err); + let memory = env.memory_view(store); match child_exit { Ok(Some((pid, exit_code))) => { @@ -107,7 +111,9 @@ pub fn proc_join( exit_normal: exit_code.into(), }, }; - status_ptr.write(&memory, status).ok(); + status_ptr + .write(&memory, status) + .map_err(mem_error_to_wasi)?; } Ok(None) => { let status = JoinStatus { @@ -116,16 +122,21 @@ pub fn proc_join( nothing_errno: Errno::Child, }, }; - status_ptr.write(&memory, status).ok(); + status_ptr + .write(&memory, status) + .map_err(mem_error_to_wasi)?; } Err(err) => { let status = JoinStatus { tag: JoinStatusType::Nothing, u: JoinStatusUnion { nothing_errno: err }, }; - status_ptr.write(&memory, status).ok(); + status_ptr + .write(&memory, status) + .map_err(mem_error_to_wasi)?; } } + Ok(()) }, )?; return match res { @@ -175,7 +186,9 @@ pub fn proc_join( ctx, None, async move { process.join().await.unwrap_or_else(|_| Errno::Child.into()) }, - move |exit_code, env, store| { + move |env, store, res| { + let exit_code = res.unwrap_or_else(ExitCode::Errno); + trace!(ret_id = pid.raw(), exit_code = exit_code.raw()); { let mut inner = env.process.inner.write().unwrap(); @@ -189,7 +202,11 @@ pub fn proc_join( exit_normal: exit_code.into(), }, }; - status_ptr.write(&memory, status).ok(); + status_ptr + .write(&memory, status) + .map_err(mem_error_to_wasi) + .map_err(ExitCode::Errno)?; + Ok(()) }, )?; return match res { diff --git a/lib/wasi/src/syscalls/wasix/thread_join.rs b/lib/wasi/src/syscalls/wasix/thread_join.rs index b6bf994b7eb..98c503e4813 100644 --- a/lib/wasi/src/syscalls/wasix/thread_join.rs +++ b/lib/wasi/src/syscalls/wasix/thread_join.rs @@ -28,7 +28,7 @@ pub fn thread_join( async move { other_thread.join().await; }, - move |_, _, _| {}, + |_, _, res| res.map_err(ExitCode::Errno), )?; Ok(Errno::Success) } else { diff --git a/lib/wasi/src/syscalls/wasix/thread_sleep.rs b/lib/wasi/src/syscalls/wasix/thread_sleep.rs index d4eee3e77d2..30dfa1c1b85 100644 --- a/lib/wasi/src/syscalls/wasix/thread_sleep.rs +++ b/lib/wasi/src/syscalls/wasix/thread_sleep.rs @@ -46,7 +46,10 @@ pub(crate) fn thread_sleep_internal( "the timeout or signals will wake up this thread even though it waits forever" ) }, - move |_, _, _| {}, + |_, _, res| match res { + Ok(()) => Ok(()), + _ => Ok(()), + }, )?; } Ok(Errno::Success) From a8cad0c220bf20b827b8a20a0145ed5fdcaeabd1 Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Thu, 23 Mar 2023 10:19:22 +1100 Subject: [PATCH 09/52] Fixes a bug which causes libc to thrash the futex_wake_all because it thinks the wake has failed when it has actually succeeded --- lib/wasi/src/syscalls/wasix/futex_wake_all.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/wasi/src/syscalls/wasix/futex_wake_all.rs b/lib/wasi/src/syscalls/wasix/futex_wake_all.rs index a0990aaf4ec..3adc50ea9ef 100644 --- a/lib/wasi/src/syscalls/wasix/futex_wake_all.rs +++ b/lib/wasi/src/syscalls/wasix/futex_wake_all.rs @@ -26,7 +26,7 @@ pub fn futex_wake_all( futex.wakers.into_iter().for_each(|w| w.wake()); true } else { - false + true } }; Span::current().record("woken", woken); From acbdb03332b6e0547bda37fc753da7e184ce0479 Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Thu, 23 Mar 2023 10:21:46 +1100 Subject: [PATCH 10/52] Also fixed the futex_wake once call --- lib/wasi/src/syscalls/wasix/futex_wake.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/wasi/src/syscalls/wasix/futex_wake.rs b/lib/wasi/src/syscalls/wasix/futex_wake.rs index 6238e0aaf21..e73b24ff3b1 100644 --- a/lib/wasi/src/syscalls/wasix/futex_wake.rs +++ b/lib/wasi/src/syscalls/wasix/futex_wake.rs @@ -33,7 +33,7 @@ pub fn futex_wake( } true } else { - false + true } }; Span::current().record("woken", woken); From 9e9cdefe071ad95031eefc6e0d96c36552dd4b27 Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Fri, 24 Mar 2023 01:22:33 +1100 Subject: [PATCH 11/52] Refactoring of the async threading implementation to fix the snapshot tests --- lib/cli/src/commands/run/wasi.rs | 56 ++++--- lib/wasi/src/bin_factory/exec.rs | 65 +++++--- lib/wasi/src/lib.rs | 56 ++++++- lib/wasi/src/os/task/process.rs | 6 + lib/wasi/src/runtime/task_manager/mod.rs | 28 ++-- lib/wasi/src/runtime/task_manager/tokio.rs | 7 +- lib/wasi/src/syscalls/mod.rs | 142 ++++++++++-------- lib/wasi/src/syscalls/wasix/futex_wait.rs | 64 +++++--- lib/wasi/src/syscalls/wasix/proc_fork.rs | 38 ++--- lib/wasi/src/syscalls/wasix/thread_join.rs | 11 +- lib/wasi/src/syscalls/wasix/thread_sleep.rs | 5 +- lib/wasi/src/syscalls/wasix/thread_spawn.rs | 28 ++-- .../snapshot__snapshot_bash_dash.snap | 2 +- 13 files changed, 316 insertions(+), 192 deletions(-) diff --git a/lib/cli/src/commands/run/wasi.rs b/lib/cli/src/commands/run/wasi.rs index 02d331bec40..9fa263d1e7b 100644 --- a/lib/cli/src/commands/run/wasi.rs +++ b/lib/cli/src/commands/run/wasi.rs @@ -274,29 +274,44 @@ impl Wasi { invoke: Option, args: Vec, tx: Sender>, - rewind_state: Option, + rewind_state: Option<(RewindState, Result<(), Errno>)>, ) { // If we need to rewind then do so - if let Some(rewind_state) = rewind_state { - let ctx = ctx.env.clone().into_mut(&mut store); - let res = if rewind_state.is_64bit { - rewind::( - ctx, - rewind_state.memory_stack.freeze(), - rewind_state.rewind_stack.freeze(), + if let Some((mut rewind_state, trigger_res)) = rewind_state { + if rewind_state.is_64bit { + if let Err(exit_code) = rewind_state + .rewinding_finish::(ctx.env.clone().into_mut(&mut store), trigger_res) + { + tx.send(Ok(exit_code.raw())).ok(); + return; + } + let res = rewind::( + ctx.env.clone().into_mut(&mut store), + rewind_state.memory_stack, + rewind_state.rewind_stack, rewind_state.store_data, - ) + ); + if res != Errno::Success { + tx.send(Ok(res as i32)).ok(); + return; + } } else { - rewind::( - ctx, - rewind_state.memory_stack.freeze(), - rewind_state.rewind_stack.freeze(), + if let Err(exit_code) = rewind_state + .rewinding_finish::(ctx.env.clone().into_mut(&mut store), trigger_res) + { + tx.send(Ok(exit_code.raw())).ok(); + return; + } + let res = rewind::( + ctx.env.clone().into_mut(&mut store), + rewind_state.memory_stack, + rewind_state.rewind_stack, rewind_state.store_data, - ) - }; - if res != Errno::Success { - tx.send(Ok(res as i32)).ok(); - return; + ); + if res != Errno::Success { + tx.send(Ok(res as i32)).ok(); + return; + } } } @@ -318,6 +333,7 @@ impl Wasi { let start: Function = RunWithPathBuf::try_find_function(&instance, path.as_path(), "_start", &[]) .unwrap(); + let result = start.call(&mut store, &[]); Self::handle_result(ctx, store, instance, path, invoke, args, result, tx) } @@ -350,7 +366,7 @@ impl Wasi { let rewind = deep.rewind; let respawn = { let ctx = ctx.clone(); - move |store, _module| { + move |store, _module, res| { Self::run_with_deep_sleep( ctx, store, @@ -359,7 +375,7 @@ impl Wasi { invoke, args, tx, - Some(rewind), + Some((rewind, res)), ); } }; diff --git a/lib/wasi/src/bin_factory/exec.rs b/lib/wasi/src/bin_factory/exec.rs index 6d18e3bdca3..3ea563aec18 100644 --- a/lib/wasi/src/bin_factory/exec.rs +++ b/lib/wasi/src/bin_factory/exec.rs @@ -8,7 +8,7 @@ use crate::{ use futures::Future; use tracing::*; use wasmer::{Function, FunctionEnvMut, Instance, Memory, Memory32, Memory64, Module, Store}; -use wasmer_wasix_types::wasi::Errno; +use wasmer_wasix_types::wasi::{Errno, ExitCode}; use super::{BinFactory, BinaryPackage, ModuleCache}; use crate::{ @@ -102,7 +102,6 @@ pub fn spawn_exec_module( // 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()); // Perform the initialization @@ -142,11 +141,15 @@ pub fn spawn_exec_module( return; } + // Set the asynchronous threads flag + let capable_of_deep_sleep = ctx.data(&store).capable_of_deep_sleep(); + ctx.data_mut(&mut store).enable_deep_sleep = capable_of_deep_sleep; + // 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); + finish_module(ctx.data(&store), Errno::Noexec.into()); return; } } @@ -171,7 +174,7 @@ pub fn spawn_exec_module( call_module(ctx, store, module, start, None); } else { debug!("wasi[{}]::exec-failed: missing _start function", pid); - finish_module(ctx, store, Errno::Noexec); + finish_module(ctx.data(&store), Errno::Noexec.into()); } } }; @@ -188,8 +191,8 @@ pub fn spawn_exec_module( } /// Finishes with a module and notifies an errcode -fn finish_module(ctx: WasiFunctionEnv, store: Store, err: Errno) { - ctx.data(&store).blocking_cleanup(Some(err.into())); +fn finish_module(env: &WasiEnv, err: ExitCode) { + env.blocking_cleanup(Some(err)); } /// Calls the module @@ -198,7 +201,7 @@ fn call_module( mut store: Store, module: Module, start: Function, - rewind_state: Option, + rewind_state: Option<(RewindState, Result<(), Errno>)>, ) { let env = ctx.data(&store); let pid = env.pid(); @@ -207,26 +210,42 @@ fn call_module( 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::( + if let Some((mut rewind_state, trigger_res)) = rewind_state { + if rewind_state.is_64bit { + if let Err(exit_code) = rewind_state + .rewinding_finish::(ctx.env.clone().into_mut(&mut store), trigger_res) + { + finish_module(ctx.data(&store), exit_code); + return; + } + let res = rewind::( ctx.env.clone().into_mut(&mut store), - rewind_state.memory_stack.freeze(), - rewind_state.rewind_stack.freeze(), + rewind_state.memory_stack, + rewind_state.rewind_stack, rewind_state.store_data, - ) + ); + if res != Errno::Success { + finish_module(ctx.data(&store), res.into()); + return; + } } else { - rewind::( + if let Err(exit_code) = rewind_state + .rewinding_finish::(ctx.env.clone().into_mut(&mut store), trigger_res) + { + finish_module(ctx.data(&store), exit_code); + return; + } + let res = rewind::( ctx.env.clone().into_mut(&mut store), - rewind_state.memory_stack.freeze(), - rewind_state.rewind_stack.freeze(), + rewind_state.memory_stack, + rewind_state.rewind_stack, rewind_state.store_data, - ) + ); + if res != Errno::Success { + finish_module(ctx.data(&store), res.into()); + return; + } }; - if res != Errno::Success { - finish_module(ctx, store, res); - return; - } } // Invoke the start function @@ -247,9 +266,9 @@ fn call_module( let rewind = deep.rewind; let respawn = { let ctx = ctx.clone(); - move |store, module| { + move |store, module, trigger_res| { // Call the thread - call_module(ctx, store, module, start, Some(rewind)); + call_module(ctx, store, module, start, Some((rewind, trigger_res))); } }; diff --git a/lib/wasi/src/lib.rs b/lib/wasi/src/lib.rs index eb3069617b6..a1a335a43d4 100644 --- a/lib/wasi/src/lib.rs +++ b/lib/wasi/src/lib.rs @@ -58,7 +58,7 @@ use std::{cell::RefCell, sync::atomic::AtomicU32}; #[allow(unused_imports)] use bytes::{Bytes, BytesMut}; use os::task::control_plane::ControlPlaneError; -use syscalls::AsyncifyFuture; +use syscalls::{get_memory_stack, set_memory_stack, AsyncifyFuture}; use thiserror::Error; use tracing::error; // re-exports needed for OS @@ -66,8 +66,8 @@ pub use wasmer; pub use wasmer_wasix_types; use wasmer::{ - imports, namespace, AsStoreMut, Exports, FunctionEnv, Imports, Memory32, MemoryAccessError, - MemorySize, RuntimeError, + imports, namespace, AsStoreMut, AsStoreRef, Exports, FunctionEnv, FunctionEnvMut, Imports, + Memory32, MemoryAccessError, MemorySize, RuntimeError, }; pub use virtual_fs; @@ -109,20 +109,59 @@ pub use crate::{ WasiEnv, WasiEnvBuilder, WasiEnvInit, WasiFunctionEnv, WasiInstanceHandles, WasiStateCreationError, ALL_RIGHTS, }, - syscalls::{types, rewind, unwind}, + syscalls::{rewind, types, unwind}, utils::{get_wasi_version, get_wasi_versions, is_wasi_module, WasiVersion}, }; +/// Trait that will be invoked after the rewind has finished +/// It is possible that the process will be terminated rather +/// than restored at this point +pub trait RewindPostProcess { + fn finish( + &mut self, + env: &WasiEnv, + store: &dyn AsStoreRef, + res: Result<(), Errno>, + ) -> Result<(), ExitCode>; +} + /// The rewind state after a deep sleep pub struct RewindState { /// Memory stack used to restore the stack trace back to where it was - pub memory_stack: BytesMut, + pub memory_stack: Bytes, /// Call stack used to restore the stack trace back to where it was - pub rewind_stack: BytesMut, + pub rewind_stack: Bytes, /// All the global data stored in the store pub store_data: Bytes, /// Flag that indicates if this rewind is 64-bit or 32-bit memory based pub is_64bit: bool, + /// This is the function that's invoked after the work is finished + /// and the rewind has been applied. + pub finish: Box, +} + +impl RewindState { + pub fn rewinding_finish( + &mut self, + mut ctx: FunctionEnvMut, + res: Result<(), Errno>, + ) -> Result<(), ExitCode> { + let (env, mut store) = ctx.data_and_store_mut(); + set_memory_stack::(env, &mut store, self.memory_stack.clone()).map_err(|err| { + tracing::error!("failed on rewinding_finish - {}", err); + ExitCode::Errno(Errno::Memviolation) + })?; + let ret = self.finish.finish(env, &mut store, res); + if ret.is_ok() { + self.memory_stack = get_memory_stack::(env, &mut store) + .map_err(|err| { + tracing::error!("failed on rewinding_finish - {}", err); + ExitCode::Errno(Errno::Memviolation) + })? + .freeze(); + } + ret + } } /// Represents the work that will be done when a thread goes to deep sleep and @@ -827,6 +866,11 @@ fn mem_error_to_wasi(err: MemoryAccessError) -> Errno { } } +fn mem_error_to_wasi_error(err: MemoryAccessError) -> WasiError { + let err = mem_error_to_wasi(err); + WasiError::Exit(err.into()) +} + fn mem_error_to_bus(err: MemoryAccessError) -> BusErrno { match err { MemoryAccessError::HeapOutOfBounds => BusErrno::Memviolation, diff --git a/lib/wasi/src/os/task/process.rs b/lib/wasi/src/os/task/process.rs index 69972cffbaf..5eb3a9e9869 100644 --- a/lib/wasi/src/os/task/process.rs +++ b/lib/wasi/src/os/task/process.rs @@ -236,6 +236,9 @@ impl WasiProcess { } let tid: WasiThreadId = tid.into(); + let pid = self.pid(); + tracing::trace!(%pid, %tid, "signal-thread({:?})", signal); + let inner = self.inner.read().unwrap(); if let Some(thread) = inner.threads.get(&tid) { thread.signal(signal); @@ -251,6 +254,9 @@ impl WasiProcess { /// Signals all the threads in this process pub fn signal_process(&self, signal: Signal) { + let pid = self.pid(); + tracing::trace!(%pid, "signal-process({:?})", signal); + { let inner = self.inner.read().unwrap(); if self.waiting.load(Ordering::Acquire) > 0 { diff --git a/lib/wasi/src/runtime/task_manager/mod.rs b/lib/wasi/src/runtime/task_manager/mod.rs index 77f7e368932..add2ddfa1ea 100644 --- a/lib/wasi/src/runtime/task_manager/mod.rs +++ b/lib/wasi/src/runtime/task_manager/mod.rs @@ -38,7 +38,7 @@ pub enum SpawnType { /// or if it should abort and exit the thread pub enum TaskResumeAction { // The task will run with the following store - Run(Store), + Run(Store, Result<(), Errno>), /// The task has been aborted Abort, } @@ -92,7 +92,7 @@ pub trait VirtualTaskManager: std::fmt::Debug + Send + Sync + 'static { /// After the trigger has successfully completed fn resume_wasm_after_trigger( &self, - task: Box, + task: Box) + Send + 'static>, store: Store, module: Module, trigger: Box, @@ -123,7 +123,7 @@ impl dyn VirtualTaskManager { /// After the poller has successed pub fn resume_wasm_after_poller( &self, - task: Box, + task: Box) + Send + 'static>, store: Store, module: Module, env: WasiFunctionEnv, @@ -142,7 +142,7 @@ impl dyn VirtualTaskManager { let store = &self.store; let env = self.env.data(store); - let res = if let Poll::Ready(res) = work.poll(env, &self.store, cx) { + Poll::Ready(if let Poll::Ready(res) = work.poll(env, &self.store, cx) { Ok(res) } else if let Some(exit_code) = env.should_exit() { Err(exit_code) @@ -150,10 +150,7 @@ impl dyn VirtualTaskManager { Ok(Err(Errno::Intr)) } else { return Poll::Pending; - }?; - - work.finish(env, store, res)?; - Poll::Ready(Ok(Ok(()))) + }) } } @@ -169,13 +166,16 @@ impl dyn VirtualTaskManager { work: RefCell::new(work), }; let res = Pin::new(&mut poller).await; - if let Err(exit_code) = res { - let env = poller.env.data(&poller.store); - env.thread.set_status_finished(Ok(exit_code)); - return TaskResumeAction::Abort; - } + let res = match res { + Ok(res) => res, + Err(exit_code) => { + let env = poller.env.data(&poller.store); + env.thread.set_status_finished(Ok(exit_code)); + return TaskResumeAction::Abort; + } + }; tracing::trace!("deep sleep woken - {:?}", res); - TaskResumeAction::Run(poller.store) + TaskResumeAction::Run(poller.store, res) }) }), ) diff --git a/lib/wasi/src/runtime/task_manager/tokio.rs b/lib/wasi/src/runtime/task_manager/tokio.rs index ddaf85dd3c2..1fb0b1ba90a 100644 --- a/lib/wasi/src/runtime/task_manager/tokio.rs +++ b/lib/wasi/src/runtime/task_manager/tokio.rs @@ -10,6 +10,7 @@ use wasmer::{ vm::{VMMemory, VMSharedMemory}, Module, Store, }; +use wasmer_wasix_types::wasi::Errno; use crate::os::task::thread::WasiThreadError; @@ -149,7 +150,7 @@ impl VirtualTaskManager for TokioTaskManager { /// See [`VirtualTaskManager::task_wasm_with_trigger`]. fn resume_wasm_after_trigger( &self, - task: Box, + task: Box) + Send + 'static>, store: Store, module: Module, trigger: Box, @@ -158,10 +159,10 @@ impl VirtualTaskManager for TokioTaskManager { let handle = self.0.clone(); self.0.spawn(async move { let action = trigger.await; - if let TaskResumeAction::Run(store) = action { + if let TaskResumeAction::Run(store, res) = action { handle.spawn_blocking(move || { // Invoke the callback - task(store, module); + task(store, module, res); }); } }); diff --git a/lib/wasi/src/syscalls/mod.rs b/lib/wasi/src/syscalls/mod.rs index bdf8289e918..5e9013bc9f9 100644 --- a/lib/wasi/src/syscalls/mod.rs +++ b/lib/wasi/src/syscalls/mod.rs @@ -114,8 +114,9 @@ use crate::{ fs_error_into_wasi_err, virtual_file_type_to_wasi_file_type, Fd, InodeVal, Kind, MAX_SYMLINKS, }, + mem_error_to_wasi_error, utils::store::InstanceSnapshot, - DeepSleepWork, RewindState, VirtualBusError, WasiInodes, + DeepSleepWork, RewindPostProcess, RewindState, VirtualBusError, WasiInodes, }; pub(crate) use crate::{net::net_error_into_wasi_err, utils::WasiParkingLot}; @@ -340,13 +341,6 @@ pub trait AsyncifyFuture { store: &dyn AsStoreRef, cx: &mut Context<'_>, ) -> Poll>; - - fn finish( - &mut self, - env: &WasiEnv, - store: &dyn AsStoreRef, - res: Result<(), Errno>, - ) -> Result<(), ExitCode>; } // This poller will process any signals when the main working function is idle @@ -397,13 +391,15 @@ pub enum AsyncifyAction<'a> { /// or it will return an WasiError which will exit the WASM call using asyncify /// and instead process it on a shared task /// -pub(crate) fn __asyncify_with_deep_sleep( +pub(crate) fn __asyncify_with_deep_sleep( ctx: FunctionEnvMut<'_, WasiEnv>, timeout: Option, work: Fut, + mut after: After, ) -> Result, WasiError> where Fut: AsyncifyFuture + Send + Sync + 'static, + After: RewindPostProcess + Send + Sync + 'static, { // We build a wrapper around the polling struct that will // check for a timeout @@ -425,38 +421,24 @@ where cx: &mut Context<'_>, ) -> Poll> { if let Poll::Ready(res) = self.inner.poll(env, store, cx) { - return Poll::Ready(self.finish(env, store, res).map_err(|err| err.into())); + return Poll::Ready(res); } if let Some(end) = self.timeout.as_ref() { let now = Instant::now(); if now >= *end { - return Poll::Ready( - self.finish(env, store, Err(Errno::Timedout)) - .map_err(|err| err.into()), - ); + return Poll::Ready(Err(Errno::Timedout)); } let diff = now - *end; let mut timeout = env.tasks().sleep_now(diff); if timeout.as_mut().poll(cx).is_ready() { - return Poll::Ready( - self.finish(env, store, Err(Errno::Timedout)) - .map_err(|err| err.into()), - ); + return Poll::Ready(Err(Errno::Timedout)); } } Poll::Pending } - fn finish( - &mut self, - env: &WasiEnv, - store: &dyn AsStoreRef, - res: Result<(), Errno>, - ) -> Result<(), ExitCode> { - self.inner.finish(env, store, res) - } } let mut work = WorkWithTimeout { inner: Box::new(work), @@ -486,12 +468,13 @@ where store: &ctx, work: &mut work, } => { + after.finish(ctx.data(), &ctx, res?); AsyncifyAction::Finish(ctx) }, // Determines when and if we should go into a deep sleep _ = deep_sleep_wait => { tracing::trace!("thread entering deep sleep"); - deep_sleep::(ctx, work)?; + deep_sleep::(ctx, work, after)?; AsyncifyAction::Unwind }, }) @@ -529,10 +512,10 @@ where + Sync + 'static, { + let ret = Arc::new(Mutex::new(None)); struct Poller { work: Pin + Send + Sync + 'static>>, - after: Option>>, - ret: Option, + ret: Arc>>, } impl AsyncifyFuture for Poller { fn poll( @@ -543,12 +526,26 @@ where ) -> Poll> { match self.work.as_mut().poll(cx) { Poll::Ready(ret) => { - self.ret.replace(ret); + let mut guard = self.ret.lock().unwrap(); + guard.replace(ret); Poll::Ready(Ok(())) } Poll::Pending => Poll::Pending, } } + } + struct Finisher { + after: Option< + Box< + dyn FnOnce(&WasiEnv, &dyn AsStoreRef, Result) -> Result<(), ExitCode> + + Send + + Sync + + 'static, + >, + >, + ret: Arc>>, + } + impl RewindPostProcess for Finisher { fn finish( &mut self, env: &WasiEnv, @@ -556,23 +553,30 @@ where res: Result<(), Errno>, ) -> Result<(), ExitCode> { if let Some(after) = self.after.take() { - if let Some(ret) = self.ret.take() { - return after(env, store, Ok(ret)); - } else if let Err(err) = res { - return after(env, store, Err(err)); - } + let res = match res { + Ok(()) => { + let mut guard = self.ret.lock().unwrap(); + guard.take().ok_or(Errno::Unknown) + } + Err(err) => Err(err), + }; + after(env, store, res) + } else { + Err(ExitCode::Errno(Errno::Unknown)) } - Ok(()) } } - __asyncify_with_deep_sleep::( + __asyncify_with_deep_sleep::( ctx, timeout, Poller:: { work: Box::pin(work), + ret: ret.clone(), + }, + Finisher { after: Some(Box::new(after)), - ret: None, + ret, }, ) } @@ -901,12 +905,12 @@ pub(crate) fn get_current_time_in_nanos() -> Result { Ok(now as Timestamp) } -pub(crate) fn get_stack_lower(mut ctx: &mut FunctionEnvMut<'_, WasiEnv>) -> u64 { - ctx.data().layout.stack_lower +pub(crate) fn get_stack_lower(env: &WasiEnv) -> u64 { + env.layout.stack_lower } -pub(crate) fn get_stack_upper(mut ctx: &mut FunctionEnvMut<'_, WasiEnv>) -> u64 { - ctx.data().layout.stack_upper +pub(crate) fn get_stack_upper(env: &WasiEnv) -> u64 { + env.layout.stack_upper } pub(crate) fn get_memory_stack_pointer( @@ -914,7 +918,7 @@ pub(crate) fn get_memory_stack_pointer( ) -> Result { // Get the current value of the stack pointer (which we will use // to save all of the stack) - let stack_upper = get_stack_upper(ctx); + let stack_upper = get_stack_upper(ctx.data()); let stack_pointer = if let Some(stack_pointer) = ctx.data().inner().stack_pointer.clone() { match stack_pointer.get(ctx) { Value::I32(a) => a as u64, @@ -930,25 +934,26 @@ pub(crate) fn get_memory_stack_pointer( pub(crate) fn get_memory_stack_offset( ctx: &mut FunctionEnvMut<'_, WasiEnv>, ) -> Result { - let stack_upper = get_stack_upper(ctx); + let stack_upper = get_stack_upper(ctx.data()); let stack_pointer = get_memory_stack_pointer(ctx)?; Ok(stack_upper - stack_pointer) } pub(crate) fn set_memory_stack_offset( - ctx: &mut FunctionEnvMut<'_, WasiEnv>, + env: &WasiEnv, + store: &mut impl AsStoreMut, offset: u64, ) -> Result<(), String> { // Sets the stack pointer - let stack_upper = get_stack_upper(ctx); + let stack_upper = get_stack_upper(env); let stack_pointer = stack_upper - offset; - if let Some(stack_pointer_ptr) = ctx.data().inner().stack_pointer.clone() { - match stack_pointer_ptr.get(ctx) { + if let Some(stack_pointer_ptr) = env.inner().stack_pointer.clone() { + match stack_pointer_ptr.get(store) { Value::I32(_) => { - stack_pointer_ptr.set(ctx, Value::I32(stack_pointer as i32)); + stack_pointer_ptr.set(store, Value::I32(stack_pointer as i32)); } Value::I64(_) => { - stack_pointer_ptr.set(ctx, Value::I64(stack_pointer as i64)); + stack_pointer_ptr.set(store, Value::I64(stack_pointer as i64)); } _ => { return Err( @@ -965,13 +970,14 @@ pub(crate) fn set_memory_stack_offset( #[allow(dead_code)] pub(crate) fn get_memory_stack( - ctx: &mut FunctionEnvMut<'_, WasiEnv>, + env: &WasiEnv, + store: &mut impl AsStoreMut, ) -> Result { // Get the current value of the stack pointer (which we will use // to save all of the stack) - let stack_base = get_stack_upper(ctx); - let stack_pointer = if let Some(stack_pointer) = ctx.data().inner().stack_pointer.clone() { - match stack_pointer.get(ctx) { + let stack_base = get_stack_upper(env); + let stack_pointer = if let Some(stack_pointer) = env.inner().stack_pointer.clone() { + match stack_pointer.get(store) { Value::I32(a) => a as u64, Value::I64(a) => a as u64, _ => stack_base, @@ -979,8 +985,7 @@ pub(crate) fn get_memory_stack( } else { return Err("failed to save stack: not exported __stack_pointer global".to_string()); }; - let env = ctx.data(); - let memory = env.memory_view(&ctx); + let memory = env.memory_view(store); let stack_offset = env.layout.stack_upper - stack_pointer; // Read the memory stack into a vector @@ -1003,11 +1008,12 @@ pub(crate) fn get_memory_stack( #[allow(dead_code)] pub(crate) fn set_memory_stack( - mut ctx: &mut FunctionEnvMut<'_, WasiEnv>, + env: &WasiEnv, + store: &mut impl AsStoreMut, stack: Bytes, ) -> Result<(), String> { // First we restore the memory stack - let stack_upper = get_stack_upper(ctx); + let stack_upper = get_stack_upper(env); let stack_offset = stack.len() as u64; let stack_pointer = stack_upper - stack_offset; let stack_ptr = WasmPtr::::new( @@ -1016,8 +1022,7 @@ pub(crate) fn set_memory_stack( .map_err(|_| "failed to restore stack: stack pointer overflow".to_string())?, ); - let env = ctx.data(); - let memory = env.memory_view(&ctx); + let memory = env.memory_view(store); stack_ptr .slice( &memory, @@ -1029,19 +1034,21 @@ pub(crate) fn set_memory_stack( .map_err(|err| format!("failed to write stack: {}", err))?; // Set the stack pointer itself and return - set_memory_stack_offset(ctx, stack_offset)?; + set_memory_stack_offset(env, store, stack_offset)?; Ok(()) } /// Puts the process to deep sleep and wakes it again when /// the supplied future completes #[must_use = "you must return the result immediately so the stack can unwind"] -pub(crate) fn deep_sleep( +pub(crate) fn deep_sleep( mut ctx: FunctionEnvMut<'_, WasiEnv>, work: Fut, + after: After, ) -> Result<(), WasiError> where Fut: AsyncifyFuture + Send + Sync + 'static, + After: RewindPostProcess + Send + Sync + 'static, { // Grab all the globals and serialize them let store_data = crate::utils::store::capture_snapshot(&mut ctx.as_store_mut()) @@ -1057,10 +1064,11 @@ where WasiError::DeepSleep(DeepSleepWork { work: Box::new(work), rewind: RewindState { - memory_stack, - rewind_stack, + memory_stack: memory_stack.freeze(), + rewind_stack: rewind_stack.freeze(), store_data, is_64bit: M::is_64bit(), + finish: Box::new(after), }, }), )))) @@ -1086,7 +1094,8 @@ where { // Get the current stack pointer (this will be used to determine the // upper limit of stack space remaining to unwind into) - let memory_stack = match get_memory_stack::(&mut ctx) { + let (env, mut store) = ctx.data_and_store_mut(); + let memory_stack = match get_memory_stack::(env, &mut store) { Ok(a) => a, Err(err) => { warn!("unable to get the memory stack - {}", err); @@ -1275,7 +1284,8 @@ pub(crate) fn handle_rewind(ctx: &mut FunctionEnvMut<'_, WasiEnv> } // Restore the memory stack - set_memory_stack::(ctx, memory_stack); + let (env, mut store) = ctx.data_and_store_mut(); + set_memory_stack::(env, &mut store, memory_stack); true } else { false diff --git a/lib/wasi/src/syscalls/wasix/futex_wait.rs b/lib/wasi/src/syscalls/wasix/futex_wait.rs index 09b1dd7d673..c8f8ce34493 100644 --- a/lib/wasi/src/syscalls/wasix/futex_wait.rs +++ b/lib/wasi/src/syscalls/wasix/futex_wait.rs @@ -9,11 +9,10 @@ where M: MemorySize, { state: Arc, - woken: bool, + woken: Arc>, futex_idx: u64, futex_ptr: WasmPtr, expected: u32, - ret_woken: WasmPtr, } impl AsyncifyFuture for FutexPoller where @@ -33,13 +32,18 @@ where let val = match self.futex_ptr.read(&view) { Ok(a) => a, Err(err) => { - self.ret_woken.write(&view, Bool::True).ok(); + { + let mut guard = self.woken.lock().unwrap(); + *guard = true; + } return Poll::Ready(Err(mem_error_to_wasi(err))); } }; if val != self.expected { - // If we are triggered then we should update the return value - self.woken = true; + { + let mut guard = self.woken.lock().unwrap(); + *guard = true; + } return Poll::Ready(Ok(())); } } @@ -53,14 +57,41 @@ where Poll::Pending } +} +impl Drop for FutexPoller +where + M: MemorySize, +{ + fn drop(&mut self) { + let futex = { + let mut guard = self.state.futexs.lock().unwrap(); + guard.remove(&self.futex_idx) + }; + if let Some(futex) = futex { + futex.wakers.into_iter().for_each(|w| w.wake()); + } + } +} +struct FutexAfter +where + M: MemorySize, +{ + woken: Arc>, + ret_woken: WasmPtr, +} +impl RewindPostProcess for FutexAfter +where + M: MemorySize, +{ fn finish( &mut self, env: &WasiEnv, store: &dyn AsStoreRef, res: Result<(), Errno>, ) -> Result<(), ExitCode> { - if self.woken { + let woken = self.woken.lock().unwrap(); + if *woken { let view = env.memory_view(store); self.ret_woken .write(&view, Bool::True) @@ -71,20 +102,6 @@ where } } } -impl Drop for FutexPoller -where - M: MemorySize, -{ - fn drop(&mut self) { - let futex = { - let mut guard = self.state.futexs.lock().unwrap(); - guard.remove(&self.futex_idx) - }; - if let Some(futex) = futex { - futex.wakers.into_iter().for_each(|w| w.wake()); - } - } -} /// Wait for a futex_wake operation to wake us. /// Returns with EINVAL if the futex doesn't hold the expected value. @@ -133,16 +150,17 @@ pub fn futex_wait( // Create a poller which will register ourselves against // this futex event and check when it has changed + let woken = Arc::new(Mutex::new(false)); let poller = FutexPoller { state: env.state.clone(), - woken: false, + woken: woken.clone(), futex_idx, futex_ptr, expected, - ret_woken, }; + let after = FutexAfter { woken, ret_woken }; // We use asyncify on the poller and potentially go into deep sleep - __asyncify_with_deep_sleep::(ctx, timeout, poller)?; + __asyncify_with_deep_sleep::(ctx, timeout, poller, after)?; Ok(Errno::Success) } diff --git a/lib/wasi/src/syscalls/wasix/proc_fork.rs b/lib/wasi/src/syscalls/wasix/proc_fork.rs index 757a8648941..1209270f26e 100644 --- a/lib/wasi/src/syscalls/wasix/proc_fork.rs +++ b/lib/wasi/src/syscalls/wasix/proc_fork.rs @@ -288,18 +288,22 @@ fn run( module: Module, tasks: Arc, child_handle: WasiThreadHandle, - rewind_state: Option, + rewind_state: Option<(RewindState, Result<(), Errno>)>, ) -> ExitCode { let pid = ctx.data(&store).pid(); let tid = ctx.data(&store).tid(); // If we need to rewind then do so - if let Some(rewind_state) = rewind_state { - let ctx = ctx.env.clone().into_mut(&mut store); + if let Some((mut rewind_state, trigger_res)) = rewind_state { + if let Err(exit_code) = + rewind_state.rewinding_finish::(ctx.env.clone().into_mut(&mut store), trigger_res) + { + return exit_code; + } let res = rewind::( - ctx, - rewind_state.memory_stack.freeze(), - rewind_state.rewind_stack.freeze(), + ctx.env.clone().into_mut(&mut store), + rewind_state.memory_stack, + rewind_state.rewind_stack, rewind_state.store_data, ); if res != Errno::Success { @@ -325,22 +329,20 @@ fn run( Ok(WasiError::DeepSleep(deep)) => { trace!(%pid, %tid, "entered a deep sleep"); - // Extract the memory - let memory = match ctx.data(&store).memory().try_clone(&store) { - Some(m) => m, - None => { - tracing::error!(%pid, %tid, "failed to clone memory before deep sleep"); - return Errno::Noexec.into(); - } - }; - // Create the respawn function let respawn = { let ctx = ctx.clone(); let tasks = tasks.clone(); - let rewind_state = Some(deep.rewind); - move |store, module| { - run::(ctx, store, module, tasks, child_handle, rewind_state); + let rewind_state = deep.rewind; + move |store, module, trigger_res| { + run::( + ctx, + store, + module, + tasks, + child_handle, + Some((rewind_state, trigger_res)), + ); } }; diff --git a/lib/wasi/src/syscalls/wasix/thread_join.rs b/lib/wasi/src/syscalls/wasix/thread_join.rs index 98c503e4813..a28e874d645 100644 --- a/lib/wasi/src/syscalls/wasix/thread_join.rs +++ b/lib/wasi/src/syscalls/wasix/thread_join.rs @@ -26,9 +26,16 @@ pub fn thread_join( ctx, None, async move { - other_thread.join().await; + other_thread + .join() + .await + .map_err(|err| { + err.as_exit_code() + .unwrap_or(ExitCode::Errno(Errno::Unknown)) + }) + .unwrap_or_else(|a| a) }, - |_, _, res| res.map_err(ExitCode::Errno), + |_, _, _| Ok(()), )?; Ok(Errno::Success) } else { diff --git a/lib/wasi/src/syscalls/wasix/thread_sleep.rs b/lib/wasi/src/syscalls/wasix/thread_sleep.rs index 30dfa1c1b85..b3107902050 100644 --- a/lib/wasi/src/syscalls/wasix/thread_sleep.rs +++ b/lib/wasi/src/syscalls/wasix/thread_sleep.rs @@ -46,10 +46,7 @@ pub(crate) fn thread_sleep_internal( "the timeout or signals will wake up this thread even though it waits forever" ) }, - |_, _, res| match res { - Ok(()) => Ok(()), - _ => Ok(()), - }, + |_, _, _| Ok(()), )?; } Ok(Errno::Success) diff --git a/lib/wasi/src/syscalls/wasix/thread_spawn.rs b/lib/wasi/src/syscalls/wasix/thread_spawn.rs index 70b46107d80..e5574e39a36 100644 --- a/lib/wasi/src/syscalls/wasix/thread_spawn.rs +++ b/lib/wasi/src/syscalls/wasix/thread_spawn.rs @@ -149,13 +149,13 @@ pub fn thread_spawn( /// Calls the module fn call_module( - env: WasiFunctionEnv, + ctx: WasiFunctionEnv, mut store: Store, module: Module, tasks: Arc, start_ptr_offset: M::Offset, thread_handle: Arc, - rewind_state: Option, + rewind_state: Option<(RewindState, Result<(), Errno>)>, ) -> u32 { // This function calls into the module let call_module_internal = move |env: &WasiFunctionEnv, store: &mut Store| { @@ -205,12 +205,16 @@ fn call_module( }; // If we need to rewind then do so - if let Some(rewind_state) = rewind_state { - let ctx = env.env.clone().into_mut(&mut store); + if let Some((mut rewind_state, trigger_res)) = rewind_state { + if let Err(exit_code) = + rewind_state.rewinding_finish::(ctx.env.clone().into_mut(&mut store), trigger_res) + { + return exit_code.raw() as u32; + } let res = rewind::( - ctx, - rewind_state.memory_stack.freeze(), - rewind_state.rewind_stack.freeze(), + ctx.env.clone().into_mut(&mut store), + rewind_state.memory_stack, + rewind_state.rewind_stack, rewind_state.store_data, ); if res != Errno::Success { @@ -219,7 +223,7 @@ fn call_module( } // Now invoke the module - let ret = call_module_internal(&env, &mut store); + let ret = call_module_internal(&ctx, &mut store); // If it went to deep sleep then we need to handle that match ret { @@ -232,9 +236,9 @@ fn call_module( // Create the callback that will be invoked when the thread respawns after a deep sleep let rewind = deep.rewind; let respawn = { - let env = env.clone(); + let env = ctx.clone(); let tasks = tasks.clone(); - move |store, module| { + move |store, module, trigger_res| { // Call the thread call_module::( env, @@ -243,13 +247,13 @@ fn call_module( tasks, start_ptr_offset, thread_handle, - Some(rewind), + Some((rewind, trigger_res)), ); } }; /// Spawns the WASM process after a trigger - tasks.resume_wasm_after_poller(Box::new(respawn), store, module, env, deep.work); + tasks.resume_wasm_after_poller(Box::new(respawn), store, module, ctx, deep.work); Errno::Unknown as u32 } } diff --git a/tests/integration/cli/tests/snapshots/snapshot__snapshot_bash_dash.snap b/tests/integration/cli/tests/snapshots/snapshot__snapshot_bash_dash.snap index 48f086210f0..a2f059bd632 100644 --- a/tests/integration/cli/tests/snapshots/snapshot__snapshot_bash_dash.snap +++ b/tests/integration/cli/tests/snapshots/snapshot__snapshot_bash_dash.snap @@ -56,7 +56,7 @@ expression: snapshot "Success": { "stdout": "hi\n", "stderr": "test.wasm-5.1# # # test.wasm-5.1# exit\n", - "exit_code": 78 + "exit_code": 0 } } } From 5ffeb68b348cab9c42f3f00130244682a305466b Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Thu, 30 Mar 2023 13:18:29 +1100 Subject: [PATCH 12/52] Fixes for clippy lints --- lib/wasi/src/lib.rs | 2 +- lib/wasi/src/runtime/task_manager/mod.rs | 6 ++++-- lib/wasi/src/syscalls/mod.rs | 9 +-------- 3 files changed, 6 insertions(+), 11 deletions(-) diff --git a/lib/wasi/src/lib.rs b/lib/wasi/src/lib.rs index 8fe87cfa59d..05eeb6a0939 100644 --- a/lib/wasi/src/lib.rs +++ b/lib/wasi/src/lib.rs @@ -151,7 +151,7 @@ impl RewindState { tracing::error!("failed on rewinding_finish - {}", err); ExitCode::Errno(Errno::Memviolation) })?; - let ret = self.finish.finish(env, &mut store, res); + let ret = self.finish.finish(env, &store, res); if ret.is_ok() { self.memory_stack = get_memory_stack::(env, &mut store) .map_err(|err| { diff --git a/lib/wasi/src/runtime/task_manager/mod.rs b/lib/wasi/src/runtime/task_manager/mod.rs index 5cdd17d0cad..3a108b2057b 100644 --- a/lib/wasi/src/runtime/task_manager/mod.rs +++ b/lib/wasi/src/runtime/task_manager/mod.rs @@ -43,6 +43,8 @@ pub enum TaskResumeAction { Abort, } +pub type WasmResumeTask = dyn FnOnce(Store, Module, Result<(), Errno>) + Send + 'static; + pub type WasmResumeTrigger = dyn FnOnce(Store) -> Pin + Send + 'static>> + Send + Sync; @@ -92,7 +94,7 @@ pub trait VirtualTaskManager: std::fmt::Debug + Send + Sync + 'static { /// After the trigger has successfully completed fn resume_wasm_after_trigger( &self, - task: Box) + Send + 'static>, + task: Box, store: Store, module: Module, trigger: Box, @@ -123,7 +125,7 @@ impl dyn VirtualTaskManager { /// After the poller has successed pub fn resume_wasm_after_poller( &self, - task: Box) + Send + 'static>, + task: Box, store: Store, module: Module, env: WasiFunctionEnv, diff --git a/lib/wasi/src/syscalls/mod.rs b/lib/wasi/src/syscalls/mod.rs index 5e9013bc9f9..955a54878ca 100644 --- a/lib/wasi/src/syscalls/mod.rs +++ b/lib/wasi/src/syscalls/mod.rs @@ -535,14 +535,7 @@ where } } struct Finisher { - after: Option< - Box< - dyn FnOnce(&WasiEnv, &dyn AsStoreRef, Result) -> Result<(), ExitCode> - + Send - + Sync - + 'static, - >, - >, + after: Option>>, ret: Arc>>, } impl RewindPostProcess for Finisher { From 89d3d3e51cdc7724cba30c6e1f952d1e99c389d7 Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Thu, 30 Mar 2023 13:49:01 +1100 Subject: [PATCH 13/52] More linting fixes --- lib/cli/src/commands/run.rs | 7 +++- lib/cli/src/commands/run/wasi.rs | 66 ++++++++++++++++++-------------- 2 files changed, 43 insertions(+), 30 deletions(-) diff --git a/lib/cli/src/commands/run.rs b/lib/cli/src/commands/run.rs index d9639645c52..bce25ed1000 100644 --- a/lib/cli/src/commands/run.rs +++ b/lib/cli/src/commands/run.rs @@ -1,3 +1,4 @@ +use crate::commands::run::wasi::RunInit; use crate::common::get_cache_dir; use crate::logging; use crate::package_source::PackageSource; @@ -195,7 +196,7 @@ impl RunWithPathBuf { instance: Instance, path: &Path, invoke: &str, - args: &Vec, + args: &[String], ) -> Result<()> { let result = Self::invoke_function(store, &instance, path, invoke, args)?; println!( @@ -338,7 +339,9 @@ impl RunWithPathBuf { .enable_deep_sleep = capable_of_deep_sleep; self.inner_module_init(&mut store, &instance)?; - Wasi::run(ctx, store, instance, self.path.clone(), self.invoke.clone(), self.args.clone()) + Wasi::run(RunInit { + ctx, store, instance, path: self.path.clone(), invoke: self.invoke.clone(), args: self.args.clone() + }) } // not WASI _ => { diff --git a/lib/cli/src/commands/run/wasi.rs b/lib/cli/src/commands/run/wasi.rs index 9fa263d1e7b..85a23ccf594 100644 --- a/lib/cli/src/commands/run/wasi.rs +++ b/lib/cli/src/commands/run/wasi.rs @@ -102,6 +102,15 @@ pub struct Wasi { pub deny_multiple_wasi_versions: bool, } +pub struct RunInit { + pub ctx: WasiFunctionEnv, + pub store: Store, + pub instance: Instance, + pub path: PathBuf, + pub invoke: Option, + pub args: Vec, +} + #[allow(dead_code)] impl Wasi { pub fn map_dir(&mut self, alias: &str, target_on_disk: PathBuf) { @@ -242,15 +251,8 @@ impl Wasi { } // Runs the Wasi process - pub fn run( - ctx: WasiFunctionEnv, - store: Store, - instance: Instance, - path: PathBuf, - invoke: Option, - args: Vec, - ) -> Result { - let tasks = ctx.data(&store).tasks().clone(); + pub fn run(run: RunInit) -> Result { + let tasks = run.ctx.data(&run.store).tasks().clone(); // The return value is passed synchronously and will block until the result is returned // this is because the main thread can go into a deep sleep and exit the dedicated thread @@ -259,7 +261,7 @@ impl Wasi { // We run it in a blocking thread as the WASM function may otherwise hold // up the IO operations tasks.task_dedicated(Box::new(move || { - Self::run_with_deep_sleep(ctx, store, instance, path, invoke, args, tx, None); + Self::run_with_deep_sleep(run, tx, None); }))?; rx.recv() .expect("main thread terminated without a result, this normally means a panic occurred within the main thread") @@ -267,16 +269,13 @@ impl Wasi { // Runs the Wasi process (asynchronously) pub fn run_with_deep_sleep( - ctx: WasiFunctionEnv, - mut store: Store, - instance: Instance, - path: PathBuf, - invoke: Option, - args: Vec, + run: RunInit, tx: Sender>, rewind_state: Option<(RewindState, Result<(), Errno>)>, ) { // If we need to rewind then do so + let ctx = run.ctx; + let mut store = run.store; if let Some((mut rewind_state, trigger_res)) = rewind_state { if rewind_state.is_64bit { if let Err(exit_code) = rewind_state @@ -316,13 +315,13 @@ impl Wasi { } // Do we want to invoke a function? - if let Some(ref invoke) = invoke { + if let Some(ref invoke) = run.invoke { let res = RunWithPathBuf::inner_module_invoke_function( &mut store, - instance, - path.as_path(), + run.instance, + run.path.as_path(), invoke, - &args, + &run.args, ) .map(|()| 0); @@ -331,11 +330,20 @@ impl Wasi { tx.send(res).unwrap(); } else { let start: Function = - RunWithPathBuf::try_find_function(&instance, path.as_path(), "_start", &[]) + RunWithPathBuf::try_find_function(&run.instance, run.path.as_path(), "_start", &[]) .unwrap(); let result = start.call(&mut store, &[]); - Self::handle_result(ctx, store, instance, path, invoke, args, result, tx) + Self::handle_result( + ctx, + store, + run.instance, + run.path, + run.invoke, + run.args, + result, + tx, + ) } } @@ -368,12 +376,14 @@ impl Wasi { let ctx = ctx.clone(); move |store, _module, res| { Self::run_with_deep_sleep( - ctx, - store, - instance, - path, - invoke, - args, + RunInit { + ctx, + store, + instance, + path, + invoke, + args, + }, tx, Some((rewind, res)), ); From 0ed9ceda54afe35d93a0c4a12c22ff6583f5233a Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Thu, 30 Mar 2023 15:13:01 +1100 Subject: [PATCH 14/52] More linting fixes --- lib/cli/src/commands/run.rs | 11 ++++--- lib/cli/src/commands/run/wasi.rs | 55 ++++++++++++++------------------ 2 files changed, 31 insertions(+), 35 deletions(-) diff --git a/lib/cli/src/commands/run.rs b/lib/cli/src/commands/run.rs index bce25ed1000..0fe2c43db4f 100644 --- a/lib/cli/src/commands/run.rs +++ b/lib/cli/src/commands/run.rs @@ -1,4 +1,4 @@ -use crate::commands::run::wasi::RunInit; +use crate::commands::run::wasi::RunProperties; use crate::common::get_cache_dir; use crate::logging; use crate::package_source::PackageSource; @@ -339,9 +339,12 @@ impl RunWithPathBuf { .enable_deep_sleep = capable_of_deep_sleep; self.inner_module_init(&mut store, &instance)?; - Wasi::run(RunInit { - ctx, store, instance, path: self.path.clone(), invoke: self.invoke.clone(), args: self.args.clone() - }) + Wasi::run( + RunProperties { + ctx, instance, path: self.path.clone(), invoke: self.invoke.clone(), args: self.args.clone() + }, + store + ) } // not WASI _ => { diff --git a/lib/cli/src/commands/run/wasi.rs b/lib/cli/src/commands/run/wasi.rs index 85a23ccf594..1d5513dc5ad 100644 --- a/lib/cli/src/commands/run/wasi.rs +++ b/lib/cli/src/commands/run/wasi.rs @@ -102,9 +102,8 @@ pub struct Wasi { pub deny_multiple_wasi_versions: bool, } -pub struct RunInit { +pub struct RunProperties { pub ctx: WasiFunctionEnv, - pub store: Store, pub instance: Instance, pub path: PathBuf, pub invoke: Option, @@ -251,8 +250,8 @@ impl Wasi { } // Runs the Wasi process - pub fn run(run: RunInit) -> Result { - let tasks = run.ctx.data(&run.store).tasks().clone(); + pub fn run(run: RunProperties, store: Store) -> Result { + let tasks = run.ctx.data(&store).tasks().clone(); // The return value is passed synchronously and will block until the result is returned // this is because the main thread can go into a deep sleep and exit the dedicated thread @@ -261,7 +260,7 @@ impl Wasi { // We run it in a blocking thread as the WASM function may otherwise hold // up the IO operations tasks.task_dedicated(Box::new(move || { - Self::run_with_deep_sleep(run, tx, None); + Self::run_with_deep_sleep(run, store, tx, None); }))?; rx.recv() .expect("main thread terminated without a result, this normally means a panic occurred within the main thread") @@ -269,13 +268,13 @@ impl Wasi { // Runs the Wasi process (asynchronously) pub fn run_with_deep_sleep( - run: RunInit, + run: RunProperties, + mut store: Store, tx: Sender>, rewind_state: Option<(RewindState, Result<(), Errno>)>, ) { // If we need to rewind then do so let ctx = run.ctx; - let mut store = run.store; if let Some((mut rewind_state, trigger_res)) = rewind_state { if rewind_state.is_64bit { if let Err(exit_code) = rewind_state @@ -335,12 +334,14 @@ impl Wasi { let result = start.call(&mut store, &[]); Self::handle_result( - ctx, + RunProperties { + ctx, + instance: run.instance, + path: run.path, + invoke: run.invoke, + args: run.args, + }, store, - run.instance, - run.path, - run.invoke, - run.args, result, tx, ) @@ -349,15 +350,12 @@ impl Wasi { /// Helper function for handling the result of a Wasi _start function. pub fn handle_result( - ctx: WasiFunctionEnv, + run: RunProperties, mut store: Store, - instance: Instance, - path: PathBuf, - invoke: Option, - args: Vec, result: Result, RuntimeError>, tx: Sender>, ) { + let ctx = run.ctx; let ret: Result = match result { Ok(_) => Ok(0), Err(err) => { @@ -369,24 +367,19 @@ impl Wasi { tracing::trace!(%pid, %tid, "entered a deep sleep"); // Create the respawn function - let module = instance.module().clone(); + let module = run.instance.module().clone(); let tasks = ctx.data(&store).tasks().clone(); let rewind = deep.rewind; let respawn = { - let ctx = ctx.clone(); + let run = RunProperties { + ctx: ctx.clone(), + instance: run.instance, + path: run.path, + invoke: run.invoke, + args: run.args, + }; move |store, _module, res| { - Self::run_with_deep_sleep( - RunInit { - ctx, - store, - instance, - path, - invoke, - args, - }, - tx, - Some((rewind, res)), - ); + Self::run_with_deep_sleep(run, store, tx, Some((rewind, res))); } }; From 525c189a9e2cf0670075c0d79c8f7016c334b474 Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Thu, 30 Mar 2023 16:20:43 +1100 Subject: [PATCH 15/52] Updated all the tests so that there are normal tests without async threads and explciti tests with async threads --- tests/integration/cli/tests/snapshot.rs | 201 ++++++++++++++++-- .../snapshot__snapshot_bash_bash.snap | 3 +- .../snapshot__snapshot_bash_dash.snap | 3 +- .../snapshot__snapshot_bash_echo.snap | 3 +- .../snapshots/snapshot__snapshot_bash_ls.snap | 3 +- .../snapshot__snapshot_bash_pipe.snap | 3 +- .../snapshot__snapshot_bash_python.snap | 3 +- .../snapshots/snapshot__snapshot_catsay.snap | 3 +- .../snapshots/snapshot__snapshot_condvar.snap | 3 +- .../snapshot__snapshot_condvar_async.snap | 24 +++ .../snapshots/snapshot__snapshot_cowsay.snap | 3 +- .../snapshot__snapshot_dash_bash.snap | 3 +- .../snapshot__snapshot_dash_dash.snap | 3 +- .../snapshot__snapshot_dash_dev_urandom.snap | 3 +- .../snapshot__snapshot_dash_dev_zero.snap | 3 +- .../snapshot__snapshot_dash_echo.snap | 3 +- .../snapshot__snapshot_dash_echo_to_cat.snap | 3 +- .../snapshot__snapshot_dash_python.snap | 3 +- ...ot__snapshot_default_file_system_tree.snap | 3 +- .../snapshots/snapshot__snapshot_epoll.snap | 7 +- .../snapshot__snapshot_epoll_async.snap | 25 +++ .../snapshots/snapshot__snapshot_execve.snap | 3 +- .../snapshot__snapshot_execve_async.snap | 31 +++ .../snapshot__snapshot_file_copy.snap | 3 +- .../snapshots/snapshot__snapshot_fork.snap | 3 +- .../snapshot__snapshot_fork_and_exec.snap | 3 +- ...napshot__snapshot_fork_and_exec_async.snap | 30 +++ .../snapshot__snapshot_fork_async.snap | 30 +++ .../snapshot__snapshot_longjump.snap | 3 +- .../snapshot__snapshot_longjump2.snap | 3 +- .../snapshot__snapshot_longjump_fork.snap | 3 +- ...napshot__snapshot_longjump_fork_async.snap | 24 +++ .../snapshot__snapshot_multithreading.snap | 3 +- ...apshot__snapshot_multithreading_async.snap | 24 +++ .../snapshots/snapshot__snapshot_pipes.snap | 3 +- .../snapshot__snapshot_process_spawn.snap | 3 +- ...napshot__snapshot_process_spawn_async.snap | 30 +++ .../snapshots/snapshot__snapshot_quickjs.snap | 3 +- .../snapshots/snapshot__snapshot_signals.snap | 3 +- .../snapshot__snapshot_signals_async.snap | 25 +++ .../snapshots/snapshot__snapshot_sleep.snap | 3 +- .../snapshot__snapshot_sleep_async.snap | 24 +++ ...napshot__snapshot_stdin_stdout_stderr.snap | 3 +- .../snapshot__snapshot_tcp_client.snap | 3 +- .../snapshot__snapshot_thread_locals.snap | 3 +- .../snapshots/snapshot__snapshot_vfork.snap | 3 +- .../snapshot__snapshot_vfork_async.snap | 30 +++ ...pshot__snapshot_web_server_internal-2.snap | 168 +++++++++++++++ ...apshot__snapshot_web_server_internal.snap} | 7 +- 49 files changed, 725 insertions(+), 57 deletions(-) create mode 100644 tests/integration/cli/tests/snapshots/snapshot__snapshot_condvar_async.snap create mode 100644 tests/integration/cli/tests/snapshots/snapshot__snapshot_epoll_async.snap create mode 100644 tests/integration/cli/tests/snapshots/snapshot__snapshot_execve_async.snap create mode 100644 tests/integration/cli/tests/snapshots/snapshot__snapshot_fork_and_exec_async.snap create mode 100644 tests/integration/cli/tests/snapshots/snapshot__snapshot_fork_async.snap create mode 100644 tests/integration/cli/tests/snapshots/snapshot__snapshot_longjump_fork_async.snap create mode 100644 tests/integration/cli/tests/snapshots/snapshot__snapshot_multithreading_async.snap create mode 100644 tests/integration/cli/tests/snapshots/snapshot__snapshot_process_spawn_async.snap create mode 100644 tests/integration/cli/tests/snapshots/snapshot__snapshot_signals_async.snap create mode 100644 tests/integration/cli/tests/snapshots/snapshot__snapshot_sleep_async.snap create mode 100644 tests/integration/cli/tests/snapshots/snapshot__snapshot_vfork_async.snap create mode 100644 tests/integration/cli/tests/snapshots/snapshot__snapshot_web_server_internal-2.snap rename tests/integration/cli/tests/snapshots/{snapshot__snapshot_web_server.snap => snapshot__snapshot_web_server_internal.snap} (93%) diff --git a/tests/integration/cli/tests/snapshot.rs b/tests/integration/cli/tests/snapshot.rs index 13606307f11..5c0599b78dc 100644 --- a/tests/integration/cli/tests/snapshot.rs +++ b/tests/integration/cli/tests/snapshot.rs @@ -40,6 +40,7 @@ pub struct TestSpec { pub debug_output: bool, pub enable_threads: bool, pub enable_network: bool, + pub enable_async_threads: bool, } static WEBC_BASH: &'static [u8] = @@ -110,6 +111,7 @@ impl TestBuilder { debug_output: false, enable_threads: true, enable_network: false, + enable_async_threads: false, }, } } @@ -119,6 +121,11 @@ impl TestBuilder { self } + pub fn with_async_threads(mut self) -> Self { + self.spec.enable_async_threads = true; + self + } + pub fn args, S: AsRef>(mut self, args: I) -> Self { let args = args.into_iter().map(|s| s.as_ref().to_string()); self.spec.cli_args.extend(args); @@ -250,6 +257,10 @@ pub fn run_test_with(spec: TestSpec, code: &[u8], with: RunWith) -> TestResult { } cmd.arg("--allow-multiple-wasi-versions"); + if spec.enable_async_threads == false { + cmd.arg("--no-async-threads"); + } + for pkg in &spec.use_packages { cmd.args(&["--use", &pkg]); } @@ -365,10 +376,12 @@ pub fn build_snapshot(mut spec: TestSpec, code: &[u8]) -> TestSnapshot { spec.clone(), code, Box::new(|mut child| { - child - .wait() - .map_err(|err| err.into()) - .map(|status| status.code().unwrap_or_default()) + child.wait().map_err(|err| err.into()).map(|status| { + match status.code().unwrap_or_default() { + 27 => 0, + status => status, + } + }) }), ); let snapshot = TestSnapshot { spec, result }; @@ -411,6 +424,17 @@ fn test_snapshot_condvar() { assert_json_snapshot!(snapshot); } +#[cfg(not(any(target_env = "musl", target_os = "macos", target_os = "windows")))] +#[test] +fn test_snapshot_condvar_async() { + let snapshot = TestBuilder::new() + .with_name(function!()) + .debug_output(true) + .with_async_threads() + .run_wasm(include_bytes!("./wasm/example-condvar.wasm")); + assert_json_snapshot!(snapshot); +} + // Test that the expected default directories are present. #[cfg(not(any(target_env = "musl", target_os = "macos", target_os = "windows")))] #[test] @@ -446,6 +470,9 @@ fn test_snapshot_cowsay() { assert_json_snapshot!(snapshot); } +// This test is not deterministic because the order of the reads and writes +// is random +/* #[cfg(not(any(target_env = "musl", target_os = "macos", target_os = "windows")))] #[test] fn test_snapshot_epoll() { @@ -454,6 +481,17 @@ fn test_snapshot_epoll() { .run_wasm(include_bytes!("./wasm/example-epoll.wasm")); assert_json_snapshot!(snapshot); } +*/ + +#[cfg(not(any(target_env = "musl", target_os = "macos", target_os = "windows")))] +#[test] +fn test_snapshot_epoll_async() { + let snapshot = TestBuilder::new() + .with_name(function!()) + .with_async_threads() + .run_wasm(include_bytes!("./wasm/example-epoll.wasm")); + assert_json_snapshot!(snapshot); +} #[cfg(not(any(target_env = "musl", target_os = "macos", target_os = "windows")))] #[test] @@ -478,10 +516,31 @@ fn test_snapshot_execve() { assert_json_snapshot!(snapshot); } +#[cfg(not(any(target_env = "musl", target_os = "macos", target_os = "windows")))] +#[test] +fn test_snapshot_execve_async() { + let snapshot = TestBuilder::new() + .with_name(function!()) + .use_coreutils() + .with_async_threads() + .run_wasm(include_bytes!("./wasm/example-execve.wasm")); + assert_json_snapshot!(snapshot); +} + #[cfg(not(any(target_env = "musl", target_os = "macos", target_os = "windows")))] #[test] fn test_snapshot_web_server() { - let with_inner = || { + test_snapshot_web_server_internal(false, 7777) +} + +#[cfg(not(any(target_env = "musl", target_os = "macos", target_os = "windows")))] +#[test] +fn test_snapshot_web_server_async() { + test_snapshot_web_server_internal(true, 7778) +} + +fn test_snapshot_web_server_internal(with_async_threads: bool, port: u16) { + let with_inner = move || { let rt = tokio::runtime::Builder::new_multi_thread() .enable_all() .build()?; @@ -515,9 +574,9 @@ fn test_snapshot_web_server() { }) }; + let url = format!("http://localhost:{}/main.js.size", port); let expected_size = usize::from_str_radix( - String::from_utf8_lossy(http_get("http://localhost:7777/main.js.size", 50)?.as_ref()) - .trim(), + String::from_utf8_lossy(http_get(&url, 50)?.as_ref()).trim(), 10, )?; if expected_size == 0 { @@ -525,9 +584,10 @@ fn test_snapshot_web_server() { } println!("expected_size: {}", expected_size); - let reference_data = http_get("http://localhost:7777/main.js", 0)?; + let url = format!("http://localhost:{}/main.js", port); + let reference_data = http_get(&url, 0)?; for _ in 0..20 { - let test_data = http_get("http://localhost:7777/main.js", 0)?; + let test_data = http_get(&url, 0)?; println!("actual_size: {}", test_data.len()); if expected_size != test_data.len() { @@ -555,20 +615,27 @@ fn test_snapshot_web_server() { ret }; - let snapshot = TestBuilder::new() + let script = format!( + r#" +cat /public/main.js | wc -c > /public/main.js.size +rm -f /cfg/config.toml +/bin/webserver --log-level warn --root /public --port {}"#, + port + ); + let mut builder = TestBuilder::new() .with_name(function!()) .enable_network(true) .include_static_package("sharrattj/static-web-server@1.0.8", WEBC_WEB_SERVER) .include_static_package("sharrattj/wasmer-sh@1.0.63", WEBC_WASMER_SH) .use_coreutils() .use_pkg("sharrattj/wasmer-sh") - .stdin_str( - r#" -cat /public/main.js | wc -c > /public/main.js.size -rm -f /cfg/config.toml -/bin/webserver --log-level warn --root /public --port 7777"#, - ) - .run_wasm_with(include_bytes!("./wasm/dash.wasm"), Box::new(with)); + .stdin_str(script); + + if with_async_threads { + builder = builder.with_async_threads(); + } + + let snapshot = builder.run_wasm_with(include_bytes!("./wasm/dash.wasm"), Box::new(with)); assert_json_snapshot!(snapshot); } @@ -585,6 +652,20 @@ fn test_snapshot_fork_and_exec() { assert_json_snapshot!(snapshot); } +// The ability to fork the current process and run a different image but retain +// the existing open file handles (which is needed for stdin and stdout redirection) +#[cfg(not(target_os = "windows"))] +#[cfg(not(any(target_env = "musl", target_os = "macos", target_os = "windows")))] +#[test] +fn test_snapshot_fork_and_exec_async() { + let snapshot = TestBuilder::new() + .with_name(function!()) + .use_coreutils() + .with_async_threads() + .run_wasm(include_bytes!("./wasm/example-execve.wasm")); + assert_json_snapshot!(snapshot); +} + // longjmp is used by C programs that save and restore the stack at specific // points - this functionality is often used for exception handling #[cfg(not(any(target_env = "musl", target_os = "macos", target_os = "windows")))] @@ -620,6 +701,18 @@ fn test_snapshot_fork() { assert_json_snapshot!(snapshot); } +// Simple fork example that is a crude multi-threading implementation - used by `dash` +#[cfg(not(any(target_env = "musl", target_os = "macos", target_os = "windows")))] +#[test] +fn test_snapshot_fork_async() { + let snapshot = TestBuilder::new() + .with_name(function!()) + .use_coreutils() + .with_async_threads() + .run_wasm(include_bytes!("./wasm/example-fork.wasm")); + assert_json_snapshot!(snapshot); +} + // Uses the `fd_pipe` syscall to create a bidirection pipe with two file // descriptors then forks the process to write and read to this pipe. #[cfg(not(any(target_env = "musl", target_os = "macos", target_os = "windows")))] @@ -645,6 +738,20 @@ fn test_snapshot_longjump_fork() { assert_json_snapshot!(snapshot); } +// Performs a longjmp of a stack that was recorded before the fork. +// This test ensures that the stacks that have been recorded are preserved +// after a fork. +// The behavior is needed for `dash` +#[cfg(not(any(target_env = "musl", target_os = "macos", target_os = "windows")))] +#[test] +fn test_snapshot_longjump_fork_async() { + let snapshot = TestBuilder::new() + .with_name(function!()) + .with_async_threads() + .run_wasm(include_bytes!("./wasm/example-fork-longjmp.wasm")); + assert_json_snapshot!(snapshot); +} + // full multi-threading with shared memory and shared compiled modules #[cfg(not(any(target_env = "musl", target_os = "macos", target_os = "windows")))] #[test] @@ -656,6 +763,18 @@ fn test_snapshot_multithreading() { assert_json_snapshot!(snapshot); } +// full multi-threading with shared memory and shared compiled modules +#[cfg(not(any(target_env = "musl", target_os = "macos", target_os = "windows")))] +#[test] +fn test_snapshot_multithreading_async() { + let snapshot = TestBuilder::new() + .with_name(function!()) + .debug_output(true) + .with_async_threads() + .run_wasm(include_bytes!("./wasm/example-multi-threading.wasm")); + assert_json_snapshot!(snapshot); +} + // full multi-threading with shared memory and shared compiled modules #[cfg(target_os = "linux")] #[cfg(not(any(target_env = "musl", target_os = "macos", target_os = "windows")))] @@ -667,6 +786,18 @@ fn test_snapshot_sleep() { assert_json_snapshot!(snapshot); } +// full multi-threading with shared memory and shared compiled modules +#[cfg(target_os = "linux")] +#[cfg(not(any(target_env = "musl", target_os = "macos", target_os = "windows")))] +#[test] +fn test_snapshot_sleep_async() { + let snapshot = TestBuilder::new() + .with_name(function!()) + .with_async_threads() + .run_wasm(include_bytes!("./wasm/example-sleep.wasm")); + assert_json_snapshot!(snapshot); +} + // Uses `posix_spawn` to launch a sub-process and wait on it to exit #[cfg(not(target_os = "windows"))] #[cfg(not(any(target_env = "musl", target_os = "macos", target_os = "windows")))] @@ -679,6 +810,19 @@ fn test_snapshot_process_spawn() { assert_json_snapshot!(snapshot); } +// Uses `posix_spawn` to launch a sub-process and wait on it to exit +#[cfg(not(target_os = "windows"))] +#[cfg(not(any(target_env = "musl", target_os = "macos", target_os = "windows")))] +#[test] +fn test_snapshot_process_spawn_async() { + let snapshot = TestBuilder::new() + .with_name(function!()) + .use_coreutils() + .with_async_threads() + .run_wasm(include_bytes!("./wasm/example-spawn.wasm")); + assert_json_snapshot!(snapshot); +} + // FIXME: re-enable - hangs on windows and macos // Connects to 8.8.8.8:53 over TCP to verify TCP clients work // #[test] @@ -724,6 +868,19 @@ fn test_snapshot_vfork() { assert_json_snapshot!(snapshot); } +// Tests that lightweight forking that does not copy the memory but retains the +// open file descriptors works correctly. +#[cfg(not(any(target_env = "musl", target_os = "macos", target_os = "windows")))] +#[test] +fn test_snapshot_vfork_async() { + let snapshot = TestBuilder::new() + .with_name(function!()) + .use_coreutils() + .with_async_threads() + .run_wasm(include_bytes!("./wasm/example-vfork.wasm")); + assert_json_snapshot!(snapshot); +} + #[cfg(not(any(target_env = "musl", target_os = "macos", target_os = "windows")))] #[test] fn test_snapshot_signals() { @@ -733,6 +890,16 @@ fn test_snapshot_signals() { assert_json_snapshot!(snapshot); } +#[cfg(not(any(target_env = "musl", target_os = "macos", target_os = "windows")))] +#[test] +fn test_snapshot_signals_async() { + let snapshot = TestBuilder::new() + .with_name(function!()) + .with_async_threads() + .run_wasm(include_bytes!("./wasm/example-signal.wasm")); + assert_json_snapshot!(snapshot); +} + #[cfg(target_os = "linux")] #[cfg(not(any(target_env = "musl", target_os = "macos", target_os = "windows")))] #[test] diff --git a/tests/integration/cli/tests/snapshots/snapshot__snapshot_bash_bash.snap b/tests/integration/cli/tests/snapshots/snapshot__snapshot_bash_bash.snap index d29d4a46db7..9536aee84dc 100644 --- a/tests/integration/cli/tests/snapshots/snapshot__snapshot_bash_bash.snap +++ b/tests/integration/cli/tests/snapshots/snapshot__snapshot_bash_bash.snap @@ -54,7 +54,8 @@ expression: snapshot ], "debug_output": false, "enable_threads": true, - "enable_network": false + "enable_network": false, + "enable_async_threads": false }, "result": { "Success": { diff --git a/tests/integration/cli/tests/snapshots/snapshot__snapshot_bash_dash.snap b/tests/integration/cli/tests/snapshots/snapshot__snapshot_bash_dash.snap index a2f059bd632..b68b82e193d 100644 --- a/tests/integration/cli/tests/snapshots/snapshot__snapshot_bash_dash.snap +++ b/tests/integration/cli/tests/snapshots/snapshot__snapshot_bash_dash.snap @@ -50,7 +50,8 @@ expression: snapshot ], "debug_output": false, "enable_threads": true, - "enable_network": false + "enable_network": false, + "enable_async_threads": false }, "result": { "Success": { diff --git a/tests/integration/cli/tests/snapshots/snapshot__snapshot_bash_echo.snap b/tests/integration/cli/tests/snapshots/snapshot__snapshot_bash_echo.snap index 892da5b1649..b602a0c6c9c 100644 --- a/tests/integration/cli/tests/snapshots/snapshot__snapshot_bash_echo.snap +++ b/tests/integration/cli/tests/snapshots/snapshot__snapshot_bash_echo.snap @@ -23,7 +23,8 @@ expression: snapshot ], "debug_output": false, "enable_threads": true, - "enable_network": false + "enable_network": false, + "enable_async_threads": false }, "result": { "Success": { diff --git a/tests/integration/cli/tests/snapshots/snapshot__snapshot_bash_ls.snap b/tests/integration/cli/tests/snapshots/snapshot__snapshot_bash_ls.snap index 1d688eb6419..f83f11412c7 100644 --- a/tests/integration/cli/tests/snapshots/snapshot__snapshot_bash_ls.snap +++ b/tests/integration/cli/tests/snapshots/snapshot__snapshot_bash_ls.snap @@ -27,7 +27,8 @@ expression: snapshot ], "debug_output": false, "enable_threads": true, - "enable_network": false + "enable_network": false, + "enable_async_threads": false }, "result": { "Success": { diff --git a/tests/integration/cli/tests/snapshots/snapshot__snapshot_bash_pipe.snap b/tests/integration/cli/tests/snapshots/snapshot__snapshot_bash_pipe.snap index 2f8dd8607ed..db6a9bafe32 100644 --- a/tests/integration/cli/tests/snapshots/snapshot__snapshot_bash_pipe.snap +++ b/tests/integration/cli/tests/snapshots/snapshot__snapshot_bash_pipe.snap @@ -41,7 +41,8 @@ expression: snapshot ], "debug_output": false, "enable_threads": true, - "enable_network": false + "enable_network": false, + "enable_async_threads": false }, "result": { "Success": { diff --git a/tests/integration/cli/tests/snapshots/snapshot__snapshot_bash_python.snap b/tests/integration/cli/tests/snapshots/snapshot__snapshot_bash_python.snap index 8ecc184c455..0710d24a56c 100644 --- a/tests/integration/cli/tests/snapshots/snapshot__snapshot_bash_python.snap +++ b/tests/integration/cli/tests/snapshots/snapshot__snapshot_bash_python.snap @@ -70,7 +70,8 @@ expression: snapshot ], "debug_output": false, "enable_threads": true, - "enable_network": false + "enable_network": false, + "enable_async_threads": false }, "result": { "Success": { diff --git a/tests/integration/cli/tests/snapshots/snapshot__snapshot_catsay.snap b/tests/integration/cli/tests/snapshots/snapshot__snapshot_catsay.snap index b37f4e4447e..6fa44a7940e 100644 --- a/tests/integration/cli/tests/snapshots/snapshot__snapshot_catsay.snap +++ b/tests/integration/cli/tests/snapshots/snapshot__snapshot_catsay.snap @@ -19,7 +19,8 @@ expression: snapshot ], "debug_output": false, "enable_threads": true, - "enable_network": false + "enable_network": false, + "enable_async_threads": false }, "result": { "Success": { diff --git a/tests/integration/cli/tests/snapshots/snapshot__snapshot_condvar.snap b/tests/integration/cli/tests/snapshots/snapshot__snapshot_condvar.snap index f58d950e827..fbba103885a 100644 --- a/tests/integration/cli/tests/snapshots/snapshot__snapshot_condvar.snap +++ b/tests/integration/cli/tests/snapshots/snapshot__snapshot_condvar.snap @@ -11,7 +11,8 @@ expression: snapshot "stdin": null, "debug_output": true, "enable_threads": true, - "enable_network": false + "enable_network": false, + "enable_async_threads": false }, "result": { "Success": { diff --git a/tests/integration/cli/tests/snapshots/snapshot__snapshot_condvar_async.snap b/tests/integration/cli/tests/snapshots/snapshot__snapshot_condvar_async.snap new file mode 100644 index 00000000000..3696112e3fc --- /dev/null +++ b/tests/integration/cli/tests/snapshots/snapshot__snapshot_condvar_async.snap @@ -0,0 +1,24 @@ +--- +source: tests/integration/cli/tests/snapshot.rs +expression: snapshot +--- +{ + "spec": { + "name": "snapshot::test_snapshot_condvar_async", + "use_packages": [], + "include_webcs": [], + "cli_args": [], + "stdin": null, + "debug_output": true, + "enable_threads": true, + "enable_network": false, + "enable_async_threads": true + }, + "result": { + "Success": { + "stdout": "condvar1 thread spawn\ncondvar1 thread started\ncondvar1 thread sleep(1sec) start\ncondvar loop\ncondvar wait\ncondvar1 thread sleep(1sec) end\ncondvar1 thread set condition\ncondvar1 thread notify\ncondvar woken\ncondvar parent done\ncondvar1 thread exit\nall done\n", + "stderr": "", + "exit_code": 0 + } + } +} diff --git a/tests/integration/cli/tests/snapshots/snapshot__snapshot_cowsay.snap b/tests/integration/cli/tests/snapshots/snapshot__snapshot_cowsay.snap index cec88387447..df781de3287 100644 --- a/tests/integration/cli/tests/snapshots/snapshot__snapshot_cowsay.snap +++ b/tests/integration/cli/tests/snapshots/snapshot__snapshot_cowsay.snap @@ -17,7 +17,8 @@ expression: snapshot ], "debug_output": false, "enable_threads": true, - "enable_network": false + "enable_network": false, + "enable_async_threads": false }, "result": { "Success": { diff --git a/tests/integration/cli/tests/snapshots/snapshot__snapshot_dash_bash.snap b/tests/integration/cli/tests/snapshots/snapshot__snapshot_dash_bash.snap index 403bda5a745..0a88d7bc8cc 100644 --- a/tests/integration/cli/tests/snapshots/snapshot__snapshot_dash_bash.snap +++ b/tests/integration/cli/tests/snapshots/snapshot__snapshot_dash_bash.snap @@ -50,7 +50,8 @@ expression: snapshot ], "debug_output": false, "enable_threads": true, - "enable_network": false + "enable_network": false, + "enable_async_threads": false }, "result": { "Success": { diff --git a/tests/integration/cli/tests/snapshots/snapshot__snapshot_dash_dash.snap b/tests/integration/cli/tests/snapshots/snapshot__snapshot_dash_dash.snap index 7e46eb51574..c8f2f9d93c7 100644 --- a/tests/integration/cli/tests/snapshots/snapshot__snapshot_dash_dash.snap +++ b/tests/integration/cli/tests/snapshots/snapshot__snapshot_dash_dash.snap @@ -50,7 +50,8 @@ expression: snapshot ], "debug_output": false, "enable_threads": true, - "enable_network": false + "enable_network": false, + "enable_async_threads": false }, "result": { "Success": { diff --git a/tests/integration/cli/tests/snapshots/snapshot__snapshot_dash_dev_urandom.snap b/tests/integration/cli/tests/snapshots/snapshot__snapshot_dash_dev_urandom.snap index 3f97603037d..9d3c8f3740f 100644 --- a/tests/integration/cli/tests/snapshots/snapshot__snapshot_dash_dev_urandom.snap +++ b/tests/integration/cli/tests/snapshots/snapshot__snapshot_dash_dev_urandom.snap @@ -50,7 +50,8 @@ expression: snapshot ], "debug_output": false, "enable_threads": true, - "enable_network": false + "enable_network": false, + "enable_async_threads": false }, "result": { "Success": { diff --git a/tests/integration/cli/tests/snapshots/snapshot__snapshot_dash_dev_zero.snap b/tests/integration/cli/tests/snapshots/snapshot__snapshot_dash_dev_zero.snap index 9aae839fe1b..f340b383c7a 100644 --- a/tests/integration/cli/tests/snapshots/snapshot__snapshot_dash_dev_zero.snap +++ b/tests/integration/cli/tests/snapshots/snapshot__snapshot_dash_dev_zero.snap @@ -39,7 +39,8 @@ expression: snapshot ], "debug_output": false, "enable_threads": true, - "enable_network": false + "enable_network": false, + "enable_async_threads": false }, "result": { "Success": { diff --git a/tests/integration/cli/tests/snapshots/snapshot__snapshot_dash_echo.snap b/tests/integration/cli/tests/snapshots/snapshot__snapshot_dash_echo.snap index aef5c929c58..f98844fa8c8 100644 --- a/tests/integration/cli/tests/snapshots/snapshot__snapshot_dash_echo.snap +++ b/tests/integration/cli/tests/snapshots/snapshot__snapshot_dash_echo.snap @@ -18,7 +18,8 @@ expression: snapshot ], "debug_output": false, "enable_threads": true, - "enable_network": false + "enable_network": false, + "enable_async_threads": false }, "result": { "Success": { diff --git a/tests/integration/cli/tests/snapshots/snapshot__snapshot_dash_echo_to_cat.snap b/tests/integration/cli/tests/snapshots/snapshot__snapshot_dash_echo_to_cat.snap index 53782128179..77210502f2d 100644 --- a/tests/integration/cli/tests/snapshots/snapshot__snapshot_dash_echo_to_cat.snap +++ b/tests/integration/cli/tests/snapshots/snapshot__snapshot_dash_echo_to_cat.snap @@ -35,7 +35,8 @@ expression: snapshot ], "debug_output": false, "enable_threads": true, - "enable_network": false + "enable_network": false, + "enable_async_threads": false }, "result": { "Success": { diff --git a/tests/integration/cli/tests/snapshots/snapshot__snapshot_dash_python.snap b/tests/integration/cli/tests/snapshots/snapshot__snapshot_dash_python.snap index a090e6c8c17..4c2f9ddb2fd 100644 --- a/tests/integration/cli/tests/snapshots/snapshot__snapshot_dash_python.snap +++ b/tests/integration/cli/tests/snapshots/snapshot__snapshot_dash_python.snap @@ -69,7 +69,8 @@ expression: snapshot ], "debug_output": false, "enable_threads": true, - "enable_network": false + "enable_network": false, + "enable_async_threads": false }, "result": { "Success": { diff --git a/tests/integration/cli/tests/snapshots/snapshot__snapshot_default_file_system_tree.snap b/tests/integration/cli/tests/snapshots/snapshot__snapshot_default_file_system_tree.snap index 52eddd561d5..a6ce25f4734 100644 --- a/tests/integration/cli/tests/snapshots/snapshot__snapshot_default_file_system_tree.snap +++ b/tests/integration/cli/tests/snapshots/snapshot__snapshot_default_file_system_tree.snap @@ -13,7 +13,8 @@ expression: snapshot "stdin": null, "debug_output": false, "enable_threads": true, - "enable_network": false + "enable_network": false, + "enable_async_threads": false }, "result": { "Success": { diff --git a/tests/integration/cli/tests/snapshots/snapshot__snapshot_epoll.snap b/tests/integration/cli/tests/snapshots/snapshot__snapshot_epoll.snap index 076d584e5f9..b14e5e15381 100644 --- a/tests/integration/cli/tests/snapshots/snapshot__snapshot_epoll.snap +++ b/tests/integration/cli/tests/snapshots/snapshot__snapshot_epoll.snap @@ -1,6 +1,6 @@ --- source: tests/integration/cli/tests/snapshot.rs -assertion_line: 363 +assertion_line: 479 expression: snapshot --- { @@ -12,11 +12,12 @@ expression: snapshot "stdin": null, "debug_output": false, "enable_threads": true, - "enable_network": false + "enable_network": false, + "enable_async_threads": false }, "result": { "Success": { - "stdout": "EFD_NONBLOCK:4\nsuccess write to efd, write 8 bytes(4)\nsuccess read from efd, read 8 bytes(4)\nsuccess write to efd, write 8 bytes(4)\nsuccess read from efd, read 8 bytes(4)\nsuccess write to efd, write 8 bytes(4)\nsuccess read from efd, read 8 bytes(4)\nsuccess write to efd, write 8 bytes(4)\nsuccess read from efd, read 8 bytes(4)\nsuccess write to efd, write 8 bytes(4)\nsuccess read from efd, read 8 bytes(4)\n", + "stdout": "EFD_NONBLOCK:4\nsuccess write to efd, write 8 bytes(4)\nsuccess read from efd, read 8 bytes(4)\nsuccess write to efd, write 8 bytes(4)\nsuccess read from efd, read 8 bytes(4)\nsuccess read from efd, read 8 bytes(4)\nsuccess write to efd, write 8 bytes(4)\nsuccess read from efd, read 8 bytes(4)\nsuccess write to efd, write 8 bytes(4)\nsuccess read from efd, read 8 bytes(4)\nsuccess write to efd, write 8 bytes(4)\n", "stderr": "", "exit_code": 8 } diff --git a/tests/integration/cli/tests/snapshots/snapshot__snapshot_epoll_async.snap b/tests/integration/cli/tests/snapshots/snapshot__snapshot_epoll_async.snap new file mode 100644 index 00000000000..1f992667aa0 --- /dev/null +++ b/tests/integration/cli/tests/snapshots/snapshot__snapshot_epoll_async.snap @@ -0,0 +1,25 @@ +--- +source: tests/integration/cli/tests/snapshot.rs +assertion_line: 363 +expression: snapshot +--- +{ + "spec": { + "name": "snapshot::test_snapshot_epoll_async", + "use_packages": [], + "include_webcs": [], + "cli_args": [], + "stdin": null, + "debug_output": false, + "enable_threads": true, + "enable_network": false, + "enable_async_threads": true + }, + "result": { + "Success": { + "stdout": "EFD_NONBLOCK:4\nsuccess write to efd, write 8 bytes(4)\nsuccess read from efd, read 8 bytes(4)\nsuccess write to efd, write 8 bytes(4)\nsuccess read from efd, read 8 bytes(4)\nsuccess write to efd, write 8 bytes(4)\nsuccess read from efd, read 8 bytes(4)\nsuccess write to efd, write 8 bytes(4)\nsuccess read from efd, read 8 bytes(4)\nsuccess write to efd, write 8 bytes(4)\nsuccess read from efd, read 8 bytes(4)\n", + "stderr": "", + "exit_code": 8 + } + } +} diff --git a/tests/integration/cli/tests/snapshots/snapshot__snapshot_execve.snap b/tests/integration/cli/tests/snapshots/snapshot__snapshot_execve.snap index 00deec631dc..2e913c746c2 100644 --- a/tests/integration/cli/tests/snapshots/snapshot__snapshot_execve.snap +++ b/tests/integration/cli/tests/snapshots/snapshot__snapshot_execve.snap @@ -18,7 +18,8 @@ expression: snapshot "stdin": null, "debug_output": false, "enable_threads": true, - "enable_network": false + "enable_network": false, + "enable_async_threads": false }, "result": { "Success": { diff --git a/tests/integration/cli/tests/snapshots/snapshot__snapshot_execve_async.snap b/tests/integration/cli/tests/snapshots/snapshot__snapshot_execve_async.snap new file mode 100644 index 00000000000..b8ca5324ce2 --- /dev/null +++ b/tests/integration/cli/tests/snapshots/snapshot__snapshot_execve_async.snap @@ -0,0 +1,31 @@ +--- +source: tests/integration/cli/tests/snapshot.rs +assertion_line: 381 +expression: snapshot +--- +{ + "spec": { + "name": "snapshot::test_snapshot_execve_async", + "use_packages": [ + "sharrattj/coreutils" + ], + "include_webcs": [ + { + "name": "sharrattj/coreutils@1.0.16" + } + ], + "cli_args": [], + "stdin": null, + "debug_output": false, + "enable_threads": true, + "enable_network": false, + "enable_async_threads": true + }, + "result": { + "Success": { + "stdout": "Main program started\nexecve: echo hi-from-child\nhi-from-child\nChild(2) exited with 0\nexecve: echo hi-from-parent\nhi-from-parent\n", + "stderr": "", + "exit_code": 0 + } + } +} diff --git a/tests/integration/cli/tests/snapshots/snapshot__snapshot_file_copy.snap b/tests/integration/cli/tests/snapshots/snapshot__snapshot_file_copy.snap index 6d2791c7f8a..70d731e7e18 100644 --- a/tests/integration/cli/tests/snapshots/snapshot__snapshot_file_copy.snap +++ b/tests/integration/cli/tests/snapshots/snapshot__snapshot_file_copy.snap @@ -24,7 +24,8 @@ expression: snapshot ], "debug_output": false, "enable_threads": true, - "enable_network": false + "enable_network": false, + "enable_async_threads": false }, "result": { "Success": { diff --git a/tests/integration/cli/tests/snapshots/snapshot__snapshot_fork.snap b/tests/integration/cli/tests/snapshots/snapshot__snapshot_fork.snap index 61b794ff7a6..6954ab944de 100644 --- a/tests/integration/cli/tests/snapshots/snapshot__snapshot_fork.snap +++ b/tests/integration/cli/tests/snapshots/snapshot__snapshot_fork.snap @@ -17,7 +17,8 @@ expression: snapshot "stdin": null, "debug_output": false, "enable_threads": true, - "enable_network": false + "enable_network": false, + "enable_async_threads": false }, "result": { "Success": { diff --git a/tests/integration/cli/tests/snapshots/snapshot__snapshot_fork_and_exec.snap b/tests/integration/cli/tests/snapshots/snapshot__snapshot_fork_and_exec.snap index 6c69a9f3ddd..f6516c07d4f 100644 --- a/tests/integration/cli/tests/snapshots/snapshot__snapshot_fork_and_exec.snap +++ b/tests/integration/cli/tests/snapshots/snapshot__snapshot_fork_and_exec.snap @@ -17,7 +17,8 @@ expression: snapshot "stdin": null, "debug_output": false, "enable_threads": true, - "enable_network": false + "enable_network": false, + "enable_async_threads": false }, "result": { "Success": { diff --git a/tests/integration/cli/tests/snapshots/snapshot__snapshot_fork_and_exec_async.snap b/tests/integration/cli/tests/snapshots/snapshot__snapshot_fork_and_exec_async.snap new file mode 100644 index 00000000000..27993eb935f --- /dev/null +++ b/tests/integration/cli/tests/snapshots/snapshot__snapshot_fork_and_exec_async.snap @@ -0,0 +1,30 @@ +--- +source: tests/integration/cli/tests/snapshot.rs +expression: snapshot +--- +{ + "spec": { + "name": "snapshot::test_snapshot_fork_and_exec_async", + "use_packages": [ + "sharrattj/coreutils" + ], + "include_webcs": [ + { + "name": "sharrattj/coreutils@1.0.16" + } + ], + "cli_args": [], + "stdin": null, + "debug_output": false, + "enable_threads": true, + "enable_network": false, + "enable_async_threads": true + }, + "result": { + "Success": { + "stdout": "Main program started\nexecve: echo hi-from-child\nhi-from-child\nChild(2) exited with 0\nexecve: echo hi-from-parent\nhi-from-parent\n", + "stderr": "", + "exit_code": 0 + } + } +} diff --git a/tests/integration/cli/tests/snapshots/snapshot__snapshot_fork_async.snap b/tests/integration/cli/tests/snapshots/snapshot__snapshot_fork_async.snap new file mode 100644 index 00000000000..3aed59af63f --- /dev/null +++ b/tests/integration/cli/tests/snapshots/snapshot__snapshot_fork_async.snap @@ -0,0 +1,30 @@ +--- +source: tests/integration/cli/tests/snapshot.rs +expression: snapshot +--- +{ + "spec": { + "name": "snapshot::test_snapshot_fork_async", + "use_packages": [ + "sharrattj/coreutils" + ], + "include_webcs": [ + { + "name": "sharrattj/coreutils@1.0.16" + } + ], + "cli_args": [], + "stdin": null, + "debug_output": false, + "enable_threads": true, + "enable_network": false, + "enable_async_threads": true + }, + "result": { + "Success": { + "stdout": "Parent has x = 0\nParent memory is good\nChild has x = 2\nChild memory is good\nChild(2) exited with 3\n", + "stderr": "", + "exit_code": 0 + } + } +} diff --git a/tests/integration/cli/tests/snapshots/snapshot__snapshot_longjump.snap b/tests/integration/cli/tests/snapshots/snapshot__snapshot_longjump.snap index 09ce0a8f0e2..cf18a83c61c 100644 --- a/tests/integration/cli/tests/snapshots/snapshot__snapshot_longjump.snap +++ b/tests/integration/cli/tests/snapshots/snapshot__snapshot_longjump.snap @@ -17,7 +17,8 @@ expression: snapshot "stdin": null, "debug_output": false, "enable_threads": true, - "enable_network": false + "enable_network": false, + "enable_async_threads": false }, "result": { "Success": { diff --git a/tests/integration/cli/tests/snapshots/snapshot__snapshot_longjump2.snap b/tests/integration/cli/tests/snapshots/snapshot__snapshot_longjump2.snap index 761a1e59938..fcc54185112 100644 --- a/tests/integration/cli/tests/snapshots/snapshot__snapshot_longjump2.snap +++ b/tests/integration/cli/tests/snapshots/snapshot__snapshot_longjump2.snap @@ -17,7 +17,8 @@ expression: snapshot "stdin": null, "debug_output": false, "enable_threads": true, - "enable_network": false + "enable_network": false, + "enable_async_threads": false }, "result": { "Success": { diff --git a/tests/integration/cli/tests/snapshots/snapshot__snapshot_longjump_fork.snap b/tests/integration/cli/tests/snapshots/snapshot__snapshot_longjump_fork.snap index 4eaa2ea17dc..bdc510b2f6e 100644 --- a/tests/integration/cli/tests/snapshots/snapshot__snapshot_longjump_fork.snap +++ b/tests/integration/cli/tests/snapshots/snapshot__snapshot_longjump_fork.snap @@ -11,7 +11,8 @@ expression: snapshot "stdin": null, "debug_output": false, "enable_threads": true, - "enable_network": false + "enable_network": false, + "enable_async_threads": false }, "result": { "Success": { diff --git a/tests/integration/cli/tests/snapshots/snapshot__snapshot_longjump_fork_async.snap b/tests/integration/cli/tests/snapshots/snapshot__snapshot_longjump_fork_async.snap new file mode 100644 index 00000000000..c1a193c1e6c --- /dev/null +++ b/tests/integration/cli/tests/snapshots/snapshot__snapshot_longjump_fork_async.snap @@ -0,0 +1,24 @@ +--- +source: tests/integration/cli/tests/snapshot.rs +expression: snapshot +--- +{ + "spec": { + "name": "snapshot::test_snapshot_longjump_fork_async", + "use_packages": [], + "include_webcs": [], + "cli_args": [], + "stdin": null, + "debug_output": false, + "enable_threads": true, + "enable_network": false, + "enable_async_threads": true + }, + "result": { + "Success": { + "stdout": "Parent memory is good\nParent memory after longjmp is good\nParent has x = 0\nChild memory is good\nChild memory after longjmp is good\nChild has x = 2\nChild(2) exited with 5\n", + "stderr": "", + "exit_code": 0 + } + } +} diff --git a/tests/integration/cli/tests/snapshots/snapshot__snapshot_multithreading.snap b/tests/integration/cli/tests/snapshots/snapshot__snapshot_multithreading.snap index da0751a6add..07699e741fd 100644 --- a/tests/integration/cli/tests/snapshots/snapshot__snapshot_multithreading.snap +++ b/tests/integration/cli/tests/snapshots/snapshot__snapshot_multithreading.snap @@ -11,7 +11,8 @@ expression: snapshot "stdin": null, "debug_output": true, "enable_threads": true, - "enable_network": false + "enable_network": false, + "enable_async_threads": false }, "result": { "Success": { diff --git a/tests/integration/cli/tests/snapshots/snapshot__snapshot_multithreading_async.snap b/tests/integration/cli/tests/snapshots/snapshot__snapshot_multithreading_async.snap new file mode 100644 index 00000000000..bbd8366544f --- /dev/null +++ b/tests/integration/cli/tests/snapshots/snapshot__snapshot_multithreading_async.snap @@ -0,0 +1,24 @@ +--- +source: tests/integration/cli/tests/snapshot.rs +expression: snapshot +--- +{ + "spec": { + "name": "snapshot::test_snapshot_multithreading_async", + "use_packages": [], + "include_webcs": [], + "cli_args": [], + "stdin": null, + "debug_output": true, + "enable_threads": true, + "enable_network": false, + "enable_async_threads": true + }, + "result": { + "Success": { + "stdout": "thread 1 started\nthread 2 started\nthread 3 started\nthread 4 started\nthread 5 started\nthread 6 started\nthread 7 started\nthread 8 started\nthread 9 started\nwaiting for threads\nthread 1 finished\nthread 2 finished\nthread 3 finished\nthread 4 finished\nthread 5 finished\nthread 6 finished\nthread 7 finished\nthread 8 finished\nthread 9 finished\nall done\n", + "stderr": "", + "exit_code": 0 + } + } +} diff --git a/tests/integration/cli/tests/snapshots/snapshot__snapshot_pipes.snap b/tests/integration/cli/tests/snapshots/snapshot__snapshot_pipes.snap index ae9f3e6e3c1..803cee29464 100644 --- a/tests/integration/cli/tests/snapshots/snapshot__snapshot_pipes.snap +++ b/tests/integration/cli/tests/snapshots/snapshot__snapshot_pipes.snap @@ -17,7 +17,8 @@ expression: snapshot "stdin": null, "debug_output": false, "enable_threads": true, - "enable_network": false + "enable_network": false, + "enable_async_threads": false }, "result": { "Success": { diff --git a/tests/integration/cli/tests/snapshots/snapshot__snapshot_process_spawn.snap b/tests/integration/cli/tests/snapshots/snapshot__snapshot_process_spawn.snap index a7eab67a4d9..b8f7185c7f0 100644 --- a/tests/integration/cli/tests/snapshots/snapshot__snapshot_process_spawn.snap +++ b/tests/integration/cli/tests/snapshots/snapshot__snapshot_process_spawn.snap @@ -17,7 +17,8 @@ expression: snapshot "stdin": null, "debug_output": false, "enable_threads": true, - "enable_network": false + "enable_network": false, + "enable_async_threads": false }, "result": { "Success": { diff --git a/tests/integration/cli/tests/snapshots/snapshot__snapshot_process_spawn_async.snap b/tests/integration/cli/tests/snapshots/snapshot__snapshot_process_spawn_async.snap new file mode 100644 index 00000000000..e8142493368 --- /dev/null +++ b/tests/integration/cli/tests/snapshots/snapshot__snapshot_process_spawn_async.snap @@ -0,0 +1,30 @@ +--- +source: tests/integration/cli/tests/snapshot.rs +expression: snapshot +--- +{ + "spec": { + "name": "snapshot::test_snapshot_process_spawn_async", + "use_packages": [ + "sharrattj/coreutils" + ], + "include_webcs": [ + { + "name": "sharrattj/coreutils@1.0.16" + } + ], + "cli_args": [], + "stdin": null, + "debug_output": false, + "enable_threads": true, + "enable_network": false, + "enable_async_threads": true + }, + "result": { + "Success": { + "stdout": "Child pid: 2\nhi\nChild status 0\n", + "stderr": "", + "exit_code": 0 + } + } +} diff --git a/tests/integration/cli/tests/snapshots/snapshot__snapshot_quickjs.snap b/tests/integration/cli/tests/snapshots/snapshot__snapshot_quickjs.snap index 4d7cb7a4f4c..1659aa2627b 100644 --- a/tests/integration/cli/tests/snapshots/snapshot__snapshot_quickjs.snap +++ b/tests/integration/cli/tests/snapshots/snapshot__snapshot_quickjs.snap @@ -27,7 +27,8 @@ expression: snapshot ], "debug_output": false, "enable_threads": true, - "enable_network": false + "enable_network": false, + "enable_async_threads": false }, "result": { "Success": { diff --git a/tests/integration/cli/tests/snapshots/snapshot__snapshot_signals.snap b/tests/integration/cli/tests/snapshots/snapshot__snapshot_signals.snap index 7810ad28db4..62f0ee96c1e 100644 --- a/tests/integration/cli/tests/snapshots/snapshot__snapshot_signals.snap +++ b/tests/integration/cli/tests/snapshots/snapshot__snapshot_signals.snap @@ -12,7 +12,8 @@ expression: snapshot "stdin": null, "debug_output": false, "enable_threads": true, - "enable_network": false + "enable_network": false, + "enable_async_threads": false }, "result": { "Success": { diff --git a/tests/integration/cli/tests/snapshots/snapshot__snapshot_signals_async.snap b/tests/integration/cli/tests/snapshots/snapshot__snapshot_signals_async.snap new file mode 100644 index 00000000000..f49f7a49908 --- /dev/null +++ b/tests/integration/cli/tests/snapshots/snapshot__snapshot_signals_async.snap @@ -0,0 +1,25 @@ +--- +source: tests/integration/cli/tests/snapshot.rs +assertion_line: 537 +expression: snapshot +--- +{ + "spec": { + "name": "snapshot::test_snapshot_signals_async", + "use_packages": [], + "include_webcs": [], + "cli_args": [], + "stdin": null, + "debug_output": false, + "enable_threads": true, + "enable_network": false, + "enable_async_threads": true + }, + "result": { + "Success": { + "stdout": "received SIGHUP\n", + "stderr": "", + "exit_code": 0 + } + } +} diff --git a/tests/integration/cli/tests/snapshots/snapshot__snapshot_sleep.snap b/tests/integration/cli/tests/snapshots/snapshot__snapshot_sleep.snap index 8b7eaff0ea1..53ef3602f91 100644 --- a/tests/integration/cli/tests/snapshots/snapshot__snapshot_sleep.snap +++ b/tests/integration/cli/tests/snapshots/snapshot__snapshot_sleep.snap @@ -11,7 +11,8 @@ expression: snapshot "stdin": null, "debug_output": false, "enable_threads": true, - "enable_network": false + "enable_network": false, + "enable_async_threads": false }, "result": { "Success": { diff --git a/tests/integration/cli/tests/snapshots/snapshot__snapshot_sleep_async.snap b/tests/integration/cli/tests/snapshots/snapshot__snapshot_sleep_async.snap new file mode 100644 index 00000000000..7afe78822f8 --- /dev/null +++ b/tests/integration/cli/tests/snapshots/snapshot__snapshot_sleep_async.snap @@ -0,0 +1,24 @@ +--- +source: tests/integration/cli/tests/snapshot.rs +expression: snapshot +--- +{ + "spec": { + "name": "snapshot::test_snapshot_sleep_async", + "use_packages": [], + "include_webcs": [], + "cli_args": [], + "stdin": null, + "debug_output": false, + "enable_threads": true, + "enable_network": false, + "enable_async_threads": true + }, + "result": { + "Success": { + "stdout": "", + "stderr": "", + "exit_code": 0 + } + } +} diff --git a/tests/integration/cli/tests/snapshots/snapshot__snapshot_stdin_stdout_stderr.snap b/tests/integration/cli/tests/snapshots/snapshot__snapshot_stdin_stdout_stderr.snap index 14e0b0a4e41..eea7dc81abb 100644 --- a/tests/integration/cli/tests/snapshots/snapshot__snapshot_stdin_stdout_stderr.snap +++ b/tests/integration/cli/tests/snapshots/snapshot__snapshot_stdin_stdout_stderr.snap @@ -19,7 +19,8 @@ expression: snapshot ], "debug_output": false, "enable_threads": true, - "enable_network": false + "enable_network": false, + "enable_async_threads": false }, "result": { "Success": { diff --git a/tests/integration/cli/tests/snapshots/snapshot__snapshot_tcp_client.snap b/tests/integration/cli/tests/snapshots/snapshot__snapshot_tcp_client.snap index 4595e1d8f63..3c75a2eb22a 100644 --- a/tests/integration/cli/tests/snapshots/snapshot__snapshot_tcp_client.snap +++ b/tests/integration/cli/tests/snapshots/snapshot__snapshot_tcp_client.snap @@ -12,7 +12,8 @@ expression: snapshot "cli_args": [], "stdin": null, "debug_output": false, - "enable_threads": true + "enable_threads": true, + "enable_async_threads": false }, "result": { "Success": { diff --git a/tests/integration/cli/tests/snapshots/snapshot__snapshot_thread_locals.snap b/tests/integration/cli/tests/snapshots/snapshot__snapshot_thread_locals.snap index 17041482d40..61cdf11de3d 100644 --- a/tests/integration/cli/tests/snapshots/snapshot__snapshot_thread_locals.snap +++ b/tests/integration/cli/tests/snapshots/snapshot__snapshot_thread_locals.snap @@ -11,7 +11,8 @@ expression: snapshot "stdin": null, "debug_output": false, "enable_threads": true, - "enable_network": false + "enable_network": false, + "enable_async_threads": false }, "result": { "Success": { diff --git a/tests/integration/cli/tests/snapshots/snapshot__snapshot_vfork.snap b/tests/integration/cli/tests/snapshots/snapshot__snapshot_vfork.snap index 4a0d8de7685..ccf048f56f2 100644 --- a/tests/integration/cli/tests/snapshots/snapshot__snapshot_vfork.snap +++ b/tests/integration/cli/tests/snapshots/snapshot__snapshot_vfork.snap @@ -17,7 +17,8 @@ expression: snapshot "stdin": null, "debug_output": false, "enable_threads": true, - "enable_network": false + "enable_network": false, + "enable_async_threads": false }, "result": { "Success": { diff --git a/tests/integration/cli/tests/snapshots/snapshot__snapshot_vfork_async.snap b/tests/integration/cli/tests/snapshots/snapshot__snapshot_vfork_async.snap new file mode 100644 index 00000000000..e5a4ca5952d --- /dev/null +++ b/tests/integration/cli/tests/snapshots/snapshot__snapshot_vfork_async.snap @@ -0,0 +1,30 @@ +--- +source: tests/integration/cli/tests/snapshot.rs +expression: snapshot +--- +{ + "spec": { + "name": "snapshot::test_snapshot_vfork_async", + "use_packages": [ + "sharrattj/coreutils" + ], + "include_webcs": [ + { + "name": "sharrattj/coreutils@1.0.16" + } + ], + "cli_args": [], + "stdin": null, + "debug_output": false, + "enable_threads": true, + "enable_network": false, + "enable_async_threads": true + }, + "result": { + "Success": { + "stdout": "Parent waiting on Child(2)\nChild(2) exited with 0\n", + "stderr": "", + "exit_code": 0 + } + } +} diff --git a/tests/integration/cli/tests/snapshots/snapshot__snapshot_web_server_internal-2.snap b/tests/integration/cli/tests/snapshots/snapshot__snapshot_web_server_internal-2.snap new file mode 100644 index 00000000000..1f81d513394 --- /dev/null +++ b/tests/integration/cli/tests/snapshots/snapshot__snapshot_web_server_internal-2.snap @@ -0,0 +1,168 @@ +--- +source: tests/integration/cli/tests/snapshot.rs +assertion_line: 639 +expression: snapshot +--- +{ + "spec": { + "name": "snapshot::test_snapshot_web_server_internal", + "use_packages": [ + "sharrattj/coreutils", + "sharrattj/wasmer-sh" + ], + "include_webcs": [ + { + "name": "sharrattj/static-web-server@1.0.8" + }, + { + "name": "sharrattj/wasmer-sh@1.0.63" + }, + { + "name": "sharrattj/coreutils@1.0.16" + } + ], + "cli_args": [], + "stdin": [ + 10, + 99, + 97, + 116, + 32, + 47, + 112, + 117, + 98, + 108, + 105, + 99, + 47, + 109, + 97, + 105, + 110, + 46, + 106, + 115, + 32, + 124, + 32, + 119, + 99, + 32, + 45, + 99, + 32, + 62, + 32, + 47, + 112, + 117, + 98, + 108, + 105, + 99, + 47, + 109, + 97, + 105, + 110, + 46, + 106, + 115, + 46, + 115, + 105, + 122, + 101, + 10, + 114, + 109, + 32, + 45, + 102, + 32, + 47, + 99, + 102, + 103, + 47, + 99, + 111, + 110, + 102, + 105, + 103, + 46, + 116, + 111, + 109, + 108, + 10, + 47, + 98, + 105, + 110, + 47, + 119, + 101, + 98, + 115, + 101, + 114, + 118, + 101, + 114, + 32, + 45, + 45, + 108, + 111, + 103, + 45, + 108, + 101, + 118, + 101, + 108, + 32, + 119, + 97, + 114, + 110, + 32, + 45, + 45, + 114, + 111, + 111, + 116, + 32, + 47, + 112, + 117, + 98, + 108, + 105, + 99, + 32, + 45, + 45, + 112, + 111, + 114, + 116, + 32, + 55, + 55, + 55, + 56 + ], + "debug_output": false, + "enable_threads": true, + "enable_network": true, + "enable_async_threads": true + }, + "result": { + "Error": "Command failed: error sending request for url (http://localhost:7778/main.js.size): error trying to connect: tcp connect error: Connection refused (os error 111)\n\nSTDOUT:\n\n\nSTDERR:\n# # # # # \n" + } +} diff --git a/tests/integration/cli/tests/snapshots/snapshot__snapshot_web_server.snap b/tests/integration/cli/tests/snapshots/snapshot__snapshot_web_server_internal.snap similarity index 93% rename from tests/integration/cli/tests/snapshots/snapshot__snapshot_web_server.snap rename to tests/integration/cli/tests/snapshots/snapshot__snapshot_web_server_internal.snap index 7e2eff9f72c..2ebfd15633c 100644 --- a/tests/integration/cli/tests/snapshots/snapshot__snapshot_web_server.snap +++ b/tests/integration/cli/tests/snapshots/snapshot__snapshot_web_server_internal.snap @@ -1,11 +1,11 @@ --- source: tests/integration/cli/tests/snapshot.rs -assertion_line: 492 +assertion_line: 639 expression: snapshot --- { "spec": { - "name": "snapshot::test_snapshot_web_server", + "name": "snapshot::test_snapshot_web_server_internal", "use_packages": [ "sharrattj/coreutils", "sharrattj/wasmer-sh" @@ -159,7 +159,8 @@ expression: snapshot ], "debug_output": false, "enable_threads": true, - "enable_network": true + "enable_network": true, + "enable_async_threads": false }, "result": { "Success": { From 748f6aef283442ea4b2d75e53b84876496779fb7 Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Thu, 30 Mar 2023 16:24:06 +1100 Subject: [PATCH 16/52] Added a comment to retrigger the integration tests --- tests/integration/cli/tests/snapshot.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration/cli/tests/snapshot.rs b/tests/integration/cli/tests/snapshot.rs index 5c0599b78dc..c2cb648a61c 100644 --- a/tests/integration/cli/tests/snapshot.rs +++ b/tests/integration/cli/tests/snapshot.rs @@ -869,7 +869,7 @@ fn test_snapshot_vfork() { } // Tests that lightweight forking that does not copy the memory but retains the -// open file descriptors works correctly. +// open file descriptors works correctly. Uses asynchronous threading #[cfg(not(any(target_env = "musl", target_os = "macos", target_os = "windows")))] #[test] fn test_snapshot_vfork_async() { From d5dece7e3106f9ae82e6e8677a2f65188e9c9b5f Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Fri, 31 Mar 2023 10:30:25 +1100 Subject: [PATCH 17/52] Flips from opt-out to opt-in for asynchronous threading --- lib/cli/src/commands/run/wasi.rs | 8 ++++---- lib/wasi/src/capabilities.rs | 2 +- lib/wasi/src/os/task/control_plane.rs | 10 +++++----- lib/wasi/src/state/builder.rs | 2 +- lib/wasi/src/state/env.rs | 2 +- tests/integration/cli/tests/snapshot.rs | 4 ++-- 6 files changed, 14 insertions(+), 14 deletions(-) diff --git a/lib/cli/src/commands/run/wasi.rs b/lib/cli/src/commands/run/wasi.rs index 1d5513dc5ad..ed6d78b4fee 100644 --- a/lib/cli/src/commands/run/wasi.rs +++ b/lib/cli/src/commands/run/wasi.rs @@ -83,9 +83,9 @@ pub struct Wasi { #[clap(long = "no-tty")] pub no_tty: bool, - /// Disables asynchronous threading - #[clap(long = "no-async-threads")] - pub no_async_threads: bool, + /// Enables asynchronous threading + #[clap(long = "enable-async-threads")] + pub enable_async_threads: bool, /// Allow instances to send http requests. /// @@ -223,7 +223,7 @@ impl Wasi { builder .capabilities_mut() .threading - .disable_asynchronous_threading = self.no_async_threads; + .enable_asynchronous_threading = self.enable_async_threads; #[cfg(feature = "experimental-io-devices")] { diff --git a/lib/wasi/src/capabilities.rs b/lib/wasi/src/capabilities.rs index e031edcf848..1468184e080 100644 --- a/lib/wasi/src/capabilities.rs +++ b/lib/wasi/src/capabilities.rs @@ -34,5 +34,5 @@ pub struct CapabilityThreadingV1 { /// Flag that indicates if asynchronous threading is disabled /// (default = false) - pub disable_asynchronous_threading: bool, + pub enable_asynchronous_threading: bool, } diff --git a/lib/wasi/src/os/task/control_plane.rs b/lib/wasi/src/os/task/control_plane.rs index 76acabba09a..be064ac4c80 100644 --- a/lib/wasi/src/os/task/control_plane.rs +++ b/lib/wasi/src/os/task/control_plane.rs @@ -39,15 +39,15 @@ impl WasiControlPlaneHandle { pub struct ControlPlaneConfig { /// Total number of tasks (processes + threads) that can be spawned. pub max_task_count: Option, - /// Flag that indicates if asynchronous threading is disable (opt-out) - pub disable_asynchronous_threading: bool, + /// Flag that indicates if asynchronous threading is enables (opt-in) + pub enable_asynchronous_threading: bool, } impl ControlPlaneConfig { pub fn new() -> Self { Self { max_task_count: None, - disable_asynchronous_threading: false, + enable_asynchronous_threading: false, } } } @@ -210,7 +210,7 @@ mod tests { fn test_control_plane_task_limits() { let p = WasiControlPlane::new(ControlPlaneConfig { max_task_count: Some(2), - disable_asynchronous_threading: false, + enable_asynchronous_threading: false, }); let p1 = p.new_process().unwrap(); @@ -228,7 +228,7 @@ mod tests { fn test_control_plane_task_limits_with_dropped_threads() { let p = WasiControlPlane::new(ControlPlaneConfig { max_task_count: Some(2), - disable_asynchronous_threading: false, + enable_asynchronous_threading: false, }); let p1 = p.new_process().unwrap(); diff --git a/lib/wasi/src/state/builder.rs b/lib/wasi/src/state/builder.rs index a25f9459196..a3b2917f37a 100644 --- a/lib/wasi/src/state/builder.rs +++ b/lib/wasi/src/state/builder.rs @@ -767,7 +767,7 @@ impl WasiEnvBuilder { let plane_config = ControlPlaneConfig { max_task_count: capabilities.threading.max_threads, - disable_asynchronous_threading: capabilities.threading.disable_asynchronous_threading, + enable_asynchronous_threading: capabilities.threading.enable_asynchronous_threading, }; let control_plane = WasiControlPlane::new(plane_config); diff --git a/lib/wasi/src/state/env.rs b/lib/wasi/src/state/env.rs index a560a75088a..cfdd9e7499f 100644 --- a/lib/wasi/src/state/env.rs +++ b/lib/wasi/src/state/env.rs @@ -379,7 +379,7 @@ impl WasiEnv { /// Returns true if this module is capable of deep sleep /// (needs asyncify to unwind and rewin) pub fn capable_of_deep_sleep(&self) -> bool { - if self.control_plane.config().disable_asynchronous_threading { + if !self.control_plane.config().enable_asynchronous_threading { return false; } let inner = self.inner(); diff --git a/tests/integration/cli/tests/snapshot.rs b/tests/integration/cli/tests/snapshot.rs index c2cb648a61c..13fab45a911 100644 --- a/tests/integration/cli/tests/snapshot.rs +++ b/tests/integration/cli/tests/snapshot.rs @@ -257,8 +257,8 @@ pub fn run_test_with(spec: TestSpec, code: &[u8], with: RunWith) -> TestResult { } cmd.arg("--allow-multiple-wasi-versions"); - if spec.enable_async_threads == false { - cmd.arg("--no-async-threads"); + if spec.enable_async_threads { + cmd.arg("--enable-async-threads"); } for pkg in &spec.use_packages { From 83e1159a97fb5c831c35052c43046d7989a0f1e5 Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Fri, 31 Mar 2023 11:56:03 +1100 Subject: [PATCH 18/52] Removed a redundant function(s) Added documentation for FutexAfter --- lib/wasi/src/bin_factory/exec.rs | 21 ++++++++----------- lib/wasi/src/lib.rs | 5 ----- lib/wasi/src/state/builder.rs | 2 +- lib/wasi/src/syscalls/mod.rs | 1 - lib/wasi/src/syscalls/wasix/futex_wait.rs | 15 +++++++++++++ tests/integration/cli/tests/snapshot.rs | 10 ++++----- .../snapshot__snapshot_epoll_async.snap | 4 ++-- 7 files changed, 31 insertions(+), 27 deletions(-) diff --git a/lib/wasi/src/bin_factory/exec.rs b/lib/wasi/src/bin_factory/exec.rs index 3ea563aec18..cb3c4076c0e 100644 --- a/lib/wasi/src/bin_factory/exec.rs +++ b/lib/wasi/src/bin_factory/exec.rs @@ -8,7 +8,7 @@ use crate::{ use futures::Future; use tracing::*; use wasmer::{Function, FunctionEnvMut, Instance, Memory, Memory32, Memory64, Module, Store}; -use wasmer_wasix_types::wasi::{Errno, ExitCode}; +use wasmer_wasix_types::wasi::Errno; use super::{BinFactory, BinaryPackage, ModuleCache}; use crate::{ @@ -149,7 +149,8 @@ pub fn spawn_exec_module( 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.data(&store), Errno::Noexec.into()); + ctx.data(&store) + .blocking_cleanup(Some(Errno::Noexec.into())); return; } } @@ -174,7 +175,8 @@ pub fn spawn_exec_module( call_module(ctx, store, module, start, None); } else { debug!("wasi[{}]::exec-failed: missing _start function", pid); - finish_module(ctx.data(&store), Errno::Noexec.into()); + ctx.data(&store) + .blocking_cleanup(Some(Errno::Noexec.into())); } } }; @@ -190,11 +192,6 @@ pub fn spawn_exec_module( Ok(join_handle) } -/// Finishes with a module and notifies an errcode -fn finish_module(env: &WasiEnv, err: ExitCode) { - env.blocking_cleanup(Some(err)); -} - /// Calls the module fn call_module( ctx: WasiFunctionEnv, @@ -215,7 +212,7 @@ fn call_module( if let Err(exit_code) = rewind_state .rewinding_finish::(ctx.env.clone().into_mut(&mut store), trigger_res) { - finish_module(ctx.data(&store), exit_code); + ctx.data(&store).blocking_cleanup(Some(exit_code)); return; } let res = rewind::( @@ -225,14 +222,14 @@ fn call_module( rewind_state.store_data, ); if res != Errno::Success { - finish_module(ctx.data(&store), res.into()); + ctx.data(&store).blocking_cleanup(Some(res.into())); return; } } else { if let Err(exit_code) = rewind_state .rewinding_finish::(ctx.env.clone().into_mut(&mut store), trigger_res) { - finish_module(ctx.data(&store), exit_code); + ctx.data(&store).blocking_cleanup(Some(exit_code)); return; } let res = rewind::( @@ -242,7 +239,7 @@ fn call_module( rewind_state.store_data, ); if res != Errno::Success { - finish_module(ctx.data(&store), res.into()); + ctx.data(&store).blocking_cleanup(Some(res.into())); return; } }; diff --git a/lib/wasi/src/lib.rs b/lib/wasi/src/lib.rs index 05eeb6a0939..1356fd1f798 100644 --- a/lib/wasi/src/lib.rs +++ b/lib/wasi/src/lib.rs @@ -866,11 +866,6 @@ fn mem_error_to_wasi(err: MemoryAccessError) -> Errno { } } -fn mem_error_to_wasi_error(err: MemoryAccessError) -> WasiError { - let err = mem_error_to_wasi(err); - WasiError::Exit(err.into()) -} - fn mem_error_to_bus(err: MemoryAccessError) -> BusErrno { match err { MemoryAccessError::HeapOutOfBounds => BusErrno::Memviolation, diff --git a/lib/wasi/src/state/builder.rs b/lib/wasi/src/state/builder.rs index a3b2917f37a..66ae0382a4b 100644 --- a/lib/wasi/src/state/builder.rs +++ b/lib/wasi/src/state/builder.rs @@ -821,7 +821,7 @@ impl WasiEnvBuilder { pub fn instantiate( self, module: Module, - store: &mut Store, + store: &mut impl AsStoreMut, ) -> Result<(Instance, WasiFunctionEnv), WasiRuntimeError> { let init = self.build_init()?; WasiEnv::instantiate(init, module, store) diff --git a/lib/wasi/src/syscalls/mod.rs b/lib/wasi/src/syscalls/mod.rs index 955a54878ca..1e36efa2234 100644 --- a/lib/wasi/src/syscalls/mod.rs +++ b/lib/wasi/src/syscalls/mod.rs @@ -114,7 +114,6 @@ use crate::{ fs_error_into_wasi_err, virtual_file_type_to_wasi_file_type, Fd, InodeVal, Kind, MAX_SYMLINKS, }, - mem_error_to_wasi_error, utils::store::InstanceSnapshot, DeepSleepWork, RewindPostProcess, RewindState, VirtualBusError, WasiInodes, }; diff --git a/lib/wasi/src/syscalls/wasix/futex_wait.rs b/lib/wasi/src/syscalls/wasix/futex_wait.rs index c8f8ce34493..d91aaf096cb 100644 --- a/lib/wasi/src/syscalls/wasix/futex_wait.rs +++ b/lib/wasi/src/syscalls/wasix/futex_wait.rs @@ -73,6 +73,21 @@ where } } +/// The futex after struct will write the response of whether the +/// futex was actually woken or not to the return memory of the syscall +/// callee after the wake event has been triggered. +/// +/// It is encased in this struct so that it can be passed around +/// between threads and execute after the threads are rewound (in an +/// asynchronous threading situation). +/// +/// The same implementation is used for both synchronous and +/// asynchronous threading. +/// +/// It is not possible to include this logic directly in the poller +/// as the poller runs before the stack is rewound and the memory +/// that this writes to is often a pointer to the stack hence a +/// rewind would override whatever is written. struct FutexAfter where M: MemorySize, diff --git a/tests/integration/cli/tests/snapshot.rs b/tests/integration/cli/tests/snapshot.rs index 13fab45a911..94821de5133 100644 --- a/tests/integration/cli/tests/snapshot.rs +++ b/tests/integration/cli/tests/snapshot.rs @@ -376,12 +376,10 @@ pub fn build_snapshot(mut spec: TestSpec, code: &[u8]) -> TestSnapshot { spec.clone(), code, Box::new(|mut child| { - child.wait().map_err(|err| err.into()).map(|status| { - match status.code().unwrap_or_default() { - 27 => 0, - status => status, - } - }) + child + .wait() + .map_err(|err| err.into()) + .map(|status| status.code().unwrap_or_default()) }), ); let snapshot = TestSnapshot { spec, result }; diff --git a/tests/integration/cli/tests/snapshots/snapshot__snapshot_epoll_async.snap b/tests/integration/cli/tests/snapshots/snapshot__snapshot_epoll_async.snap index 1f992667aa0..082946b4585 100644 --- a/tests/integration/cli/tests/snapshots/snapshot__snapshot_epoll_async.snap +++ b/tests/integration/cli/tests/snapshots/snapshot__snapshot_epoll_async.snap @@ -1,6 +1,6 @@ --- source: tests/integration/cli/tests/snapshot.rs -assertion_line: 363 +assertion_line: 491 expression: snapshot --- { @@ -17,7 +17,7 @@ expression: snapshot }, "result": { "Success": { - "stdout": "EFD_NONBLOCK:4\nsuccess write to efd, write 8 bytes(4)\nsuccess read from efd, read 8 bytes(4)\nsuccess write to efd, write 8 bytes(4)\nsuccess read from efd, read 8 bytes(4)\nsuccess write to efd, write 8 bytes(4)\nsuccess read from efd, read 8 bytes(4)\nsuccess write to efd, write 8 bytes(4)\nsuccess read from efd, read 8 bytes(4)\nsuccess write to efd, write 8 bytes(4)\nsuccess read from efd, read 8 bytes(4)\n", + "stdout": "EFD_NONBLOCK:4\nsuccess write to efd, write 8 bytes(4)\nsuccess read from efd, read 8 bytes(4)\nsuccess write to efd, write 8 bytes(4)\nsuccess read from efd, read 8 bytes(4)\nsuccess read from efd, read 8 bytes(4)\nsuccess write to efd, write 8 bytes(4)\nsuccess write to efd, write 8 bytes(4)\nsuccess read from efd, read 8 bytes(4)\nsuccess read from efd, read 8 bytes(4)\nsuccess write to efd, write 8 bytes(4)\n", "stderr": "", "exit_code": 8 } From 6350cb1141268a65c469a86cf6f8cee1bc9a7b18 Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Fri, 7 Apr 2023 13:50:16 +1000 Subject: [PATCH 19/52] Fixed compile errors after merge --- lib/wasi/src/syscalls/wasi/poll_oneoff.rs | 116 ++++++++++++---------- 1 file changed, 62 insertions(+), 54 deletions(-) diff --git a/lib/wasi/src/syscalls/wasi/poll_oneoff.rs b/lib/wasi/src/syscalls/wasi/poll_oneoff.rs index 36d5342e3d5..bf38b5abb83 100644 --- a/lib/wasi/src/syscalls/wasi/poll_oneoff.rs +++ b/lib/wasi/src/syscalls/wasi/poll_oneoff.rs @@ -294,69 +294,77 @@ where fd_guards }; - // If the time is infinite then we omit the time_to_sleep parameter - let asyncify_time = match time_to_sleep { - Duration::ZERO => { - Span::current().record("timeout_ns", "nonblocking"); - Some(Duration::ZERO) - } - Duration::MAX => { - Span::current().record("timeout_ns", "infinite"); - None - } - time => { - Span::current().record("timeout_ns", time.as_millis()); - Some(time) - } - }; - // Block polling the file descriptors - let batch = PollBatch::new(pid, tid, &mut guards); - __asyncify(ctx, asyncify_time, batch)? + PollBatch::new(pid, tid, guards) + }; + + // If the time is infinite then we omit the time_to_sleep parameter + let asyncify_time = match time_to_sleep { + Duration::ZERO => { + Span::current().record("timeout_ns", "nonblocking"); + Some(Duration::ZERO) + } + Duration::MAX => { + Span::current().record("timeout_ns", "infinite"); + None + } + time => { + Span::current().record("timeout_ns", time.as_millis()); + Some(time) + } }; // We use asyncify with a deep sleep to wait on new IO events let res = __asyncify_with_deep_sleep_ext::( ctx, - time_to_sleep, + asyncify_time, batch, move |env, store, res| { let events = res.unwrap_or_else(Err); + // Process the result + match events { + Ok(evts) => { + // If its a timeout then return an event for it + Span::current().record("seen", evts.len()); - // Process the result - match ret { - Ok(evts) => { - // If its a timeout then return an event for it - Span::current().record("seen", evts.len()); - Ok(Ok(evts)) - } - Err(Errno::Timedout) => { - // The timeout has triggerred so lets add that event - if clock_subs.is_empty() { - tracing::warn!("triggered_timeout (without any clock subscriptions)",); - } - let mut evts = Vec::new(); - for (clock_info, userdata) in clock_subs { - let evt = Event { - userdata, - error: Errno::Success, - type_: Eventtype::Clock, - u: EventUnion { clock: 0 }, - }; - Span::current().record( - "seen", - &format!( - "clock(id={},userdata={})", - clock_info.clock_id as u32, evt.userdata - ), - ); - evts.push(evt); + // Process the events + process_events(env, store, evts); + } + Err(Errno::Timedout) => { + // The timeout has triggerred so lets add that event + if clock_subs.is_empty() { + tracing::warn!("triggered_timeout (without any clock subscriptions)",); + } + let mut evts = Vec::new(); + for (clock_info, userdata) in clock_subs { + let evt = Event { + userdata, + error: Errno::Success, + type_: Eventtype::Clock, + u: EventUnion { clock: 0 }, + }; + Span::current().record( + "seen", + &format!( + "clock(id={},userdata={})", + clock_info.clock_id as u32, evt.userdata + ), + ); + evts.push(evt); + } + process_events(env, store, evts); + } + // If nonblocking the Errno::Again needs to be turned into an empty list + Err(Errno::Again) => { + process_events(env, store, Default::default()); + } + // Otherwise process the error + Err(err) => { + tracing::warn!("triggered_timeout (without any clock subscriptions)"); + } } - Ok(Ok(evts)) - } - // If nonblocking the Errno::Again needs to be turned into an empty list - Err(Errno::Again) => Ok(Ok(Default::default())), - // Otherwise process the rror - Err(err) => Ok(Err(err)), - } + Ok(()) + }, + )?; + Ok(Errno::Success) } From eb8cfec8b0cd46efc329f43ff14f7ef8b8eadf56 Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Fri, 7 Apr 2023 14:36:52 +1000 Subject: [PATCH 20/52] Updated the unit tests and fixed many of them --- tests/integration/cli/tests/snapshot.rs | 27 ++- .../snapshot__snapshot_bash_bash.snap | 3 +- .../snapshot__snapshot_bash_dash.snap | 3 +- .../snapshot__snapshot_bash_echo.snap | 3 +- .../snapshots/snapshot__snapshot_bash_ls.snap | 3 +- .../snapshot__snapshot_bash_pipe.snap | 5 +- .../snapshot__snapshot_bash_python.snap | 3 +- .../snapshots/snapshot__snapshot_catsay.snap | 3 +- .../snapshots/snapshot__snapshot_condvar.snap | 3 +- .../snapshot__snapshot_condvar_async.snap | 1 - .../snapshots/snapshot__snapshot_cowsay.snap | 3 +- .../snapshot__snapshot_dash_bash.snap | 3 +- .../snapshot__snapshot_dash_dash.snap | 3 +- .../snapshot__snapshot_dash_dev_urandom.snap | 3 +- .../snapshot__snapshot_dash_dev_zero.snap | 3 +- .../snapshot__snapshot_dash_echo.snap | 3 +- .../snapshot__snapshot_dash_echo_to_cat.snap | 5 +- .../snapshot__snapshot_dash_python.snap | 3 +- ...ot__snapshot_default_file_system_tree.snap | 3 +- .../snapshots/snapshot__snapshot_epoll.snap | 3 +- .../snapshot__snapshot_epoll_async.snap | 3 +- .../snapshots/snapshot__snapshot_execve.snap | 3 +- .../snapshot__snapshot_execve_async.snap | 1 - .../snapshot__snapshot_file_copy.snap | 3 +- .../snapshots/snapshot__snapshot_fork.snap | 3 +- .../snapshot__snapshot_fork_and_exec.snap | 3 +- ...napshot__snapshot_fork_and_exec_async.snap | 1 - .../snapshot__snapshot_fork_async.snap | 1 - .../snapshot__snapshot_longjump.snap | 3 +- .../snapshot__snapshot_longjump2.snap | 3 +- .../snapshot__snapshot_longjump_fork.snap | 3 +- ...napshot__snapshot_longjump_fork_async.snap | 1 - .../snapshot__snapshot_multithreading.snap | 3 +- ...apshot__snapshot_multithreading_async.snap | 1 - .../snapshots/snapshot__snapshot_pipes.snap | 3 +- .../snapshot__snapshot_process_spawn.snap | 3 +- ...napshot__snapshot_process_spawn_async.snap | 1 - .../snapshots/snapshot__snapshot_quickjs.snap | 3 +- .../snapshots/snapshot__snapshot_signals.snap | 3 +- .../snapshot__snapshot_signals_async.snap | 1 - .../snapshots/snapshot__snapshot_sleep.snap | 3 +- .../snapshot__snapshot_sleep_async.snap | 1 - ...napshot__snapshot_stdin_stdout_stderr.snap | 3 +- .../snapshot__snapshot_tcp_client.snap | 2 - .../snapshot__snapshot_thread_locals.snap | 3 +- .../snapshots/snapshot__snapshot_vfork.snap | 3 +- .../snapshot__snapshot_vfork_async.snap | 1 - ...nap => snapshot__snapshot_web_server.snap} | 9 +- .../snapshot__snapshot_web_server_async.snap | 38 ++++ ...pshot__snapshot_web_server_internal-2.snap | 168 ------------------ .../cli/tests/wasm/web-server.wasm | Bin 5266683 -> 5247335 bytes ...22ccedaa-3f96-4de0-b24a-ef48ade8151b.webc} | Bin 5268488 -> 5255997 bytes 52 files changed, 98 insertions(+), 265 deletions(-) rename tests/integration/cli/tests/snapshots/{snapshot__snapshot_web_server_internal.snap => snapshot__snapshot_web_server.snap} (75%) create mode 100644 tests/integration/cli/tests/snapshots/snapshot__snapshot_web_server_async.snap delete mode 100644 tests/integration/cli/tests/snapshots/snapshot__snapshot_web_server_internal-2.snap rename tests/integration/cli/tests/webc/{static-web-server-1.0.8-a241658c-e409-4749-872c-ae8eab142ef0.webc => static-web-server-1.0.92-22ccedaa-3f96-4de0-b24a-ef48ade8151b.webc} (64%) diff --git a/tests/integration/cli/tests/snapshot.rs b/tests/integration/cli/tests/snapshot.rs index d10a095268d..61affcf7908 100644 --- a/tests/integration/cli/tests/snapshot.rs +++ b/tests/integration/cli/tests/snapshot.rs @@ -44,9 +44,15 @@ pub struct TestSpec { pub debug_output: bool, pub enable_threads: bool, pub enable_network: bool, + #[serde(skip_serializing_if = "is_false")] + #[serde(default)] pub enable_async_threads: bool, } +fn is_false(b: &bool) -> bool { + *b == false +} + static WEBC_BASH: &'static [u8] = include_bytes!("./webc/bash-1.0.12-0103d733-1afb-4a56-b0ef-0e124139e996.webc"); static WEBC_COREUTILS_14: &'static [u8] = @@ -57,7 +63,7 @@ static WEBC_DASH: &'static [u8] = include_bytes!("./webc/dash-1.0.16-bd931010-c134-4785-9423-13c0a0d49d90.webc"); static WEBC_PYTHON: &'static [u8] = include_bytes!("./webc/python-0.1.0.webc"); static WEBC_WEB_SERVER: &'static [u8] = - include_bytes!("./webc/static-web-server-1.0.8-a241658c-e409-4749-872c-ae8eab142ef0.webc"); + include_bytes!("./webc/static-web-server-1.0.92-22ccedaa-3f96-4de0-b24a-ef48ade8151b.webc"); static WEBC_WASMER_SH: &'static [u8] = include_bytes!("./webc/wasmer-sh-1.0.63-dd3d67d1-de94-458c-a9ee-caea3b230ccf.webc"); @@ -573,16 +579,22 @@ fn test_snapshot_minimodem_rx() { #[cfg(not(any(target_env = "musl", target_os = "macos", target_os = "windows")))] #[test] fn test_snapshot_web_server() { - test_snapshot_web_server_internal(false, 7777) + let snapshot = test_snapshot_web_server_internal(function!(), false, 7777); + assert_json_snapshot!(snapshot); } #[cfg(not(any(target_env = "musl", target_os = "macos", target_os = "windows")))] #[test] fn test_snapshot_web_server_async() { - test_snapshot_web_server_internal(true, 7778) + let snapshot = test_snapshot_web_server_internal(function!(), true, 7778); + assert_json_snapshot!(snapshot); } -fn test_snapshot_web_server_internal(with_async_threads: bool, port: u16) { +fn test_snapshot_web_server_internal( + name: &str, + with_async_threads: bool, + port: u16, +) -> TestSnapshot { let with_inner = move || { let rt = tokio::runtime::Builder::new_multi_thread() .enable_all() @@ -666,9 +678,9 @@ rm -f /cfg/config.toml port ); let mut builder = TestBuilder::new() - .with_name(function!()) + .with_name(name) .enable_network(true) - .include_static_package("sharrattj/static-web-server@1.0.8", WEBC_WEB_SERVER) + .include_static_package("sharrattj/static-web-server@1.0.92", WEBC_WEB_SERVER) .include_static_package("sharrattj/wasmer-sh@1.0.63", WEBC_WASMER_SH) .use_coreutils() .use_pkg("sharrattj/wasmer-sh") @@ -678,8 +690,7 @@ rm -f /cfg/config.toml builder = builder.with_async_threads(); } - let snapshot = builder.run_wasm_with(include_bytes!("./wasm/dash.wasm"), Box::new(with)); - assert_json_snapshot!(snapshot); + builder.run_wasm_with(include_bytes!("./wasm/dash.wasm"), Box::new(with)) } // The ability to fork the current process and run a different image but retain diff --git a/tests/integration/cli/tests/snapshots/snapshot__snapshot_bash_bash.snap b/tests/integration/cli/tests/snapshots/snapshot__snapshot_bash_bash.snap index 0933da3c62e..28bee404e60 100644 --- a/tests/integration/cli/tests/snapshots/snapshot__snapshot_bash_bash.snap +++ b/tests/integration/cli/tests/snapshots/snapshot__snapshot_bash_bash.snap @@ -21,8 +21,7 @@ expression: snapshot "stdin_hash": "eb83da76ee5fbab28288ecf6385c69db", "debug_output": false, "enable_threads": true, - "enable_network": false, - "enable_async_threads": false + "enable_network": false }, "result": { "Success": { diff --git a/tests/integration/cli/tests/snapshots/snapshot__snapshot_bash_dash.snap b/tests/integration/cli/tests/snapshots/snapshot__snapshot_bash_dash.snap index d8b8db1654b..59a8622936a 100644 --- a/tests/integration/cli/tests/snapshots/snapshot__snapshot_bash_dash.snap +++ b/tests/integration/cli/tests/snapshots/snapshot__snapshot_bash_dash.snap @@ -21,8 +21,7 @@ expression: snapshot "stdin_hash": "702da725f176c20024965c8830b620ae", "debug_output": false, "enable_threads": true, - "enable_network": false, - "enable_async_threads": false + "enable_network": false }, "result": { "Success": { diff --git a/tests/integration/cli/tests/snapshots/snapshot__snapshot_bash_echo.snap b/tests/integration/cli/tests/snapshots/snapshot__snapshot_bash_echo.snap index 85ff352689b..2afe70af1f6 100644 --- a/tests/integration/cli/tests/snapshots/snapshot__snapshot_bash_echo.snap +++ b/tests/integration/cli/tests/snapshots/snapshot__snapshot_bash_echo.snap @@ -11,8 +11,7 @@ expression: snapshot "stdin_hash": "664d0430ee33458602e580520841a2d4", "debug_output": false, "enable_threads": true, - "enable_network": false, - "enable_async_threads": false + "enable_network": false }, "result": { "Success": { diff --git a/tests/integration/cli/tests/snapshots/snapshot__snapshot_bash_ls.snap b/tests/integration/cli/tests/snapshots/snapshot__snapshot_bash_ls.snap index 333cb8eecfb..fd203b50344 100644 --- a/tests/integration/cli/tests/snapshots/snapshot__snapshot_bash_ls.snap +++ b/tests/integration/cli/tests/snapshots/snapshot__snapshot_bash_ls.snap @@ -18,8 +18,7 @@ expression: snapshot "stdin_hash": "ceb5d83a0368a10c813f8e05c42a3a7d", "debug_output": false, "enable_threads": true, - "enable_network": false, - "enable_async_threads": false + "enable_network": false }, "result": { "Success": { diff --git a/tests/integration/cli/tests/snapshots/snapshot__snapshot_bash_pipe.snap b/tests/integration/cli/tests/snapshots/snapshot__snapshot_bash_pipe.snap index 9984f1349c2..a36f562ca8f 100644 --- a/tests/integration/cli/tests/snapshots/snapshot__snapshot_bash_pipe.snap +++ b/tests/integration/cli/tests/snapshots/snapshot__snapshot_bash_pipe.snap @@ -1,6 +1,6 @@ --- source: tests/integration/cli/tests/snapshot.rs -assertion_line: 487 +assertion_line: 1064 expression: snapshot --- { @@ -18,8 +18,7 @@ expression: snapshot "stdin_hash": "d83523479d9f84e9f94230d5aed27347", "debug_output": false, "enable_threads": true, - "enable_network": false, - "enable_async_threads": false + "enable_network": false }, "result": { "Success": { diff --git a/tests/integration/cli/tests/snapshots/snapshot__snapshot_bash_python.snap b/tests/integration/cli/tests/snapshots/snapshot__snapshot_bash_python.snap index 7cc13461e64..9cceb75488c 100644 --- a/tests/integration/cli/tests/snapshots/snapshot__snapshot_bash_python.snap +++ b/tests/integration/cli/tests/snapshots/snapshot__snapshot_bash_python.snap @@ -21,8 +21,7 @@ expression: snapshot "stdin_hash": "bda2b0db976f1724ef16ae1491211d52", "debug_output": false, "enable_threads": true, - "enable_network": false, - "enable_async_threads": false + "enable_network": false }, "result": { "Success": { diff --git a/tests/integration/cli/tests/snapshots/snapshot__snapshot_catsay.snap b/tests/integration/cli/tests/snapshots/snapshot__snapshot_catsay.snap index 08f0762f1c9..f0cabeb39a1 100644 --- a/tests/integration/cli/tests/snapshots/snapshot__snapshot_catsay.snap +++ b/tests/integration/cli/tests/snapshots/snapshot__snapshot_catsay.snap @@ -11,8 +11,7 @@ expression: snapshot "stdin_hash": "ed17ce0ab9af325cceb217a26ee9b8a1", "debug_output": false, "enable_threads": true, - "enable_network": false, - "enable_async_threads": false + "enable_network": false }, "result": { "Success": { diff --git a/tests/integration/cli/tests/snapshots/snapshot__snapshot_condvar.snap b/tests/integration/cli/tests/snapshots/snapshot__snapshot_condvar.snap index 67f1f06de6e..1498588ed76 100644 --- a/tests/integration/cli/tests/snapshots/snapshot__snapshot_condvar.snap +++ b/tests/integration/cli/tests/snapshots/snapshot__snapshot_condvar.snap @@ -10,8 +10,7 @@ expression: snapshot "cli_args": [], "debug_output": true, "enable_threads": true, - "enable_network": false, - "enable_async_threads": false + "enable_network": false }, "result": { "Success": { diff --git a/tests/integration/cli/tests/snapshots/snapshot__snapshot_condvar_async.snap b/tests/integration/cli/tests/snapshots/snapshot__snapshot_condvar_async.snap index 3696112e3fc..8754d7b0df7 100644 --- a/tests/integration/cli/tests/snapshots/snapshot__snapshot_condvar_async.snap +++ b/tests/integration/cli/tests/snapshots/snapshot__snapshot_condvar_async.snap @@ -8,7 +8,6 @@ expression: snapshot "use_packages": [], "include_webcs": [], "cli_args": [], - "stdin": null, "debug_output": true, "enable_threads": true, "enable_network": false, diff --git a/tests/integration/cli/tests/snapshots/snapshot__snapshot_cowsay.snap b/tests/integration/cli/tests/snapshots/snapshot__snapshot_cowsay.snap index f784f8a2b50..d9c328ed373 100644 --- a/tests/integration/cli/tests/snapshots/snapshot__snapshot_cowsay.snap +++ b/tests/integration/cli/tests/snapshots/snapshot__snapshot_cowsay.snap @@ -11,8 +11,7 @@ expression: snapshot "stdin_hash": "0d599f0ec05c3bda8c3b8a68c32a1b47", "debug_output": false, "enable_threads": true, - "enable_network": false, - "enable_async_threads": false + "enable_network": false }, "result": { "Success": { diff --git a/tests/integration/cli/tests/snapshots/snapshot__snapshot_dash_bash.snap b/tests/integration/cli/tests/snapshots/snapshot__snapshot_dash_bash.snap index dc2749e47a5..ef449c3ae00 100644 --- a/tests/integration/cli/tests/snapshots/snapshot__snapshot_dash_bash.snap +++ b/tests/integration/cli/tests/snapshots/snapshot__snapshot_dash_bash.snap @@ -21,8 +21,7 @@ expression: snapshot "stdin_hash": "31837f6a5667cb17dd97f274b20281bb", "debug_output": false, "enable_threads": true, - "enable_network": false, - "enable_async_threads": false + "enable_network": false }, "result": { "Success": { diff --git a/tests/integration/cli/tests/snapshots/snapshot__snapshot_dash_dash.snap b/tests/integration/cli/tests/snapshots/snapshot__snapshot_dash_dash.snap index 0cc29fba35c..1e9ada84624 100644 --- a/tests/integration/cli/tests/snapshots/snapshot__snapshot_dash_dash.snap +++ b/tests/integration/cli/tests/snapshots/snapshot__snapshot_dash_dash.snap @@ -21,8 +21,7 @@ expression: snapshot "stdin_hash": "702da725f176c20024965c8830b620ae", "debug_output": false, "enable_threads": true, - "enable_network": false, - "enable_async_threads": false + "enable_network": false }, "result": { "Success": { diff --git a/tests/integration/cli/tests/snapshots/snapshot__snapshot_dash_dev_urandom.snap b/tests/integration/cli/tests/snapshots/snapshot__snapshot_dash_dev_urandom.snap index 60c1f045a33..7b310a5230a 100644 --- a/tests/integration/cli/tests/snapshots/snapshot__snapshot_dash_dev_urandom.snap +++ b/tests/integration/cli/tests/snapshots/snapshot__snapshot_dash_dev_urandom.snap @@ -18,8 +18,7 @@ expression: snapshot "stdin_hash": "80a57e78213b40607259d3909a44b0cf", "debug_output": false, "enable_threads": true, - "enable_network": false, - "enable_async_threads": false + "enable_network": false }, "result": { "Success": { diff --git a/tests/integration/cli/tests/snapshots/snapshot__snapshot_dash_dev_zero.snap b/tests/integration/cli/tests/snapshots/snapshot__snapshot_dash_dev_zero.snap index 37f84db5a3a..9f3beef9388 100644 --- a/tests/integration/cli/tests/snapshots/snapshot__snapshot_dash_dev_zero.snap +++ b/tests/integration/cli/tests/snapshots/snapshot__snapshot_dash_dev_zero.snap @@ -18,8 +18,7 @@ expression: snapshot "stdin_hash": "a911ff36e7a2d67baba7c3dcb45ac5c9", "debug_output": false, "enable_threads": true, - "enable_network": false, - "enable_async_threads": false + "enable_network": false }, "result": { "Success": { diff --git a/tests/integration/cli/tests/snapshots/snapshot__snapshot_dash_echo.snap b/tests/integration/cli/tests/snapshots/snapshot__snapshot_dash_echo.snap index ee549c6a51f..40bea2ef5d4 100644 --- a/tests/integration/cli/tests/snapshots/snapshot__snapshot_dash_echo.snap +++ b/tests/integration/cli/tests/snapshots/snapshot__snapshot_dash_echo.snap @@ -11,8 +11,7 @@ expression: snapshot "stdin_hash": "f21e64a91c2b4c628da60b9a6ef26c46", "debug_output": false, "enable_threads": true, - "enable_network": false, - "enable_async_threads": false + "enable_network": false }, "result": { "Success": { diff --git a/tests/integration/cli/tests/snapshots/snapshot__snapshot_dash_echo_to_cat.snap b/tests/integration/cli/tests/snapshots/snapshot__snapshot_dash_echo_to_cat.snap index 3bb7b8d8896..3cff6cc3c58 100644 --- a/tests/integration/cli/tests/snapshots/snapshot__snapshot_dash_echo_to_cat.snap +++ b/tests/integration/cli/tests/snapshots/snapshot__snapshot_dash_echo_to_cat.snap @@ -1,6 +1,6 @@ --- source: tests/integration/cli/tests/snapshot.rs -assertion_line: 456 +assertion_line: 976 expression: snapshot --- { @@ -18,8 +18,7 @@ expression: snapshot "stdin_hash": "4a75babacd4e06efe6c6594a2864f61f", "debug_output": false, "enable_threads": true, - "enable_network": false, - "enable_async_threads": false + "enable_network": false }, "result": { "Success": { diff --git a/tests/integration/cli/tests/snapshots/snapshot__snapshot_dash_python.snap b/tests/integration/cli/tests/snapshots/snapshot__snapshot_dash_python.snap index 0af1744d73d..021d098f221 100644 --- a/tests/integration/cli/tests/snapshots/snapshot__snapshot_dash_python.snap +++ b/tests/integration/cli/tests/snapshots/snapshot__snapshot_dash_python.snap @@ -21,8 +21,7 @@ expression: snapshot "stdin_hash": "56c581e32481cdbf1d70227f3c4b1a84", "debug_output": false, "enable_threads": true, - "enable_network": false, - "enable_async_threads": false + "enable_network": false }, "result": { "Success": { diff --git a/tests/integration/cli/tests/snapshots/snapshot__snapshot_default_file_system_tree.snap b/tests/integration/cli/tests/snapshots/snapshot__snapshot_default_file_system_tree.snap index 72234b6610d..9e8be647aea 100644 --- a/tests/integration/cli/tests/snapshots/snapshot__snapshot_default_file_system_tree.snap +++ b/tests/integration/cli/tests/snapshots/snapshot__snapshot_default_file_system_tree.snap @@ -12,8 +12,7 @@ expression: snapshot ], "debug_output": false, "enable_threads": true, - "enable_network": false, - "enable_async_threads": false + "enable_network": false }, "result": { "Success": { diff --git a/tests/integration/cli/tests/snapshots/snapshot__snapshot_epoll.snap b/tests/integration/cli/tests/snapshots/snapshot__snapshot_epoll.snap index f6a1cdd1e8f..38591054816 100644 --- a/tests/integration/cli/tests/snapshots/snapshot__snapshot_epoll.snap +++ b/tests/integration/cli/tests/snapshots/snapshot__snapshot_epoll.snap @@ -11,8 +11,7 @@ expression: snapshot "cli_args": [], "debug_output": false, "enable_threads": true, - "enable_network": false, - "enable_async_threads": false + "enable_network": false }, "result": { "Success": { diff --git a/tests/integration/cli/tests/snapshots/snapshot__snapshot_epoll_async.snap b/tests/integration/cli/tests/snapshots/snapshot__snapshot_epoll_async.snap index 082946b4585..d97c1004284 100644 --- a/tests/integration/cli/tests/snapshots/snapshot__snapshot_epoll_async.snap +++ b/tests/integration/cli/tests/snapshots/snapshot__snapshot_epoll_async.snap @@ -1,6 +1,6 @@ --- source: tests/integration/cli/tests/snapshot.rs -assertion_line: 491 +assertion_line: 521 expression: snapshot --- { @@ -9,7 +9,6 @@ expression: snapshot "use_packages": [], "include_webcs": [], "cli_args": [], - "stdin": null, "debug_output": false, "enable_threads": true, "enable_network": false, diff --git a/tests/integration/cli/tests/snapshots/snapshot__snapshot_execve.snap b/tests/integration/cli/tests/snapshots/snapshot__snapshot_execve.snap index a8b0fb85432..45723256eb6 100644 --- a/tests/integration/cli/tests/snapshots/snapshot__snapshot_execve.snap +++ b/tests/integration/cli/tests/snapshots/snapshot__snapshot_execve.snap @@ -17,8 +17,7 @@ expression: snapshot "cli_args": [], "debug_output": false, "enable_threads": true, - "enable_network": false, - "enable_async_threads": false + "enable_network": false }, "result": { "Success": { diff --git a/tests/integration/cli/tests/snapshots/snapshot__snapshot_execve_async.snap b/tests/integration/cli/tests/snapshots/snapshot__snapshot_execve_async.snap index b8ca5324ce2..c8292814781 100644 --- a/tests/integration/cli/tests/snapshots/snapshot__snapshot_execve_async.snap +++ b/tests/integration/cli/tests/snapshots/snapshot__snapshot_execve_async.snap @@ -15,7 +15,6 @@ expression: snapshot } ], "cli_args": [], - "stdin": null, "debug_output": false, "enable_threads": true, "enable_network": false, diff --git a/tests/integration/cli/tests/snapshots/snapshot__snapshot_file_copy.snap b/tests/integration/cli/tests/snapshots/snapshot__snapshot_file_copy.snap index aac32ca8061..c4b7d09202d 100644 --- a/tests/integration/cli/tests/snapshots/snapshot__snapshot_file_copy.snap +++ b/tests/integration/cli/tests/snapshots/snapshot__snapshot_file_copy.snap @@ -21,8 +21,7 @@ expression: snapshot "stdin_hash": "49f68a5c8493ec2c0bf489821c21fc3b", "debug_output": false, "enable_threads": true, - "enable_network": false, - "enable_async_threads": false + "enable_network": false }, "result": { "Success": { diff --git a/tests/integration/cli/tests/snapshots/snapshot__snapshot_fork.snap b/tests/integration/cli/tests/snapshots/snapshot__snapshot_fork.snap index b45a738342c..da92ed84112 100644 --- a/tests/integration/cli/tests/snapshots/snapshot__snapshot_fork.snap +++ b/tests/integration/cli/tests/snapshots/snapshot__snapshot_fork.snap @@ -16,8 +16,7 @@ expression: snapshot "cli_args": [], "debug_output": false, "enable_threads": true, - "enable_network": false, - "enable_async_threads": false + "enable_network": false }, "result": { "Success": { diff --git a/tests/integration/cli/tests/snapshots/snapshot__snapshot_fork_and_exec.snap b/tests/integration/cli/tests/snapshots/snapshot__snapshot_fork_and_exec.snap index cf406918340..047af398289 100644 --- a/tests/integration/cli/tests/snapshots/snapshot__snapshot_fork_and_exec.snap +++ b/tests/integration/cli/tests/snapshots/snapshot__snapshot_fork_and_exec.snap @@ -16,8 +16,7 @@ expression: snapshot "cli_args": [], "debug_output": false, "enable_threads": true, - "enable_network": false, - "enable_async_threads": false + "enable_network": false }, "result": { "Success": { diff --git a/tests/integration/cli/tests/snapshots/snapshot__snapshot_fork_and_exec_async.snap b/tests/integration/cli/tests/snapshots/snapshot__snapshot_fork_and_exec_async.snap index 27993eb935f..ca5f56d7ecc 100644 --- a/tests/integration/cli/tests/snapshots/snapshot__snapshot_fork_and_exec_async.snap +++ b/tests/integration/cli/tests/snapshots/snapshot__snapshot_fork_and_exec_async.snap @@ -14,7 +14,6 @@ expression: snapshot } ], "cli_args": [], - "stdin": null, "debug_output": false, "enable_threads": true, "enable_network": false, diff --git a/tests/integration/cli/tests/snapshots/snapshot__snapshot_fork_async.snap b/tests/integration/cli/tests/snapshots/snapshot__snapshot_fork_async.snap index 3aed59af63f..bb196296c13 100644 --- a/tests/integration/cli/tests/snapshots/snapshot__snapshot_fork_async.snap +++ b/tests/integration/cli/tests/snapshots/snapshot__snapshot_fork_async.snap @@ -14,7 +14,6 @@ expression: snapshot } ], "cli_args": [], - "stdin": null, "debug_output": false, "enable_threads": true, "enable_network": false, diff --git a/tests/integration/cli/tests/snapshots/snapshot__snapshot_longjump.snap b/tests/integration/cli/tests/snapshots/snapshot__snapshot_longjump.snap index fb8f5c5c3e6..23f04c937ac 100644 --- a/tests/integration/cli/tests/snapshots/snapshot__snapshot_longjump.snap +++ b/tests/integration/cli/tests/snapshots/snapshot__snapshot_longjump.snap @@ -16,8 +16,7 @@ expression: snapshot "cli_args": [], "debug_output": false, "enable_threads": true, - "enable_network": false, - "enable_async_threads": false + "enable_network": false }, "result": { "Success": { diff --git a/tests/integration/cli/tests/snapshots/snapshot__snapshot_longjump2.snap b/tests/integration/cli/tests/snapshots/snapshot__snapshot_longjump2.snap index e1b2dfe9abf..6cecd4fadb5 100644 --- a/tests/integration/cli/tests/snapshots/snapshot__snapshot_longjump2.snap +++ b/tests/integration/cli/tests/snapshots/snapshot__snapshot_longjump2.snap @@ -16,8 +16,7 @@ expression: snapshot "cli_args": [], "debug_output": false, "enable_threads": true, - "enable_network": false, - "enable_async_threads": false + "enable_network": false }, "result": { "Success": { diff --git a/tests/integration/cli/tests/snapshots/snapshot__snapshot_longjump_fork.snap b/tests/integration/cli/tests/snapshots/snapshot__snapshot_longjump_fork.snap index 6223d427f18..8585a2ebaeb 100644 --- a/tests/integration/cli/tests/snapshots/snapshot__snapshot_longjump_fork.snap +++ b/tests/integration/cli/tests/snapshots/snapshot__snapshot_longjump_fork.snap @@ -10,8 +10,7 @@ expression: snapshot "cli_args": [], "debug_output": false, "enable_threads": true, - "enable_network": false, - "enable_async_threads": false + "enable_network": false }, "result": { "Success": { diff --git a/tests/integration/cli/tests/snapshots/snapshot__snapshot_longjump_fork_async.snap b/tests/integration/cli/tests/snapshots/snapshot__snapshot_longjump_fork_async.snap index c1a193c1e6c..788013a98d4 100644 --- a/tests/integration/cli/tests/snapshots/snapshot__snapshot_longjump_fork_async.snap +++ b/tests/integration/cli/tests/snapshots/snapshot__snapshot_longjump_fork_async.snap @@ -8,7 +8,6 @@ expression: snapshot "use_packages": [], "include_webcs": [], "cli_args": [], - "stdin": null, "debug_output": false, "enable_threads": true, "enable_network": false, diff --git a/tests/integration/cli/tests/snapshots/snapshot__snapshot_multithreading.snap b/tests/integration/cli/tests/snapshots/snapshot__snapshot_multithreading.snap index bd828b494a9..bc183c818a3 100644 --- a/tests/integration/cli/tests/snapshots/snapshot__snapshot_multithreading.snap +++ b/tests/integration/cli/tests/snapshots/snapshot__snapshot_multithreading.snap @@ -10,8 +10,7 @@ expression: snapshot "cli_args": [], "debug_output": true, "enable_threads": true, - "enable_network": false, - "enable_async_threads": false + "enable_network": false }, "result": { "Success": { diff --git a/tests/integration/cli/tests/snapshots/snapshot__snapshot_multithreading_async.snap b/tests/integration/cli/tests/snapshots/snapshot__snapshot_multithreading_async.snap index bbd8366544f..0d601547a9d 100644 --- a/tests/integration/cli/tests/snapshots/snapshot__snapshot_multithreading_async.snap +++ b/tests/integration/cli/tests/snapshots/snapshot__snapshot_multithreading_async.snap @@ -8,7 +8,6 @@ expression: snapshot "use_packages": [], "include_webcs": [], "cli_args": [], - "stdin": null, "debug_output": true, "enable_threads": true, "enable_network": false, diff --git a/tests/integration/cli/tests/snapshots/snapshot__snapshot_pipes.snap b/tests/integration/cli/tests/snapshots/snapshot__snapshot_pipes.snap index 6e9a5abc0a8..d725ddc2450 100644 --- a/tests/integration/cli/tests/snapshots/snapshot__snapshot_pipes.snap +++ b/tests/integration/cli/tests/snapshots/snapshot__snapshot_pipes.snap @@ -16,8 +16,7 @@ expression: snapshot "cli_args": [], "debug_output": false, "enable_threads": true, - "enable_network": false, - "enable_async_threads": false + "enable_network": false }, "result": { "Success": { diff --git a/tests/integration/cli/tests/snapshots/snapshot__snapshot_process_spawn.snap b/tests/integration/cli/tests/snapshots/snapshot__snapshot_process_spawn.snap index 8c4706327a0..c3a1e43b81b 100644 --- a/tests/integration/cli/tests/snapshots/snapshot__snapshot_process_spawn.snap +++ b/tests/integration/cli/tests/snapshots/snapshot__snapshot_process_spawn.snap @@ -16,8 +16,7 @@ expression: snapshot "cli_args": [], "debug_output": false, "enable_threads": true, - "enable_network": false, - "enable_async_threads": false + "enable_network": false }, "result": { "Success": { diff --git a/tests/integration/cli/tests/snapshots/snapshot__snapshot_process_spawn_async.snap b/tests/integration/cli/tests/snapshots/snapshot__snapshot_process_spawn_async.snap index e8142493368..944eb23c930 100644 --- a/tests/integration/cli/tests/snapshots/snapshot__snapshot_process_spawn_async.snap +++ b/tests/integration/cli/tests/snapshots/snapshot__snapshot_process_spawn_async.snap @@ -14,7 +14,6 @@ expression: snapshot } ], "cli_args": [], - "stdin": null, "debug_output": false, "enable_threads": true, "enable_network": false, diff --git a/tests/integration/cli/tests/snapshots/snapshot__snapshot_quickjs.snap b/tests/integration/cli/tests/snapshots/snapshot__snapshot_quickjs.snap index b79840900df..7e741056287 100644 --- a/tests/integration/cli/tests/snapshots/snapshot__snapshot_quickjs.snap +++ b/tests/integration/cli/tests/snapshots/snapshot__snapshot_quickjs.snap @@ -11,8 +11,7 @@ expression: snapshot "stdin_hash": "7b1ec18c69fb609403f75c6e673f2d33", "debug_output": false, "enable_threads": true, - "enable_network": false, - "enable_async_threads": false + "enable_network": false }, "result": { "Success": { diff --git a/tests/integration/cli/tests/snapshots/snapshot__snapshot_signals.snap b/tests/integration/cli/tests/snapshots/snapshot__snapshot_signals.snap index 26d847a3fe0..5afbc9b4449 100644 --- a/tests/integration/cli/tests/snapshots/snapshot__snapshot_signals.snap +++ b/tests/integration/cli/tests/snapshots/snapshot__snapshot_signals.snap @@ -11,8 +11,7 @@ expression: snapshot "cli_args": [], "debug_output": false, "enable_threads": true, - "enable_network": false, - "enable_async_threads": false + "enable_network": false }, "result": { "Success": { diff --git a/tests/integration/cli/tests/snapshots/snapshot__snapshot_signals_async.snap b/tests/integration/cli/tests/snapshots/snapshot__snapshot_signals_async.snap index f49f7a49908..4cbd9784bd1 100644 --- a/tests/integration/cli/tests/snapshots/snapshot__snapshot_signals_async.snap +++ b/tests/integration/cli/tests/snapshots/snapshot__snapshot_signals_async.snap @@ -9,7 +9,6 @@ expression: snapshot "use_packages": [], "include_webcs": [], "cli_args": [], - "stdin": null, "debug_output": false, "enable_threads": true, "enable_network": false, diff --git a/tests/integration/cli/tests/snapshots/snapshot__snapshot_sleep.snap b/tests/integration/cli/tests/snapshots/snapshot__snapshot_sleep.snap index c70b2c3594c..175d6d79392 100644 --- a/tests/integration/cli/tests/snapshots/snapshot__snapshot_sleep.snap +++ b/tests/integration/cli/tests/snapshots/snapshot__snapshot_sleep.snap @@ -10,8 +10,7 @@ expression: snapshot "cli_args": [], "debug_output": false, "enable_threads": true, - "enable_network": false, - "enable_async_threads": false + "enable_network": false }, "result": { "Success": { diff --git a/tests/integration/cli/tests/snapshots/snapshot__snapshot_sleep_async.snap b/tests/integration/cli/tests/snapshots/snapshot__snapshot_sleep_async.snap index 7afe78822f8..11d37b9e6f6 100644 --- a/tests/integration/cli/tests/snapshots/snapshot__snapshot_sleep_async.snap +++ b/tests/integration/cli/tests/snapshots/snapshot__snapshot_sleep_async.snap @@ -8,7 +8,6 @@ expression: snapshot "use_packages": [], "include_webcs": [], "cli_args": [], - "stdin": null, "debug_output": false, "enable_threads": true, "enable_network": false, diff --git a/tests/integration/cli/tests/snapshots/snapshot__snapshot_stdin_stdout_stderr.snap b/tests/integration/cli/tests/snapshots/snapshot__snapshot_stdin_stdout_stderr.snap index 0e0163730fb..da1007772d7 100644 --- a/tests/integration/cli/tests/snapshots/snapshot__snapshot_stdin_stdout_stderr.snap +++ b/tests/integration/cli/tests/snapshots/snapshot__snapshot_stdin_stdout_stderr.snap @@ -14,8 +14,7 @@ expression: snapshot "stdin_hash": "6f1ed002ab5595859014ebf0951522d9", "debug_output": false, "enable_threads": true, - "enable_network": false, - "enable_async_threads": false + "enable_network": false }, "result": { "Success": { diff --git a/tests/integration/cli/tests/snapshots/snapshot__snapshot_tcp_client.snap b/tests/integration/cli/tests/snapshots/snapshot__snapshot_tcp_client.snap index 3c75a2eb22a..fbe7f5050dc 100644 --- a/tests/integration/cli/tests/snapshots/snapshot__snapshot_tcp_client.snap +++ b/tests/integration/cli/tests/snapshots/snapshot__snapshot_tcp_client.snap @@ -10,10 +10,8 @@ expression: snapshot "sharrattj/coreutils" ], "cli_args": [], - "stdin": null, "debug_output": false, "enable_threads": true, - "enable_async_threads": false }, "result": { "Success": { diff --git a/tests/integration/cli/tests/snapshots/snapshot__snapshot_thread_locals.snap b/tests/integration/cli/tests/snapshots/snapshot__snapshot_thread_locals.snap index 313fe387de1..0dcb8fee24c 100644 --- a/tests/integration/cli/tests/snapshots/snapshot__snapshot_thread_locals.snap +++ b/tests/integration/cli/tests/snapshots/snapshot__snapshot_thread_locals.snap @@ -10,8 +10,7 @@ expression: snapshot "cli_args": [], "debug_output": false, "enable_threads": true, - "enable_network": false, - "enable_async_threads": false + "enable_network": false }, "result": { "Success": { diff --git a/tests/integration/cli/tests/snapshots/snapshot__snapshot_vfork.snap b/tests/integration/cli/tests/snapshots/snapshot__snapshot_vfork.snap index a24deab16d4..f78e8083eff 100644 --- a/tests/integration/cli/tests/snapshots/snapshot__snapshot_vfork.snap +++ b/tests/integration/cli/tests/snapshots/snapshot__snapshot_vfork.snap @@ -16,8 +16,7 @@ expression: snapshot "cli_args": [], "debug_output": false, "enable_threads": true, - "enable_network": false, - "enable_async_threads": false + "enable_network": false }, "result": { "Success": { diff --git a/tests/integration/cli/tests/snapshots/snapshot__snapshot_vfork_async.snap b/tests/integration/cli/tests/snapshots/snapshot__snapshot_vfork_async.snap index e5a4ca5952d..8964b221da0 100644 --- a/tests/integration/cli/tests/snapshots/snapshot__snapshot_vfork_async.snap +++ b/tests/integration/cli/tests/snapshots/snapshot__snapshot_vfork_async.snap @@ -14,7 +14,6 @@ expression: snapshot } ], "cli_args": [], - "stdin": null, "debug_output": false, "enable_threads": true, "enable_network": false, diff --git a/tests/integration/cli/tests/snapshots/snapshot__snapshot_web_server_internal.snap b/tests/integration/cli/tests/snapshots/snapshot__snapshot_web_server.snap similarity index 75% rename from tests/integration/cli/tests/snapshots/snapshot__snapshot_web_server_internal.snap rename to tests/integration/cli/tests/snapshots/snapshot__snapshot_web_server.snap index 11fe66185af..99b755b8699 100644 --- a/tests/integration/cli/tests/snapshots/snapshot__snapshot_web_server_internal.snap +++ b/tests/integration/cli/tests/snapshots/snapshot__snapshot_web_server.snap @@ -1,18 +1,18 @@ --- source: tests/integration/cli/tests/snapshot.rs -assertion_line: 639 +assertion_line: 583 expression: snapshot --- { "spec": { - "name": "snapshot::test_snapshot_web_server_internal", + "name": "snapshot::test_snapshot_web_server", "use_packages": [ "sharrattj/coreutils", "sharrattj/wasmer-sh" ], "include_webcs": [ { - "name": "sharrattj/static-web-server@1.0.8" + "name": "sharrattj/static-web-server@1.0.92" }, { "name": "sharrattj/wasmer-sh@1.0.63" @@ -25,8 +25,7 @@ expression: snapshot "stdin_hash": "5341c172edb6109ca9aa44b67adc2eeb", "debug_output": false, "enable_threads": true, - "enable_network": true, - "enable_async_threads": false + "enable_network": true }, "result": { "Success": { diff --git a/tests/integration/cli/tests/snapshots/snapshot__snapshot_web_server_async.snap b/tests/integration/cli/tests/snapshots/snapshot__snapshot_web_server_async.snap new file mode 100644 index 00000000000..0ccea752c88 --- /dev/null +++ b/tests/integration/cli/tests/snapshots/snapshot__snapshot_web_server_async.snap @@ -0,0 +1,38 @@ +--- +source: tests/integration/cli/tests/snapshot.rs +assertion_line: 590 +expression: snapshot +--- +{ + "spec": { + "name": "snapshot::test_snapshot_web_server_async", + "use_packages": [ + "sharrattj/coreutils", + "sharrattj/wasmer-sh" + ], + "include_webcs": [ + { + "name": "sharrattj/static-web-server@1.0.92" + }, + { + "name": "sharrattj/wasmer-sh@1.0.63" + }, + { + "name": "sharrattj/coreutils@1.0.16" + } + ], + "cli_args": [], + "stdin_hash": "d565078c0e804a85856a4d8f36cbe9af", + "debug_output": false, + "enable_threads": true, + "enable_network": true, + "enable_async_threads": true + }, + "result": { + "Success": { + "stdout": "", + "stderr": "# # # # ", + "exit_code": 0 + } + } +} diff --git a/tests/integration/cli/tests/snapshots/snapshot__snapshot_web_server_internal-2.snap b/tests/integration/cli/tests/snapshots/snapshot__snapshot_web_server_internal-2.snap deleted file mode 100644 index 1f81d513394..00000000000 --- a/tests/integration/cli/tests/snapshots/snapshot__snapshot_web_server_internal-2.snap +++ /dev/null @@ -1,168 +0,0 @@ ---- -source: tests/integration/cli/tests/snapshot.rs -assertion_line: 639 -expression: snapshot ---- -{ - "spec": { - "name": "snapshot::test_snapshot_web_server_internal", - "use_packages": [ - "sharrattj/coreutils", - "sharrattj/wasmer-sh" - ], - "include_webcs": [ - { - "name": "sharrattj/static-web-server@1.0.8" - }, - { - "name": "sharrattj/wasmer-sh@1.0.63" - }, - { - "name": "sharrattj/coreutils@1.0.16" - } - ], - "cli_args": [], - "stdin": [ - 10, - 99, - 97, - 116, - 32, - 47, - 112, - 117, - 98, - 108, - 105, - 99, - 47, - 109, - 97, - 105, - 110, - 46, - 106, - 115, - 32, - 124, - 32, - 119, - 99, - 32, - 45, - 99, - 32, - 62, - 32, - 47, - 112, - 117, - 98, - 108, - 105, - 99, - 47, - 109, - 97, - 105, - 110, - 46, - 106, - 115, - 46, - 115, - 105, - 122, - 101, - 10, - 114, - 109, - 32, - 45, - 102, - 32, - 47, - 99, - 102, - 103, - 47, - 99, - 111, - 110, - 102, - 105, - 103, - 46, - 116, - 111, - 109, - 108, - 10, - 47, - 98, - 105, - 110, - 47, - 119, - 101, - 98, - 115, - 101, - 114, - 118, - 101, - 114, - 32, - 45, - 45, - 108, - 111, - 103, - 45, - 108, - 101, - 118, - 101, - 108, - 32, - 119, - 97, - 114, - 110, - 32, - 45, - 45, - 114, - 111, - 111, - 116, - 32, - 47, - 112, - 117, - 98, - 108, - 105, - 99, - 32, - 45, - 45, - 112, - 111, - 114, - 116, - 32, - 55, - 55, - 55, - 56 - ], - "debug_output": false, - "enable_threads": true, - "enable_network": true, - "enable_async_threads": true - }, - "result": { - "Error": "Command failed: error sending request for url (http://localhost:7778/main.js.size): error trying to connect: tcp connect error: Connection refused (os error 111)\n\nSTDOUT:\n\n\nSTDERR:\n# # # # # \n" - } -} diff --git a/tests/integration/cli/tests/wasm/web-server.wasm b/tests/integration/cli/tests/wasm/web-server.wasm index 640a3f48c9ea524e66fd2250e39858b7fb02a60e..854b580c5def9e92d6eaf3fd48d1510ef13a9c0e 100755 GIT binary patch delta 970204 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

FM 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%0DWuaQkr&X1QpC;q43@SYJ>+4 zEde_XSwMWjUx=?lbSV>0R`3HrE3zq|zB&iJqsQvICh-`zw9{MXr!i z6Tj+jyHti}iyyp3+Tu?;+>* zHLh>i060jW`NHMleecQh-u%OUsAd`Vtkt9+_G8tJ9X7o;-&kHA-_&386{Ai4HLZ7k z&8BWJS~<3<5sL)cGS4Q-?y5F8@m$b!rV8-=P<%yo2P zC(;;`0qGdm7(=>J$rAX*VrZliAZEYi4`tD?Df^SmaMPL1w^;IzSBzIkzumgRys&ji z=UH9JbzCcuohZagccL&j)#XUi#Z)N!>gxjQvUP$@5w?{fPV+;qifs)$Bk?z5<7xSA z6RgwUjE^SrR@w(51^c99I1&0f<(9msR-h@9$Pzit%CH8NF)9i>8uw%jDTa&*Ob8+s zbVxKp3u#e+=bro@?%ib2+#!N#Gj@6s(K8&6~#-F(v8gS-lxfkt(%Z0+$)>i1EpICHl@EV> zu)Ob#m@!WXf^C@8&e00TZxY1yZ}|zqX1fcTc+1b4@sWuTCw|-i=2-GttTgBPZO4+w zqX`G#JQa+mLaxTueY;<&L|SFCKc8*N_j^dpPP~FUlZ#Kse!58S6N0=@)*XinI{MVV zKg!V`BTxO|UFQ^zFOw%@eT?5x-|_H(JJ|2IqrT%&M9#sn@zb#E^Smbw*lvx==TKu? z3SX@9jy%hj7U0e!&Qf3jplnI5j#=r^K{?xhdLf}RBs?dicxwiFx4p9?(k-s_K+>h0 zE5Wh88}%q*AHJ*oS;@hs7y&6qwqxKwtI}TJ(&-^+jy;Rw=bRYJzZYu{+Ac~^+x?!l zigRE`(l7%MHL$X{dTF&wOfQlnXqnGRsP6Ckrp-D}WWVp>qte6Cu<*o*?69vD5!vB* zrB&w@CeNK&)uV92M}1YZ+IRS<)@4df8|gP=)@X;~p78@;3-i|)KR<|7s-)_^a;cLF z2t00~bO)W@vX8F~5c2ULjjvK(fx#0URi~+uv;lH}C?gℜAD?ZK?VX#~KZ+Ykw+L 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

+2J6 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

^ATGgUCG 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

7S>0=*3K}FDl%g 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

t@Tt z^_43ejyMfzucGfkqhCS2JcV=mbn(}QbKGHC#$|TTRBhxZ2+a23S3p1-Mt*4Aad_#< z`rKotyen+1-DlsnHGA&vYpJ9!Y!Ij`= zUnX}Yd(4_+l*JP~803{K%zmp%Zy2aYx2&m8YQZq26Sh zMKJc$g5x7}XJf8E**A#m#QxuZLhJ~??a9y)!m+Jxo~z3bfV}4ly&&p6H1ifX_k654 zHUGga{R0RaMpB#92Dq}42rjeqzMO|II%tuxQ z#koy5w)G=G-Bw7uM6dVBZQQ~Pf2g+9c#=MZG##a>aGDBAPvr?X$c0fr*MMP{FQ2>M z-S7J?!n2!X>kdLfPjbVrxn6otA$2l&-Tck&S%gVCK-FwW0A%)^GG?$F0o1sM>PMl2 zFj?$%TlL-@r0-2$g;#)=x(G@zdxOPeq13n_HHOP7)cR()*&EG_Z(M#FiA(PJ&QP`1 z7`sy7jO?o5ji-}lZ&$U_+3IZ|{u1;c@C<;}BZCkwO7owR%&;(j^pQ^D$wVH;r0`c!ydG0&%S~(Z#2L*f)fD zn>di~+cQ=w3WXx180u}pKMwT{7e1hZP;WMZ7?<}+Yawswdo9c5trQ?6^>Q6=fdJVl zdxWyYdlg0Pe}U+N|L=Hu3Nq|->!_Mv4_*5gTk?#a<22zG*tGI^^o!Vj_hTwL*+>4 z!du3k52uR@>qmw8Up^~VL6{m;4G#1{I1EN9-Vo~<{sk<#Dqd#I;q6_fwfy%2sdFnk>&gc4VWQ~+7Tf8!j=e;QOj z-l79(YO1#(zW6ZJ`x}B)jl6GKbFc71WC*~+#t3z2?0uq|�>j`JWPA)JCOwhY8mG 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

_dTwL-Cms*$9L!$v_ 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