From 9bc44d49a28be71a5586e0b3afb42a8599b01ea8 Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Sun, 5 Feb 2023 10:16:41 +1100 Subject: [PATCH 1/8] Many performance and memory optimizations - Avoiding a memory copy on all socket reads and writes using a new `copy_from_slice` instead - File openers no longer box the implementation avoiding a memory allocation on all file access - Polling sockets rather than using an async function which significantly reduces locking contention and removes an box operation - Futex now uses wakers rather than broadcasts which makes them more efficient and durable - Converted many async functions into sync functions in vnet - Sleeping no longer allocates memory - Avoiding a number of WasiEnv clones which was impacting performance and memory efficiency --- Cargo.toml | 3 + lib/api/src/js/mem_access.rs | 11 + lib/api/src/sys/mem_access.rs | 11 + lib/vfs/src/empty_fs.rs | 4 +- lib/vfs/src/host_fs.rs | 9 +- lib/vfs/src/lib.rs | 12 +- lib/vfs/src/mem_fs/file_opener.rs | 46 +- lib/vfs/src/mem_fs/filesystem.rs | 10 +- lib/vfs/src/mem_fs/mod.rs | 1 - lib/vfs/src/static_fs.rs | 17 +- lib/vfs/src/tmp_fs.rs | 2 +- lib/vfs/src/union_fs.rs | 14 +- lib/vfs/src/webc_fs.rs | 20 +- lib/vnet/src/lib.rs | 171 +-- lib/wasi-local-networking/src/lib.rs | 762 ++-------- lib/wasi/src/bin_factory/module_cache.rs | 6 +- lib/wasi/src/fs/inode_guard.rs | 267 ++-- lib/wasi/src/fs/mod.rs | 4 +- lib/wasi/src/net/socket.rs | 1262 +++++++++-------- lib/wasi/src/os/task/thread.rs | 42 +- lib/wasi/src/runtime/task_manager/mod.rs | 23 +- lib/wasi/src/runtime/task_manager/tokio.rs | 23 +- lib/wasi/src/state/env.rs | 132 +- lib/wasi/src/state/mod.rs | 13 +- lib/wasi/src/syscalls/legacy/snapshot0.rs | 14 +- lib/wasi/src/syscalls/mod.rs | 358 ++++- lib/wasi/src/syscalls/wasi/fd_close.rs | 2 +- .../src/syscalls/wasi/fd_fdstat_set_flags.rs | 20 - lib/wasi/src/syscalls/wasi/fd_read.rs | 28 +- lib/wasi/src/syscalls/wasi/fd_write.rs | 16 +- lib/wasi/src/syscalls/wasi/poll_oneoff.rs | 143 +- lib/wasi/src/syscalls/wasix/futex_wait.rs | 146 +- lib/wasi/src/syscalls/wasix/futex_wake.rs | 17 +- lib/wasi/src/syscalls/wasix/futex_wake_all.rs | 18 +- lib/wasi/src/syscalls/wasix/port_addr_add.rs | 3 +- .../src/syscalls/wasix/port_addr_clear.rs | 4 +- lib/wasi/src/syscalls/wasix/port_addr_list.rs | 4 +- .../src/syscalls/wasix/port_addr_remove.rs | 4 +- .../src/syscalls/wasix/port_gateway_set.rs | 4 +- lib/wasi/src/syscalls/wasix/port_mac.rs | 4 +- lib/wasi/src/syscalls/wasix/port_route_add.rs | 3 +- .../src/syscalls/wasix/port_route_clear.rs | 4 +- .../src/syscalls/wasix/port_route_list.rs | 4 +- .../src/syscalls/wasix/port_route_remove.rs | 4 +- lib/wasi/src/syscalls/wasix/proc_exec.rs | 2 +- lib/wasi/src/syscalls/wasix/sched_yield.rs | 9 +- lib/wasi/src/syscalls/wasix/sock_accept.rs | 32 +- .../src/syscalls/wasix/sock_addr_local.rs | 2 +- lib/wasi/src/syscalls/wasix/sock_addr_peer.rs | 2 +- lib/wasi/src/syscalls/wasix/sock_bind.rs | 5 +- lib/wasi/src/syscalls/wasix/sock_connect.rs | 4 +- .../src/syscalls/wasix/sock_get_opt_flag.rs | 2 +- .../src/syscalls/wasix/sock_get_opt_size.rs | 16 +- .../src/syscalls/wasix/sock_get_opt_time.rs | 14 +- .../syscalls/wasix/sock_join_multicast_v4.rs | 2 +- .../syscalls/wasix/sock_join_multicast_v6.rs | 2 +- .../syscalls/wasix/sock_leave_multicast_v4.rs | 2 +- .../syscalls/wasix/sock_leave_multicast_v6.rs | 2 +- lib/wasi/src/syscalls/wasix/sock_listen.rs | 5 +- lib/wasi/src/syscalls/wasix/sock_open.rs | 7 +- lib/wasi/src/syscalls/wasix/sock_recv.rs | 68 +- lib/wasi/src/syscalls/wasix/sock_recv_from.rs | 69 +- lib/wasi/src/syscalls/wasix/sock_send.rs | 52 +- lib/wasi/src/syscalls/wasix/sock_send_file.rs | 30 +- lib/wasi/src/syscalls/wasix/sock_send_to.rs | 60 +- .../src/syscalls/wasix/sock_set_opt_flag.rs | 2 +- .../src/syscalls/wasix/sock_set_opt_size.rs | 26 +- .../src/syscalls/wasix/sock_set_opt_time.rs | 14 +- lib/wasi/src/syscalls/wasix/sock_shutdown.rs | 2 +- lib/wasi/src/syscalls/wasix/sock_status.rs | 2 +- lib/wasi/src/syscalls/wasix/thread_sleep.rs | 7 + 71 files changed, 2004 insertions(+), 2101 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c63543bb98e..c793ccf4c9c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -135,6 +135,9 @@ coverage = [] [profile.dev] split-debuginfo = "unpacked" +#[profile.release] +#debug = true + [[bench]] name = "static_and_dynamic_functions" harness = false diff --git a/lib/api/src/js/mem_access.rs b/lib/api/src/js/mem_access.rs index 5a9142e48e4..de82f5c6e1c 100644 --- a/lib/api/src/js/mem_access.rs +++ b/lib/api/src/js/mem_access.rs @@ -311,6 +311,17 @@ impl<'a, T: ValueType> WasmSlice<'a, T> { self.buffer.write(self.offset, bytes) } + /// Reads this `WasmSlice` into a `slice`. + #[inline] + pub fn read_to_slice<'b>( + self, + buf: &'b mut [MaybeUninit], + ) -> Result { + let len = self.len.try_into().expect("WasmSlice length overflow"); + self.buffer.read_uninit(self.offset, buf)?; + Ok(len) + } + /// Reads this `WasmSlice` into a `Vec`. #[inline] pub fn read_to_vec(self) -> Result, MemoryAccessError> { diff --git a/lib/api/src/sys/mem_access.rs b/lib/api/src/sys/mem_access.rs index 8bceda732e9..da52a5b9aab 100644 --- a/lib/api/src/sys/mem_access.rs +++ b/lib/api/src/sys/mem_access.rs @@ -313,6 +313,17 @@ impl<'a, T: ValueType> WasmSlice<'a, T> { self.buffer.write(self.offset, bytes) } + /// Reads this `WasmSlice` into a `slice`. + #[inline] + pub fn copy_to_slice<'b>( + self, + buf: &'b mut [MaybeUninit], + ) -> Result { + let len = self.len.try_into().expect("WasmSlice length overflow"); + self.buffer.read_uninit(self.offset, buf)?; + Ok(len) + } + /// Reads this `WasmSlice` into a `Vec`. #[inline] pub fn read_to_vec(self) -> Result, MemoryAccessError> { diff --git a/lib/vfs/src/empty_fs.rs b/lib/vfs/src/empty_fs.rs index 32e68fd89c7..e046f6307e6 100644 --- a/lib/vfs/src/empty_fs.rs +++ b/lib/vfs/src/empty_fs.rs @@ -41,14 +41,14 @@ impl FileSystem for EmptyFileSystem { } fn new_open_options(&self) -> OpenOptions { - OpenOptions::new(Box::new(EmptyFileSystem::default())) + OpenOptions::new(self) } } impl FileOpener for EmptyFileSystem { #[allow(unused_variables)] fn open( - &mut self, + &self, path: &Path, conf: &OpenOptionsConfig, ) -> Result> { diff --git a/lib/vfs/src/host_fs.rs b/lib/vfs/src/host_fs.rs index e9aac786b4b..fb26bf07ab4 100644 --- a/lib/vfs/src/host_fs.rs +++ b/lib/vfs/src/host_fs.rs @@ -120,7 +120,7 @@ impl crate::FileSystem for FileSystem { } fn new_open_options(&self) -> OpenOptions { - OpenOptions::new(Box::new(FileOpener)) + OpenOptions::new(self) } fn metadata(&self, path: &Path) -> Result { @@ -188,12 +188,9 @@ impl TryInto for std::fs::Metadata { } } -#[derive(Debug, Clone)] -pub struct FileOpener; - -impl crate::FileOpener for FileOpener { +impl crate::FileOpener for FileSystem { fn open( - &mut self, + &self, path: &Path, conf: &OpenOptionsConfig, ) -> Result> { diff --git a/lib/vfs/src/lib.rs b/lib/vfs/src/lib.rs index b5dbbe21cc9..5d179aeb150 100644 --- a/lib/vfs/src/lib.rs +++ b/lib/vfs/src/lib.rs @@ -80,7 +80,7 @@ impl dyn FileSystem + 'static { pub trait FileOpener { fn open( - &mut self, + &self, path: &Path, conf: &OpenOptionsConfig, ) -> Result>; @@ -134,19 +134,19 @@ impl OpenOptionsConfig { } } -impl fmt::Debug for OpenOptions { +impl<'a> fmt::Debug for OpenOptions<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.conf.fmt(f) } } -pub struct OpenOptions { - opener: Box, +pub struct OpenOptions<'a> { + opener: &'a dyn FileOpener, conf: OpenOptionsConfig, } -impl OpenOptions { - pub fn new(opener: Box) -> Self { +impl<'a> OpenOptions<'a> { + pub fn new(opener: &'a dyn FileOpener) -> Self { Self { opener, conf: OpenOptionsConfig { diff --git a/lib/vfs/src/mem_fs/file_opener.rs b/lib/vfs/src/mem_fs/file_opener.rs index 7f3a41ac141..493ad37d45b 100644 --- a/lib/vfs/src/mem_fs/file_opener.rs +++ b/lib/vfs/src/mem_fs/file_opener.rs @@ -5,17 +5,11 @@ use std::borrow::Cow; use std::path::Path; use tracing::*; -/// The type that is responsible to open a file. -#[derive(Debug, Clone)] -pub struct FileOpener { - pub(super) filesystem: FileSystem, -} - -impl FileOpener { +impl FileSystem { /// Inserts a readonly file into the file system that uses copy-on-write /// (this is required for zero-copy creation of the same file) - pub fn insert_ro_file(&mut self, path: &Path, contents: Cow<'static, [u8]>) -> Result<()> { - let _ = crate::FileSystem::remove_file(&self.filesystem, path); + pub fn insert_ro_file(&self, path: &Path, contents: Cow<'static, [u8]>) -> Result<()> { + let _ = crate::FileSystem::remove_file(self, path); let (inode_of_parent, maybe_inode_of_file, name_of_file) = self.insert_inode(path)?; let inode_of_parent = match inode_of_parent { @@ -32,7 +26,7 @@ impl FileOpener { // The file doesn't already exist; it's OK to create it if None => { // Write lock. - let mut fs = self.filesystem.inner.write().map_err(|_| FsError::Lock)?; + let mut fs = self.inner.write().map_err(|_| FsError::Lock)?; let file = ReadOnlyFile::new(contents); let file_len = file.len() as u64; @@ -76,11 +70,11 @@ impl FileOpener { /// Inserts a arc file into the file system that references another file /// in another file system (does not copy the real data) pub fn insert_arc_file( - &mut self, + &self, path: PathBuf, fs: Arc, ) -> Result<()> { - let _ = crate::FileSystem::remove_file(&self.filesystem, path.as_path()); + let _ = crate::FileSystem::remove_file(self, path.as_path()); let (inode_of_parent, maybe_inode_of_file, name_of_file) = self.insert_inode(path.as_path())?; @@ -98,7 +92,7 @@ impl FileOpener { // The file doesn't already exist; it's OK to create it if None => { // Write lock. - let mut fs_lock = self.filesystem.inner.write().map_err(|_| FsError::Lock)?; + let mut fs_lock = self.inner.write().map_err(|_| FsError::Lock)?; // Read the metadata or generate a dummy one let meta = match fs.metadata(&path) { @@ -145,11 +139,11 @@ impl FileOpener { /// Inserts a arc directory into the file system that references another file /// in another file system (does not copy the real data) pub fn insert_arc_directory( - &mut self, + &self, path: PathBuf, fs: Arc, ) -> Result<()> { - let _ = crate::FileSystem::remove_dir(&self.filesystem, path.as_path()); + let _ = crate::FileSystem::remove_dir(self, path.as_path()); let (inode_of_parent, maybe_inode_of_file, name_of_file) = self.insert_inode(path.as_path())?; @@ -167,7 +161,7 @@ impl FileOpener { // The file doesn't already exist; it's OK to create it if None => { // Write lock. - let mut fs_lock = self.filesystem.inner.write().map_err(|_| FsError::Lock)?; + let mut fs_lock = self.inner.write().map_err(|_| FsError::Lock)?; // Creating the file in the storage. let inode_of_file = fs_lock.storage.vacant_entry().key(); @@ -209,11 +203,11 @@ impl FileOpener { /// Inserts a arc file into the file system that references another file /// in another file system (does not copy the real data) pub fn insert_custom_file( - &mut self, + &self, path: PathBuf, file: Box, ) -> Result<()> { - let _ = crate::FileSystem::remove_file(&self.filesystem, path.as_path()); + let _ = crate::FileSystem::remove_file(self, path.as_path()); let (inode_of_parent, maybe_inode_of_file, name_of_file) = self.insert_inode(path.as_path())?; @@ -231,7 +225,7 @@ impl FileOpener { // The file doesn't already exist; it's OK to create it if None => { // Write lock. - let mut fs_lock = self.filesystem.inner.write().map_err(|_| FsError::Lock)?; + let mut fs_lock = self.inner.write().map_err(|_| FsError::Lock)?; // Creating the file in the storage. let inode_of_file = fs_lock.storage.vacant_entry().key(); @@ -269,11 +263,11 @@ impl FileOpener { } fn insert_inode( - &mut self, + &self, path: &Path, ) -> Result<(InodeResolution, Option, OsString)> { // Read lock. - let fs = self.filesystem.inner.read().map_err(|_| FsError::Lock)?; + let fs = self.inner.read().map_err(|_| FsError::Lock)?; // Check the path has a parent. let parent_of_path = path.parent().ok_or(FsError::BaseNotDirectory)?; @@ -309,9 +303,9 @@ impl FileOpener { } } -impl crate::FileOpener for FileOpener { +impl crate::FileOpener for FileSystem { fn open( - &mut self, + &self, path: &Path, conf: &OpenOptionsConfig, ) -> Result> { @@ -370,7 +364,7 @@ impl crate::FileOpener for FileOpener { }; // Write lock. - let mut fs = self.filesystem.inner.write().map_err(|_| FsError::Lock)?; + let mut fs = self.inner.write().map_err(|_| FsError::Lock)?; let inode = fs.storage.get_mut(inode_of_file); match inode { @@ -456,7 +450,7 @@ impl crate::FileOpener for FileOpener { // 2. `create` is used with `write` or `append`. None if (create_new || create) && (write || append) => { // Write lock. - let mut fs = self.filesystem.inner.write().map_err(|_| FsError::Lock)?; + let mut fs = self.inner.write().map_err(|_| FsError::Lock)?; let file = File::new(); @@ -500,7 +494,7 @@ impl crate::FileOpener for FileOpener { Ok(Box::new(FileHandle::new( inode_of_file, - self.filesystem.clone(), + self.clone(), read, write || append || truncate, append, diff --git a/lib/vfs/src/mem_fs/filesystem.rs b/lib/vfs/src/mem_fs/filesystem.rs index a7831505466..e8a9dc178d9 100644 --- a/lib/vfs/src/mem_fs/filesystem.rs +++ b/lib/vfs/src/mem_fs/filesystem.rs @@ -21,10 +21,8 @@ pub struct FileSystem { } impl FileSystem { - pub fn new_open_options_ext(&self) -> FileOpener { - FileOpener { - filesystem: self.clone(), - } + pub fn new_open_options_ext(&self) -> &FileSystem { + self } pub fn union(&self, other: &Arc) { @@ -526,9 +524,7 @@ impl crate::FileSystem for FileSystem { } fn new_open_options(&self) -> OpenOptions { - OpenOptions::new(Box::new(FileOpener { - filesystem: self.clone(), - })) + OpenOptions::new(self) } } diff --git a/lib/vfs/src/mem_fs/mod.rs b/lib/vfs/src/mem_fs/mod.rs index 5db10c4fb97..8d16dd784eb 100644 --- a/lib/vfs/src/mem_fs/mod.rs +++ b/lib/vfs/src/mem_fs/mod.rs @@ -4,7 +4,6 @@ mod filesystem; mod stdio; use file::{File, FileHandle, ReadOnlyFile}; -pub use file_opener::FileOpener; pub use filesystem::FileSystem; pub use stdio::{Stderr, Stdin, Stdout}; diff --git a/lib/vfs/src/static_fs.rs b/lib/vfs/src/static_fs.rs index 552670cef07..f33199cfb87 100644 --- a/lib/vfs/src/static_fs.rs +++ b/lib/vfs/src/static_fs.rs @@ -43,16 +43,9 @@ impl StaticFileSystem { } /// Custom file opener, returns a WebCFile -#[derive(Debug)] -struct WebCFileOpener { - pub package: String, - pub volumes: Arc>>, - pub memory: Arc, -} - -impl FileOpener for WebCFileOpener { +impl FileOpener for StaticFileSystem { fn open( - &mut self, + &self, path: &Path, _conf: &OpenOptionsConfig, ) -> Result, FsError> { @@ -338,11 +331,7 @@ impl FileSystem for StaticFileSystem { } } fn new_open_options(&self) -> OpenOptions { - OpenOptions::new(Box::new(WebCFileOpener { - package: self.package.clone(), - volumes: self.volumes.clone(), - memory: self.memory.clone(), - })) + OpenOptions::new(self) } fn symlink_metadata(&self, path: &Path) -> Result { let path = normalizes_path(path); diff --git a/lib/vfs/src/tmp_fs.rs b/lib/vfs/src/tmp_fs.rs index 13f53b46a40..9722aa215de 100644 --- a/lib/vfs/src/tmp_fs.rs +++ b/lib/vfs/src/tmp_fs.rs @@ -30,7 +30,7 @@ impl TmpFileSystem { Self::default() } - pub fn new_open_options_ext(&self) -> mem_fs::FileOpener { + pub fn new_open_options_ext(&self) -> &mem_fs::FileSystem { self.fs.new_open_options_ext() } diff --git a/lib/vfs/src/union_fs.rs b/lib/vfs/src/union_fs.rs index 5a4efb74965..2c5788c5efd 100644 --- a/lib/vfs/src/union_fs.rs +++ b/lib/vfs/src/union_fs.rs @@ -364,10 +364,7 @@ impl FileSystem for UnionFileSystem { Err(ret_error) } fn new_open_options(&self) -> OpenOptions { - let opener = Box::new(UnionFileOpener { - mounts: self.mounts.clone(), - }); - OpenOptions::new(opener) + OpenOptions::new(self) } } @@ -413,14 +410,9 @@ fn filter_mounts( ret.into_iter() } -#[derive(Debug)] -pub struct UnionFileOpener { - mounts: Vec, -} - -impl FileOpener for UnionFileOpener { +impl FileOpener for UnionFileSystem { fn open( - &mut self, + &self, path: &Path, conf: &OpenOptionsConfig, ) -> Result> { diff --git a/lib/vfs/src/webc_fs.rs b/lib/vfs/src/webc_fs.rs index fa35e8315ce..9acad28557e 100644 --- a/lib/vfs/src/webc_fs.rs +++ b/lib/vfs/src/webc_fs.rs @@ -46,23 +46,13 @@ where } /// Custom file opener, returns a WebCFile -#[derive(Debug)] -struct WebCFileOpener -where - T: std::fmt::Debug + Send + Sync + 'static, -{ - pub package: String, - pub webc: Arc, - pub memory: Arc, -} - -impl FileOpener for WebCFileOpener +impl FileOpener for WebcFileSystem where T: std::fmt::Debug + Send + Sync + 'static, T: Deref>, { fn open( - &mut self, + &self, path: &Path, _conf: &OpenOptionsConfig, ) -> Result, FsError> { @@ -357,11 +347,7 @@ where } } fn new_open_options(&self) -> OpenOptions { - OpenOptions::new(Box::new(WebCFileOpener { - package: self.package.clone(), - webc: self.webc.clone(), - memory: self.memory.clone(), - })) + OpenOptions::new(self) } fn symlink_metadata(&self, path: &Path) -> Result { let path = normalizes_path(path); diff --git a/lib/vnet/src/lib.rs b/lib/vnet/src/lib.rs index 5c900cda077..1ffc70c6df6 100644 --- a/lib/vnet/src/lib.rs +++ b/lib/vnet/src/lib.rs @@ -1,10 +1,13 @@ use std::fmt; +use std::mem::MaybeUninit; use std::net::IpAddr; use std::net::Ipv4Addr; use std::net::Ipv6Addr; use std::net::Shutdown; use std::net::SocketAddr; use std::sync::Arc; +use std::task::Context; +use std::task::Poll; use std::time::Duration; use thiserror::Error; @@ -62,37 +65,37 @@ pub trait VirtualNetworking: fmt::Debug + Send + Sync + 'static { } /// Adds a static IP address to the interface with a netmask prefix - async fn ip_add(&self, ip: IpAddr, prefix: u8) -> Result<()> { + fn ip_add(&self, ip: IpAddr, prefix: u8) -> Result<()> { Err(NetworkError::Unsupported) } /// Removes a static (or dynamic) IP address from the interface - async fn ip_remove(&self, ip: IpAddr) -> Result<()> { + fn ip_remove(&self, ip: IpAddr) -> Result<()> { Err(NetworkError::Unsupported) } /// Clears all the assigned IP addresses for this interface - async fn ip_clear(&self) -> Result<()> { + fn ip_clear(&self) -> Result<()> { Err(NetworkError::Unsupported) } /// Lists all the IP addresses currently assigned to this interface - async fn ip_list(&self) -> Result> { + fn ip_list(&self) -> Result> { Err(NetworkError::Unsupported) } /// Returns the hardware MAC address for this interface - async fn mac(&self) -> Result<[u8; 6]> { + fn mac(&self) -> Result<[u8; 6]> { Err(NetworkError::Unsupported) } /// Adds a default gateway to the routing table - async fn gateway_set(&self, ip: IpAddr) -> Result<()> { + fn gateway_set(&self, ip: IpAddr) -> Result<()> { Err(NetworkError::Unsupported) } /// Adds a specific route to the routing table - async fn route_add( + fn route_add( &self, cidr: IpCidr, via_router: IpAddr, @@ -103,17 +106,17 @@ pub trait VirtualNetworking: fmt::Debug + Send + Sync + 'static { } /// Removes a routing rule from the routing table - async fn route_remove(&self, cidr: IpAddr) -> Result<()> { + fn route_remove(&self, cidr: IpAddr) -> Result<()> { Err(NetworkError::Unsupported) } /// Clears the routing table for this interface - async fn route_clear(&self) -> Result<()> { + fn route_clear(&self) -> Result<()> { Err(NetworkError::Unsupported) } /// Lists all the routes defined in the routing table for this interface - async fn route_list(&self) -> Result> { + fn route_list(&self) -> Result> { Err(NetworkError::Unsupported) } @@ -159,7 +162,6 @@ pub trait VirtualNetworking: fmt::Debug + Send + Sync + 'static { &self, addr: SocketAddr, peer: SocketAddr, - timeout: Option, ) -> Result> { Err(NetworkError::Unsupported) } @@ -177,25 +179,6 @@ pub trait VirtualNetworking: fmt::Debug + Send + Sync + 'static { pub type DynVirtualNetworking = Arc; -#[derive(Debug, Clone)] -pub struct SocketReceive { - /// Data that was received - pub data: Bytes, - /// Indicates if the data was truncated (e.g. UDP packet) - pub truncated: bool, -} - -#[derive(Debug, Clone)] -pub struct SocketReceiveFrom { - /// Data that was received - pub data: Bytes, - /// Indicates if the data was truncated (e.g. UDP packet) - pub truncated: bool, - /// Peer sender address of the data - pub addr: SocketAddr, -} - -#[async_trait::async_trait] pub trait VirtualTcpListener: fmt::Debug + Send + Sync + 'static { /// Tries to accept a new connection fn try_accept(&mut self) -> Option, SocketAddr)>>; @@ -212,38 +195,19 @@ pub trait VirtualTcpListener: fmt::Debug + Send + Sync + 'static { cx: &mut std::task::Context<'_>, ) -> std::task::Poll>; - /// Sets the accept timeout - fn set_timeout(&mut self, timeout: Option) -> Result<()>; - - /// Gets the accept timeout - fn timeout(&self) -> Result>; - /// Returns the local address of this TCP listener fn addr_local(&self) -> Result; /// Sets how many network hops the packets are permitted for new connections - async fn set_ttl(&mut self, ttl: u8) -> Result<()>; + fn set_ttl(&mut self, ttl: u8) -> Result<()>; /// Returns the maximum number of network hops before packets are dropped fn ttl(&self) -> Result; - - /// Determines if the socket is blocking or not - fn set_nonblocking(&mut self, nonblocking: bool) -> Result<()>; - - // Returns true if the socket is nonblocking - fn nonblocking(&self) -> Result; } -#[async_trait::async_trait] pub trait VirtualSocket: fmt::Debug + Send + Sync + 'static { /// Sets how many network hops the packets are permitted for new connections - async fn set_ttl(&mut self, ttl: u32) -> Result<()>; - - /// Determines if the socket is blocking or not - fn set_nonblocking(&mut self, nonblocking: bool) -> Result<()>; - - // Returns true if the socket is nonblocking - fn nonblocking(&self) -> Result; + fn set_ttl(&mut self, ttl: u32) -> Result<()>; /// Returns the maximum number of network hops before packets are dropped fn ttl(&self) -> Result; @@ -284,19 +248,22 @@ pub enum StreamSecurity { } /// Interface used for sending and receiving data from a web socket -#[async_trait::async_trait] pub trait VirtualWebSocket: fmt::Debug + Send + Sync + 'static { /// Sends out a datagram or stream of bytes on this socket - async fn send(&mut self, data: Bytes) -> Result; + fn poll_send(&mut self, cx: &mut Context<'_>, data: &[u8]) -> Poll>; /// FLushes all the datagrams fn flush(&mut self) -> Result<()>; /// Recv a packet from the socket - async fn recv(&mut self) -> Result; + fn poll_recv<'a>( + &mut self, + cx: &mut Context<'_>, + buf: &'a mut [MaybeUninit], + ) -> Poll>; /// Recv a packet from the socket - fn try_recv(&mut self) -> Result>; + fn try_recv<'a>(&mut self, buf: &'a mut [MaybeUninit]) -> Result; /// Polls the socket for when there is data to be received fn poll_read_ready( @@ -312,7 +279,6 @@ pub trait VirtualWebSocket: fmt::Debug + Send + Sync + 'static { } /// Connected sockets have a persistent connection to a remote peer -#[async_trait::async_trait] pub trait VirtualConnectedSocket: VirtualSocket + fmt::Debug + Send + Sync + 'static { /// Determines how long the socket will remain in a TIME_WAIT /// after it disconnects (only the one that initiates the close will @@ -324,63 +290,93 @@ pub trait VirtualConnectedSocket: VirtualSocket + fmt::Debug + Send + Sync + 'st /// after it disconnects fn linger(&self) -> Result>; + /// Tries to send out a datagram or stream of bytes on this socket + fn try_send(&mut self, data: &[u8]) -> Result; + /// Sends out a datagram or stream of bytes on this socket - async fn send(&mut self, data: Bytes) -> Result; + fn poll_send(&mut self, cx: &mut Context<'_>, data: &[u8]) -> Poll>; - /// FLushes all the datagrams - async fn flush(&mut self) -> Result<()>; + /// Attempts to flush the object, ensuring that any buffered data reach + /// their destination. + fn poll_flush(&mut self, cx: &mut Context<'_>) -> Poll>; /// Closes the socket fn close(&mut self) -> Result<()>; /// Recv a packet from the socket - async fn recv(&mut self) -> Result; + fn poll_recv<'a>( + &mut self, + cx: &mut Context<'_>, + buf: &'a mut [MaybeUninit], + ) -> Poll>; /// Recv a packet from the socket - fn try_recv(&mut self) -> Result>; + fn try_recv<'a>(&mut self, buf: &'a mut [MaybeUninit]) -> Result; } /// Connectionless sockets are able to send and receive datagrams and stream /// bytes to multiple addresses at the same time (peer-to-peer) -#[async_trait::async_trait] pub trait VirtualConnectionlessSocket: VirtualSocket + fmt::Debug + Send + Sync + 'static { /// Sends out a datagram or stream of bytes on this socket /// to a specific address - async fn send_to(&mut self, data: Bytes, addr: SocketAddr) -> Result; + fn poll_send_to( + &mut self, + cx: &mut Context<'_>, + data: &[u8], + addr: SocketAddr, + ) -> Poll>; + + /// Sends out a datagram or stream of bytes on this socket + /// to a specific address + fn try_send_to(&mut self, data: &[u8], addr: SocketAddr) -> Result; /// Recv a packet from the socket - async fn recv_from(&mut self) -> Result; + fn poll_recv_from<'a>( + &mut self, + cx: &mut Context<'_>, + buf: &'a mut [MaybeUninit], + ) -> Poll>; /// Recv a packet from the socket - fn try_recv_from(&mut self) -> Result>; + fn try_recv_from<'a>(&mut self, buf: &'a mut [MaybeUninit]) -> Result<(usize, SocketAddr)>; } /// ICMP sockets are low level devices bound to a specific address /// that can send and receive ICMP packets -#[async_trait::async_trait] pub trait VirtualIcmpSocket: VirtualConnectionlessSocket + fmt::Debug + Send + Sync + 'static { } -#[async_trait::async_trait] pub trait VirtualRawSocket: VirtualSocket + fmt::Debug + Send + Sync + 'static { - /// Sends out a raw packet on this socket - async fn send(&mut self, data: Bytes) -> Result; + /// Sends out a datagram or stream of bytes on this socket + fn poll_send(&mut self, cx: &mut Context<'_>, data: &[u8]) -> Poll>; - /// FLushes all the datagrams - async fn flush(&mut self) -> Result<()>; + /// Sends out a datagram or stream of bytes on this socket + fn try_send(&mut self, data: &[u8]) -> Result; + + /// Attempts to flush the object, ensuring that any buffered data reach + /// their destination. + fn poll_flush(&mut self, cx: &mut Context<'_>) -> Poll>; + + /// Attempts to flush the object, ensuring that any buffered data reach + /// their destination. + fn try_flush(&mut self) -> Result<()>; /// Recv a packet from the socket - async fn recv(&mut self) -> Result; + fn poll_recv<'a>( + &mut self, + cx: &mut Context<'_>, + buf: &'a mut [MaybeUninit], + ) -> Poll>; /// Recv a packet from the socket - fn try_recv(&mut self) -> Result>; + fn try_recv<'a>(&mut self, buf: &'a mut [MaybeUninit]) -> Result; /// Tells the raw socket and its backing switch that all packets /// should be received by this socket even if they are not /// destined for this device - async fn set_promiscuous(&mut self, promiscuous: bool) -> Result<()>; + fn set_promiscuous(&mut self, promiscuous: bool) -> Result<()>; /// Returns if the socket is running in promiscuous mode whereby it /// will receive all packets even if they are not destined for the @@ -388,23 +384,7 @@ pub trait VirtualRawSocket: VirtualSocket + fmt::Debug + Send + Sync + 'static { fn promiscuous(&self) -> Result; } -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub enum TimeType { - ReadTimeout, - WriteTimeout, - AcceptTimeout, - ConnectTimeout, - Linger, -} - -#[async_trait::async_trait] pub trait VirtualTcpSocket: VirtualConnectedSocket + fmt::Debug + Send + Sync + 'static { - /// Sets the timeout for a specific action on the socket - fn set_opt_time(&mut self, ty: TimeType, timeout: Option) -> Result<()>; - - /// Returns one of the previous set timeouts - fn opt_time(&self, ty: TimeType) -> Result>; - /// Sets the receive buffer size which acts as a trottle for how /// much data is buffered on this side of the pipe fn set_recv_buf_size(&mut self, size: usize) -> Result<()>; @@ -425,7 +405,7 @@ pub trait VirtualTcpSocket: VirtualConnectedSocket + fmt::Debug + Send + Sync + /// the peer is sent immediately rather than waiting for a bigger /// batch of data, this reduces latency but increases encapsulation /// overhead. - async fn set_nodelay(&mut self, reuse: bool) -> Result<()>; + fn set_nodelay(&mut self, reuse: bool) -> Result<()>; /// Indicates if the NO_DELAY flag is set which means that data /// is immediately sent to the peer without waiting. This reduces @@ -438,20 +418,15 @@ pub trait VirtualTcpSocket: VirtualConnectedSocket + fmt::Debug + Send + Sync + /// Shuts down either the READER or WRITER sides of the socket /// connection. - async fn shutdown(&mut self, how: Shutdown) -> Result<()>; + fn shutdown(&mut self, how: Shutdown) -> Result<()>; /// Return true if the socket is closed fn is_closed(&self) -> bool; } -#[async_trait::async_trait] pub trait VirtualUdpSocket: - VirtualConnectedSocket + VirtualConnectionlessSocket + fmt::Debug + Send + Sync + 'static + VirtualConnectionlessSocket + fmt::Debug + Send + Sync + 'static { - /// Connects to a destination peer so that the normal - /// send/recv operations can be used. - async fn connect(&mut self, addr: SocketAddr) -> Result<()>; - /// Sets a flag that means that the UDP socket is able /// to receive and process broadcast packets. fn set_broadcast(&mut self, broadcast: bool) -> Result<()>; diff --git a/lib/wasi-local-networking/src/lib.rs b/lib/wasi-local-networking/src/lib.rs index 5710fbe02cb..86f7284fe57 100644 --- a/lib/wasi-local-networking/src/lib.rs +++ b/lib/wasi-local-networking/src/lib.rs @@ -1,22 +1,21 @@ #![allow(unused_variables)] -use bytes::Bytes; use std::future::Future; +use std::mem::MaybeUninit; use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr}; use std::pin::Pin; use std::ptr; use std::sync::Mutex; use std::task::{Context, Poll, RawWaker, RawWakerVTable, Waker}; use std::time::Duration; -use tokio::io::{AsyncRead, AsyncWriteExt}; use tokio::sync::mpsc; #[allow(unused_imports, dead_code)] use tracing::{debug, error, info, trace, warn}; #[allow(unused_imports)] use wasmer_vnet::{ - io_err_into_net_error, IpCidr, IpRoute, NetworkError, Result, SocketReceive, SocketReceiveFrom, - SocketStatus, StreamSecurity, TimeType, VirtualConnectedSocket, VirtualConnectionlessSocket, - VirtualIcmpSocket, VirtualNetworking, VirtualRawSocket, VirtualSocket, VirtualTcpListener, - VirtualTcpSocket, VirtualUdpSocket, VirtualWebSocket, + io_err_into_net_error, IpCidr, IpRoute, NetworkError, Result, SocketStatus, StreamSecurity, + VirtualConnectedSocket, VirtualConnectionlessSocket, VirtualIcmpSocket, VirtualNetworking, + VirtualRawSocket, VirtualSocket, VirtualTcpListener, VirtualTcpSocket, VirtualUdpSocket, + VirtualWebSocket, }; #[derive(Debug, Default)] @@ -37,9 +36,7 @@ impl VirtualNetworking for LocalNetworking { .map(|sock| { Box::new(LocalTcpListener { stream: sock, - timeout: None, backlog: Mutex::new(Vec::new()), - nonblocking: false, }) }) .map_err(io_err_into_net_error)?; @@ -56,7 +53,7 @@ impl VirtualNetworking for LocalNetworking { .await .map_err(io_err_into_net_error)?; Ok(Box::new(LocalUdpSocket { - socket: LocalUdpSocketMode::Async(socket), + socket, addr, nonblocking: false, })) @@ -66,19 +63,12 @@ impl VirtualNetworking for LocalNetworking { &self, _addr: SocketAddr, peer: SocketAddr, - timeout: Option, ) -> Result> { - let stream = if let Some(timeout) = timeout { - match tokio::time::timeout(timeout, tokio::net::TcpStream::connect(&peer)).await { - Ok(a) => a, - Err(err) => Err(Into::::into(std::io::ErrorKind::TimedOut)), - } - } else { - tokio::net::TcpStream::connect(peer).await - } - .map_err(io_err_into_net_error)?; + let stream = tokio::net::TcpStream::connect(peer) + .await + .map_err(io_err_into_net_error)?; let peer = stream.peer_addr().map_err(io_err_into_net_error)?; - Ok(Box::new(LocalTcpStream::new(stream, peer, false))) + Ok(Box::new(LocalTcpStream::new(stream, peer))) } async fn resolve( @@ -97,9 +87,7 @@ impl VirtualNetworking for LocalNetworking { #[derive(Debug)] pub struct LocalTcpListener { stream: tokio::net::TcpListener, - timeout: Option, backlog: Mutex, SocketAddr)>>, - nonblocking: bool, } #[async_trait::async_trait] @@ -119,10 +107,9 @@ impl VirtualTcpListener for LocalTcpListener { .poll_accept(&mut cx) .map_err(io_err_into_net_error) { - Poll::Ready(Ok((stream, addr))) => Some(Ok(( - Box::new(LocalTcpStream::new(stream, addr, self.nonblocking)), - addr, - ))), + Poll::Ready(Ok((stream, addr))) => { + Some(Ok((Box::new(LocalTcpStream::new(stream, addr)), addr))) + } Poll::Ready(Err(NetworkError::WouldBlock)) => None, Poll::Ready(Err(err)) => Some(Err(err)), Poll::Pending => None, @@ -140,24 +127,9 @@ impl VirtualTcpListener for LocalTcpListener { } } - let nonblocking = self.nonblocking; - if nonblocking { - return Poll::Ready( - match self.stream.poll_accept(cx).map_err(io_err_into_net_error) { - Poll::Ready(Ok((sock, addr))) => { - Ok((Box::new(LocalTcpStream::new(sock, addr, nonblocking)), addr)) - } - Poll::Ready(Err(err)) => Err(err), - Poll::Pending => Err(NetworkError::WouldBlock), - }, - ); - } - // We poll the socket let (sock, addr) = match self.stream.poll_accept(cx).map_err(io_err_into_net_error) { - Poll::Ready(Ok((sock, addr))) => { - (Box::new(LocalTcpStream::new(sock, addr, nonblocking)), addr) - } + Poll::Ready(Ok((sock, addr))) => (Box::new(LocalTcpStream::new(sock, addr)), addr), Poll::Ready(Err(err)) => return Poll::Ready(Err(err)), Poll::Pending => return Poll::Pending, }; @@ -173,30 +145,16 @@ impl VirtualTcpListener for LocalTcpListener { .map_err(io_err_into_net_error) .map_ok(|(sock, addr)| { let mut backlog = self.backlog.lock().unwrap(); - backlog.push(( - Box::new(LocalTcpStream::new(sock, addr, self.nonblocking)), - addr, - )); + backlog.push((Box::new(LocalTcpStream::new(sock, addr)), addr)); backlog.len() }) } - /// Sets the accept timeout - fn set_timeout(&mut self, timeout: Option) -> Result<()> { - self.timeout = timeout; - Ok(()) - } - - /// Gets the accept timeout - fn timeout(&self) -> Result> { - Ok(self.timeout) - } - fn addr_local(&self) -> Result { self.stream.local_addr().map_err(io_err_into_net_error) } - async fn set_ttl(&mut self, ttl: u8) -> Result<()> { + fn set_ttl(&mut self, ttl: u8) -> Result<()> { self.stream .set_ttl(ttl as u32) .map_err(io_err_into_net_error) @@ -208,26 +166,12 @@ impl VirtualTcpListener for LocalTcpListener { .map(|ttl| ttl as u8) .map_err(io_err_into_net_error) } - - fn set_nonblocking(&mut self, nonblocking: bool) -> Result<()> { - self.nonblocking = nonblocking; - Ok(()) - } - - fn nonblocking(&self) -> Result { - Ok(self.nonblocking) - } } #[derive(Debug)] pub struct LocalTcpStream { stream: tokio::net::TcpStream, addr: SocketAddr, - read_timeout: Option, - write_timeout: Option, - connect_timeout: Option, - linger_timeout: Option, - nonblocking: bool, shutdown: Option, tx_write_ready: mpsc::Sender<()>, rx_write_ready: mpsc::Receiver<()>, @@ -236,17 +180,12 @@ pub struct LocalTcpStream { } impl LocalTcpStream { - pub fn new(stream: tokio::net::TcpStream, addr: SocketAddr, nonblocking: bool) -> Self { + pub fn new(stream: tokio::net::TcpStream, addr: SocketAddr) -> Self { let (tx_write_ready, rx_write_ready) = mpsc::channel(1); let (tx_write_poll_ready, rx_write_poll_ready) = mpsc::channel(1); Self { stream, addr, - read_timeout: None, - write_timeout: None, - connect_timeout: None, - linger_timeout: None, - nonblocking, shutdown: None, tx_write_ready, rx_write_ready, @@ -258,35 +197,6 @@ impl LocalTcpStream { #[async_trait::async_trait] impl VirtualTcpSocket for LocalTcpStream { - fn set_opt_time(&mut self, ty: TimeType, timeout: Option) -> Result<()> { - match ty { - TimeType::ReadTimeout => { - self.read_timeout = timeout; - } - TimeType::WriteTimeout => { - self.write_timeout = timeout; - } - TimeType::ConnectTimeout => { - self.connect_timeout = timeout; - } - TimeType::Linger => { - self.linger_timeout = timeout; - } - _ => return Err(NetworkError::InvalidInput), - } - Ok(()) - } - - fn opt_time(&self, ty: TimeType) -> Result> { - match ty { - TimeType::ReadTimeout => Ok(self.read_timeout), - TimeType::WriteTimeout => Ok(self.write_timeout), - TimeType::ConnectTimeout => Ok(self.connect_timeout), - TimeType::Linger => Ok(self.linger_timeout), - _ => Err(NetworkError::InvalidInput), - } - } - fn set_recv_buf_size(&mut self, size: usize) -> Result<()> { Ok(()) } @@ -303,7 +213,7 @@ impl VirtualTcpSocket for LocalTcpStream { Err(NetworkError::Unsupported) } - async fn set_nodelay(&mut self, nodelay: bool) -> Result<()> { + fn set_nodelay(&mut self, nodelay: bool) -> Result<()> { self.stream .set_nodelay(nodelay) .map_err(io_err_into_net_error) @@ -317,8 +227,7 @@ impl VirtualTcpSocket for LocalTcpStream { Ok(self.addr) } - async fn shutdown(&mut self, how: Shutdown) -> Result<()> { - self.stream.flush().await.map_err(io_err_into_net_error)?; + fn shutdown(&mut self, how: Shutdown) -> Result<()> { self.shutdown = Some(how); Ok(()) } @@ -328,75 +237,6 @@ impl VirtualTcpSocket for LocalTcpStream { } } -impl LocalTcpStream { - async fn recv_now_ext( - nonblocking: bool, - stream: &mut tokio::net::TcpStream, - timeout: Option, - ) -> Result { - if nonblocking { - let max_buf_size = 8192; - let mut buf = vec![0u8; max_buf_size]; - - let waker = unsafe { Waker::from_raw(RawWaker::new(ptr::null(), &NOOP_WAKER_VTABLE)) }; - let mut cx = Context::from_waker(&waker); - let stream = Pin::new(stream); - let mut read_buf = tokio::io::ReadBuf::new(&mut buf); - match stream.poll_read(&mut cx, &mut read_buf) { - Poll::Ready(Ok(read)) => { - let read = read_buf.remaining(); - unsafe { - buf.set_len(read); - } - if read == 0 { - return Err(NetworkError::WouldBlock); - } - let buf = Bytes::from(buf); - Ok(SocketReceive { - data: buf, - truncated: read == max_buf_size, - }) - } - Poll::Ready(Err(err)) => Err(io_err_into_net_error(err)), - Poll::Pending => Err(NetworkError::WouldBlock), - } - } else { - Self::recv_now(stream, timeout).await - } - } - - async fn recv_now( - stream: &mut tokio::net::TcpStream, - timeout: Option, - ) -> Result { - use tokio::io::AsyncReadExt; - let max_buf_size = 8192; - let mut buf = vec![0u8; max_buf_size]; - - let work = async move { - match timeout { - Some(timeout) => tokio::time::timeout(timeout, stream.read(&mut buf[..])) - .await - .map_err(|_| Into::::into(std::io::ErrorKind::TimedOut))?, - None => stream.read(&mut buf[..]).await, - } - .map(|read| { - unsafe { - buf.set_len(read); - } - Bytes::from(buf) - }) - }; - - let buf = work.await.map_err(io_err_into_net_error)?; - Ok(SocketReceive { - truncated: buf.len() == max_buf_size, - data: buf, - }) - } -} - -#[async_trait::async_trait] impl VirtualConnectedSocket for LocalTcpStream { fn set_linger(&mut self, linger: Option) -> Result<()> { self.stream @@ -409,88 +249,60 @@ impl VirtualConnectedSocket for LocalTcpStream { self.stream.linger().map_err(io_err_into_net_error) } - async fn send(&mut self, data: Bytes) -> Result { - self.rx_write_ready.try_recv().ok(); - self.tx_write_poll_ready.try_send(()).ok(); - let nonblocking = self.nonblocking; - if nonblocking { - let waker = unsafe { Waker::from_raw(RawWaker::new(ptr::null(), &NOOP_WAKER_VTABLE)) }; - let mut cx = Context::from_waker(&waker); - if self.stream.poll_write_ready(&mut cx).is_pending() { - return Err(NetworkError::WouldBlock); - } - } - - use tokio::io::AsyncWriteExt; - let timeout = self.write_timeout; - let work = async move { - match timeout { - Some(timeout) => tokio::time::timeout(timeout, self.stream.write_all(&data[..])) - .await - .map_err(|_| Into::::into(std::io::ErrorKind::WouldBlock))?, - None => self.stream.write_all(&data[..]).await, - } - .map(|_| data.len()) - }; + fn try_send(&mut self, data: &[u8]) -> Result { + self.stream.try_write(data).map_err(io_err_into_net_error) + } - let amt = work.await.map_err(io_err_into_net_error)?; - if amt == 0 { - if nonblocking { - return Err(NetworkError::WouldBlock); - } else { - return Err(NetworkError::BrokenPipe); - } - } - Ok(amt) + fn poll_send(&mut self, cx: &mut Context<'_>, data: &[u8]) -> Poll> { + use tokio::io::AsyncWrite; + Pin::new(&mut self.stream) + .poll_write(cx, data) + .map_err(io_err_into_net_error) } - async fn flush(&mut self) -> Result<()> { + fn poll_flush(&mut self, cx: &mut Context<'_>) -> Poll> { while self.rx_write_ready.try_recv().is_ok() {} self.tx_write_poll_ready.try_send(()).ok(); - if self.nonblocking { - let waker = unsafe { Waker::from_raw(RawWaker::new(ptr::null(), &NOOP_WAKER_VTABLE)) }; - let mut cx = Context::from_waker(&waker); - if self.stream.poll_write_ready(&mut cx).is_pending() { - return Err(NetworkError::WouldBlock); - } - } - use tokio::io::AsyncWriteExt; - let timeout = self.write_timeout; - let work = async move { - match timeout { - Some(timeout) => tokio::time::timeout(timeout, self.stream.flush()) - .await - .map_err(|_| Into::::into(std::io::ErrorKind::WouldBlock))?, - None => self.stream.flush().await, - } - }; - - work.await.map_err(io_err_into_net_error) + use tokio::io::AsyncWrite; + Pin::new(&mut self.stream) + .poll_flush(cx) + .map_err(io_err_into_net_error) } fn close(&mut self) -> Result<()> { Ok(()) } - async fn recv(&mut self) -> Result { - Self::recv_now_ext(self.nonblocking, &mut self.stream, self.read_timeout).await + fn poll_recv<'a>( + &mut self, + cx: &mut Context<'_>, + buf: &'a mut [MaybeUninit], + ) -> Poll> { + use tokio::io::AsyncRead; + let mut read_buf = tokio::io::ReadBuf::uninit(buf); + let res = Pin::new(&mut self.stream) + .poll_read(cx, &mut read_buf) + .map_err(io_err_into_net_error); + match res { + Poll::Ready(Ok(_)) => { + let amt = read_buf.filled().len(); + let data: &[u8] = unsafe { std::mem::transmute(&buf[..amt]) }; + Poll::Ready(Ok(amt)) + } + Poll::Ready(Err(err)) => Poll::Ready(Err(err)), + Poll::Pending => Poll::Pending, + } } - fn try_recv(&mut self) -> Result> { - let mut work = self.recv(); - let waker = unsafe { Waker::from_raw(RawWaker::new(ptr::null(), &NOOP_WAKER_VTABLE)) }; - let mut cx = Context::from_waker(&waker); - match work.as_mut().poll(&mut cx) { - Poll::Ready(Ok(ret)) => Ok(Some(ret)), - Poll::Ready(Err(err)) => Err(err), - Poll::Pending => Ok(None), - } + fn try_recv<'a>(&mut self, buf: &'a mut [MaybeUninit]) -> Result { + let buf: &mut [u8] = unsafe { std::mem::transmute(buf) }; + self.stream.try_read(buf).map_err(io_err_into_net_error) } } #[async_trait::async_trait] impl VirtualSocket for LocalTcpStream { - async fn set_ttl(&mut self, ttl: u32) -> Result<()> { + fn set_ttl(&mut self, ttl: u32) -> Result<()> { self.stream.set_ttl(ttl).map_err(io_err_into_net_error) } @@ -498,15 +310,6 @@ impl VirtualSocket for LocalTcpStream { self.stream.ttl().map_err(io_err_into_net_error) } - fn set_nonblocking(&mut self, nonblocking: bool) -> Result<()> { - self.nonblocking = nonblocking; - Ok(()) - } - - fn nonblocking(&self) -> Result { - Ok(self.nonblocking) - } - fn addr_local(&self) -> Result { self.stream.local_addr().map_err(io_err_into_net_error) } @@ -585,441 +388,150 @@ impl<'a> Future for LocalTcpStreamWriteReady<'a> { #[derive(Debug)] pub struct LocalUdpSocket { - socket: LocalUdpSocketMode, + socket: tokio::net::UdpSocket, #[allow(dead_code)] addr: SocketAddr, nonblocking: bool, } -#[derive(Debug)] -enum LocalUdpSocketMode { - Blocking(std::net::UdpSocket), - Async(tokio::net::UdpSocket), - Uninitialized, -} - -impl LocalUdpSocketMode { - fn as_blocking_mut(&mut self) -> std::io::Result<&mut std::net::UdpSocket> { - match self { - Self::Blocking(a) => Ok(a), - Self::Async(_) => { - let mut listener = Self::Uninitialized; - std::mem::swap(self, &mut listener); - listener = match listener { - Self::Async(a) => Self::Blocking(a.into_std()?), - a => unreachable!(), - }; - std::mem::swap(self, &mut listener); - match self { - Self::Blocking(a) => Ok(a), - _ => unreachable!(), - } - } - Self::Uninitialized => unreachable!(), - } - } - - fn as_async_mut(&mut self) -> std::io::Result<&mut tokio::net::UdpSocket> { - match self { - Self::Async(a) => Ok(a), - Self::Blocking(_) => { - let mut listener = Self::Uninitialized; - std::mem::swap(self, &mut listener); - listener = match listener { - Self::Blocking(a) => Self::Async(tokio::net::UdpSocket::from_std(a)?), - a => unreachable!(), - }; - std::mem::swap(self, &mut listener); - match self { - Self::Async(a) => Ok(a), - _ => unreachable!(), - } - } - Self::Uninitialized => unreachable!(), - } - } -} - #[async_trait::async_trait] impl VirtualUdpSocket for LocalUdpSocket { - async fn connect(&mut self, addr: SocketAddr) -> Result<()> { + fn set_broadcast(&mut self, broadcast: bool) -> Result<()> { self.socket - .as_async_mut() - .map_err(io_err_into_net_error)? - .connect(addr) - .await + .set_broadcast(broadcast) .map_err(io_err_into_net_error) } - fn set_broadcast(&mut self, broadcast: bool) -> Result<()> { - match &mut self.socket { - LocalUdpSocketMode::Blocking(a) => { - a.set_broadcast(broadcast).map_err(io_err_into_net_error) - } - LocalUdpSocketMode::Async(a) => { - a.set_broadcast(broadcast).map_err(io_err_into_net_error) - } - LocalUdpSocketMode::Uninitialized => unreachable!(), - } - } - fn broadcast(&self) -> Result { - match &self.socket { - LocalUdpSocketMode::Blocking(a) => a.broadcast().map_err(io_err_into_net_error), - LocalUdpSocketMode::Async(a) => a.broadcast().map_err(io_err_into_net_error), - LocalUdpSocketMode::Uninitialized => unreachable!(), - } + self.socket.broadcast().map_err(io_err_into_net_error) } fn set_multicast_loop_v4(&mut self, val: bool) -> Result<()> { - match &mut self.socket { - LocalUdpSocketMode::Blocking(a) => { - a.set_multicast_loop_v4(val).map_err(io_err_into_net_error) - } - LocalUdpSocketMode::Async(a) => { - a.set_multicast_loop_v4(val).map_err(io_err_into_net_error) - } - LocalUdpSocketMode::Uninitialized => unreachable!(), - } + self.socket + .set_multicast_loop_v4(val) + .map_err(io_err_into_net_error) } fn multicast_loop_v4(&self) -> Result { - match &self.socket { - LocalUdpSocketMode::Blocking(a) => a.multicast_loop_v4().map_err(io_err_into_net_error), - LocalUdpSocketMode::Async(a) => a.multicast_loop_v4().map_err(io_err_into_net_error), - LocalUdpSocketMode::Uninitialized => unreachable!(), - } + self.socket + .multicast_loop_v4() + .map_err(io_err_into_net_error) } fn set_multicast_loop_v6(&mut self, val: bool) -> Result<()> { - match &mut self.socket { - LocalUdpSocketMode::Blocking(a) => { - a.set_multicast_loop_v6(val).map_err(io_err_into_net_error) - } - LocalUdpSocketMode::Async(a) => { - a.set_multicast_loop_v6(val).map_err(io_err_into_net_error) - } - LocalUdpSocketMode::Uninitialized => unreachable!(), - } + self.socket + .set_multicast_loop_v6(val) + .map_err(io_err_into_net_error) } fn multicast_loop_v6(&self) -> Result { - match &self.socket { - LocalUdpSocketMode::Blocking(a) => a.multicast_loop_v6().map_err(io_err_into_net_error), - LocalUdpSocketMode::Async(a) => a.multicast_loop_v6().map_err(io_err_into_net_error), - LocalUdpSocketMode::Uninitialized => unreachable!(), - } + self.socket + .multicast_loop_v6() + .map_err(io_err_into_net_error) } fn set_multicast_ttl_v4(&mut self, ttl: u32) -> Result<()> { - match &mut self.socket { - LocalUdpSocketMode::Blocking(a) => { - a.set_multicast_ttl_v4(ttl).map_err(io_err_into_net_error) - } - LocalUdpSocketMode::Async(a) => { - a.set_multicast_ttl_v4(ttl).map_err(io_err_into_net_error) - } - LocalUdpSocketMode::Uninitialized => unreachable!(), - } + self.socket + .set_multicast_ttl_v4(ttl) + .map_err(io_err_into_net_error) } fn multicast_ttl_v4(&self) -> Result { - match &self.socket { - LocalUdpSocketMode::Blocking(a) => a.multicast_ttl_v4().map_err(io_err_into_net_error), - LocalUdpSocketMode::Async(a) => a.multicast_ttl_v4().map_err(io_err_into_net_error), - LocalUdpSocketMode::Uninitialized => unreachable!(), - } + self.socket + .multicast_ttl_v4() + .map_err(io_err_into_net_error) } fn join_multicast_v4(&mut self, multiaddr: Ipv4Addr, iface: Ipv4Addr) -> Result<()> { - match &mut self.socket { - LocalUdpSocketMode::Blocking(a) => a - .join_multicast_v4(&multiaddr, &iface) - .map_err(io_err_into_net_error), - LocalUdpSocketMode::Async(a) => a - .join_multicast_v4(multiaddr, iface) - .map_err(io_err_into_net_error), - LocalUdpSocketMode::Uninitialized => unreachable!(), - } + self.socket + .join_multicast_v4(multiaddr, iface) + .map_err(io_err_into_net_error) } fn leave_multicast_v4(&mut self, multiaddr: Ipv4Addr, iface: Ipv4Addr) -> Result<()> { - match &mut self.socket { - LocalUdpSocketMode::Blocking(a) => a - .leave_multicast_v4(&multiaddr, &iface) - .map_err(io_err_into_net_error), - LocalUdpSocketMode::Async(a) => a - .leave_multicast_v4(multiaddr, iface) - .map_err(io_err_into_net_error), - LocalUdpSocketMode::Uninitialized => unreachable!(), - } + self.socket + .leave_multicast_v4(multiaddr, iface) + .map_err(io_err_into_net_error) } fn join_multicast_v6(&mut self, multiaddr: Ipv6Addr, iface: u32) -> Result<()> { - match &mut self.socket { - LocalUdpSocketMode::Blocking(a) => a - .join_multicast_v6(&multiaddr, iface) - .map_err(io_err_into_net_error), - LocalUdpSocketMode::Async(a) => a - .join_multicast_v6(&multiaddr, iface) - .map_err(io_err_into_net_error), - LocalUdpSocketMode::Uninitialized => unreachable!(), - } + self.socket + .join_multicast_v6(&multiaddr, iface) + .map_err(io_err_into_net_error) } fn leave_multicast_v6(&mut self, multiaddr: Ipv6Addr, iface: u32) -> Result<()> { - match &mut self.socket { - LocalUdpSocketMode::Blocking(a) => a - .leave_multicast_v6(&multiaddr, iface) - .map_err(io_err_into_net_error), - LocalUdpSocketMode::Async(a) => a - .leave_multicast_v6(&multiaddr, iface) - .map_err(io_err_into_net_error), - LocalUdpSocketMode::Uninitialized => unreachable!(), - } + self.socket + .leave_multicast_v6(&multiaddr, iface) + .map_err(io_err_into_net_error) } fn addr_peer(&self) -> Result> { - match &self.socket { - LocalUdpSocketMode::Blocking(a) => { - a.peer_addr().map(Some).map_err(io_err_into_net_error) - } - LocalUdpSocketMode::Async(a) => a.peer_addr().map(Some).map_err(io_err_into_net_error), - LocalUdpSocketMode::Uninitialized => unreachable!(), - } - } -} - -#[async_trait::async_trait] -impl VirtualConnectedSocket for LocalUdpSocket { - fn set_linger(&mut self, linger: Option) -> Result<()> { - Err(NetworkError::Unsupported) - } - - fn linger(&self) -> Result> { - Err(NetworkError::Unsupported) - } - - async fn send(&mut self, data: Bytes) -> Result { - let amt = self - .socket - .as_async_mut() - .map_err(io_err_into_net_error)? - .send(&data[..]) - .await - .map_err(io_err_into_net_error)?; - if amt == 0 { - if self.nonblocking { - return Err(NetworkError::WouldBlock); - } else { - return Err(NetworkError::BrokenPipe); - } - } - Ok(amt) - } - - async fn flush(&mut self) -> Result<()> { - Ok(()) - } - - fn close(&mut self) -> Result<()> { - Ok(()) - } - - async fn recv(&mut self) -> Result { - let buf_size = 8192; - let mut buf = vec![0u8; buf_size]; - - let read = self - .socket - .as_async_mut() - .map_err(io_err_into_net_error)? - .recv(&mut buf[..]) - .await - .map_err(io_err_into_net_error)?; - unsafe { - buf.set_len(read); - } - if read == 0 { - if self.nonblocking { - return Err(NetworkError::WouldBlock); - } else { - return Err(NetworkError::BrokenPipe); - } - } - let buf = Bytes::from(buf); - Ok(SocketReceive { - data: buf, - truncated: read == buf_size, - }) - } - - fn try_recv(&mut self) -> Result> { - let buf_size = 8192; - let mut buf = vec![0u8; buf_size]; - - let socket = self - .socket - .as_blocking_mut() - .map_err(io_err_into_net_error)?; - socket - .set_nonblocking(true) - .map_err(io_err_into_net_error)?; - let read = socket.recv(&mut buf[..]); - let _ = socket.set_nonblocking(self.nonblocking); - - let read = match read { - Ok(0) => { - return Ok(None); - } - Ok(a) => Ok(a), - Err(err) - if err.kind() == std::io::ErrorKind::TimedOut - || err.kind() == std::io::ErrorKind::WouldBlock => - { - return Ok(None); - } - Err(err) => Err(io_err_into_net_error(err)), - }?; - unsafe { - buf.set_len(read); - } - - let buf = Bytes::from(buf); - Ok(Some(SocketReceive { - data: buf, - truncated: read == buf_size, - })) + self.socket + .peer_addr() + .map(Some) + .map_err(io_err_into_net_error) } } -#[async_trait::async_trait] impl VirtualConnectionlessSocket for LocalUdpSocket { - async fn send_to(&mut self, data: Bytes, addr: SocketAddr) -> Result { - let amt = self - .socket - .as_async_mut() - .map_err(io_err_into_net_error)? - .send_to(&data[..], addr) - .await - .map_err(io_err_into_net_error)?; - if amt == 0 { - if self.nonblocking { - return Err(NetworkError::WouldBlock); - } else { - return Err(NetworkError::BrokenPipe); - } - } - Ok(amt) + fn poll_send_to( + &mut self, + cx: &mut Context<'_>, + data: &[u8], + addr: SocketAddr, + ) -> Poll> { + self.socket + .poll_send_to(cx, data, addr) + .map_err(io_err_into_net_error) } - fn try_recv_from(&mut self) -> Result> { - let buf_size = 8192; - let mut buf = vec![0u8; buf_size]; - - let socket = self - .socket - .as_blocking_mut() - .map_err(io_err_into_net_error)?; - socket - .set_nonblocking(true) - .map_err(io_err_into_net_error)?; - let read = socket.recv_from(&mut buf[..]); - let _ = socket.set_nonblocking(self.nonblocking); - - let (read, peer) = match read { - Ok((0, _)) => { - return Ok(None); - } - Ok((a, b)) => Ok((a, b)), - Err(err) - if err.kind() == std::io::ErrorKind::TimedOut - || err.kind() == std::io::ErrorKind::WouldBlock => - { - return Ok(None); - } - Err(err) => Err(io_err_into_net_error(err)), - }?; - unsafe { - buf.set_len(read); - } - - let buf = Bytes::from(buf); - Ok(Some(SocketReceiveFrom { - data: buf, - truncated: read == buf_size, - addr: peer, - })) + fn try_send_to(&mut self, data: &[u8], addr: SocketAddr) -> Result { + self.socket + .try_send_to(data, addr) + .map_err(io_err_into_net_error) } - async fn recv_from(&mut self) -> Result { - let buf_size = 8192; - let mut buf = vec![0u8; buf_size]; - - let (read, peer) = self + fn poll_recv_from<'a>( + &mut self, + cx: &mut Context<'_>, + buf: &'a mut [MaybeUninit], + ) -> Poll> { + let mut read_buf = tokio::io::ReadBuf::uninit(buf); + let res = self .socket - .as_async_mut() - .map_err(io_err_into_net_error)? - .recv_from(&mut buf[..]) - .await - .map_err(io_err_into_net_error)?; - unsafe { - buf.set_len(read); - } - if read == 0 { - if self.nonblocking { - return Err(NetworkError::WouldBlock); - } else { - return Err(NetworkError::BrokenPipe); + .poll_recv_from(cx, &mut read_buf) + .map_err(io_err_into_net_error); + match res { + Poll::Ready(Ok(addr)) => { + let amt = read_buf.filled().len(); + Poll::Ready(Ok((amt, addr))) } - } - let buf = Bytes::from(buf); - Ok(SocketReceiveFrom { - data: buf, - truncated: read == buf_size, - addr: peer, - }) - } -} - -#[async_trait::async_trait] -impl VirtualSocket for LocalUdpSocket { - async fn set_ttl(&mut self, ttl: u32) -> Result<()> { - match &mut self.socket { - LocalUdpSocketMode::Blocking(a) => a.set_ttl(ttl).map_err(io_err_into_net_error), - LocalUdpSocketMode::Async(a) => a.set_ttl(ttl).map_err(io_err_into_net_error), - LocalUdpSocketMode::Uninitialized => unreachable!(), + Poll::Ready(Err(err)) => Poll::Ready(Err(err)), + Poll::Pending if self.nonblocking => Poll::Ready(Err(NetworkError::WouldBlock)), + Poll::Pending => Poll::Pending, } } - fn set_nonblocking(&mut self, nonblocking: bool) -> Result<()> { - self.nonblocking = nonblocking; + fn try_recv_from<'a>(&mut self, buf: &'a mut [MaybeUninit]) -> Result<(usize, SocketAddr)> { + let buf: &mut [u8] = unsafe { std::mem::transmute(buf) }; self.socket - .as_blocking_mut() - .map_err(io_err_into_net_error)? - .set_nonblocking(nonblocking) - .map_err(io_err_into_net_error)?; - Ok(()) + .try_recv_from(buf) + .map_err(io_err_into_net_error) } +} - fn nonblocking(&self) -> Result { - Ok(self.nonblocking) +impl VirtualSocket for LocalUdpSocket { + fn set_ttl(&mut self, ttl: u32) -> Result<()> { + self.socket.set_ttl(ttl).map_err(io_err_into_net_error) } fn ttl(&self) -> Result { - match &self.socket { - LocalUdpSocketMode::Blocking(a) => a.ttl().map_err(io_err_into_net_error), - LocalUdpSocketMode::Async(a) => a.ttl().map_err(io_err_into_net_error), - LocalUdpSocketMode::Uninitialized => unreachable!(), - } + self.socket.ttl().map_err(io_err_into_net_error) } fn addr_local(&self) -> Result { - match &self.socket { - LocalUdpSocketMode::Blocking(a) => a.local_addr().map_err(io_err_into_net_error), - LocalUdpSocketMode::Async(a) => a.local_addr().map_err(io_err_into_net_error), - LocalUdpSocketMode::Uninitialized => unreachable!(), - } + self.socket.local_addr().map_err(io_err_into_net_error) } fn status(&self) -> Result { @@ -1030,10 +542,9 @@ impl VirtualSocket for LocalUdpSocket { &mut self, cx: &mut std::task::Context<'_>, ) -> std::task::Poll> { - let socket = self.socket.as_async_mut().map_err(io_err_into_net_error)?; - socket + self.socket .poll_recv_ready(cx) - .map_ok(|a| 8192usize) + .map_ok(|()| 8192usize) .map_err(io_err_into_net_error) } @@ -1041,10 +552,9 @@ impl VirtualSocket for LocalUdpSocket { &mut self, cx: &mut std::task::Context<'_>, ) -> std::task::Poll> { - let socket = self.socket.as_async_mut().map_err(io_err_into_net_error)?; - socket + self.socket .poll_send_ready(cx) - .map_ok(|a| 8192usize) + .map_ok(|()| 8192usize) .map_err(io_err_into_net_error) } } diff --git a/lib/wasi/src/bin_factory/module_cache.rs b/lib/wasi/src/bin_factory/module_cache.rs index bc71c97f920..6f8e03b090c 100644 --- a/lib/wasi/src/bin_factory/module_cache.rs +++ b/lib/wasi/src/bin_factory/module_cache.rs @@ -247,6 +247,8 @@ impl ModuleCache { #[cfg(test)] #[cfg(feature = "sys")] mod tests { + use std::time::Duration; + use tracing_subscriber::{ filter, prelude::__tracing_subscriber_SubscriberExt, util::SubscriberInitExt, Layer, }; @@ -275,7 +277,9 @@ mod tests { for _ in 0..2 { let webc = cache.get_webc("sharrattj/dash", &rt, &tasks).unwrap(); store.push(webc); - tasks.runtime().block_on(tasks.sleep_now(0.into(), 1000)); + tasks + .runtime() + .block_on(tasks.sleep_now(Duration::from_secs(1))); } } } diff --git a/lib/wasi/src/fs/inode_guard.rs b/lib/wasi/src/fs/inode_guard.rs index cdfe985132d..8f1668f315e 100644 --- a/lib/wasi/src/fs/inode_guard.rs +++ b/lib/wasi/src/fs/inode_guard.rs @@ -1,5 +1,4 @@ use std::{ - collections::HashMap, future::Future, io::{IoSlice, SeekFrom}, ops::{Deref, DerefMut}, @@ -39,36 +38,23 @@ pub(crate) enum InodeValFilePollGuardMode { counter: Arc, }, Socket { - inner: Arc>, - lock_state: InodeValFilePollGuardSocketLocking, + inner: Arc>, }, } -pub(crate) enum InodeValFilePollGuardSocketLocking { - Locking( - Pin< - Box< - dyn Future> - + Send - + Sync - + 'static, - >, - >, - ), - Locked(tokio::sync::OwnedRwLockWriteGuard), -} - pub(crate) struct InodeValFilePollGuard { pub(crate) fd: u32, + pub(crate) peb: PollEventSet, + pub(crate) subscription: Subscription, pub(crate) mode: InodeValFilePollGuardMode, - pub(crate) subscriptions: HashMap, } impl InodeValFilePollGuard { pub(crate) fn new( fd: u32, + peb: PollEventSet, + subscription: Subscription, guard: &Kind, - subscriptions: HashMap, ) -> Option { let mode = match guard.deref() { Kind::EventNotifications { @@ -91,22 +77,9 @@ impl InodeValFilePollGuard { counter: counter.clone(), } } - Kind::Socket { socket } => { - if let Ok(guard) = socket.inner.clone().try_write_owned() { - InodeValFilePollGuardMode::Socket { - inner: socket.inner.clone(), - lock_state: InodeValFilePollGuardSocketLocking::Locked(guard), - } - } else { - let socket = socket.clone(); - InodeValFilePollGuardMode::Socket { - inner: socket.inner.clone(), - lock_state: InodeValFilePollGuardSocketLocking::Locking(Box::pin( - socket.inner.write_owned(), - )), - } - } - } + Kind::Socket { socket } => InodeValFilePollGuardMode::Socket { + inner: socket.inner.clone(), + }, Kind::File { handle: Some(handle), .. @@ -118,7 +91,8 @@ impl InodeValFilePollGuard { Some(Self { fd, mode, - subscriptions, + peb, + subscription, }) } } @@ -130,23 +104,23 @@ impl std::fmt::Debug for InodeValFilePollGuard { InodeValFilePollGuardMode::EventNotifications { .. } => { write!(f, "guard-notifications") } - InodeValFilePollGuardMode::Socket { lock_state, .. } => match lock_state { - InodeValFilePollGuardSocketLocking::Locked(guard) => match guard.kind { - InodeSocketKind::TcpListener(..) => write!(f, "guard-tcp-listener"), - InodeSocketKind::TcpStream(ref stream) => { - if stream.is_closed() { + InodeValFilePollGuardMode::Socket { inner } => { + let inner = inner.read().unwrap(); + match inner.kind { + InodeSocketKind::TcpListener { .. } => write!(f, "guard-tcp-listener"), + InodeSocketKind::TcpStream { ref socket, .. } => { + if socket.is_closed() { write!(f, "guard-tcp-stream (closed)") } else { write!(f, "guard-tcp-stream") } } - InodeSocketKind::UdpSocket(..) => write!(f, "guard-udp-socket"), + InodeSocketKind::UdpSocket { .. } => write!(f, "guard-udp-socket"), InodeSocketKind::Raw(..) => write!(f, "guard-raw-socket"), InodeSocketKind::WebSocket(..) => write!(f, "guard-web-socket"), _ => write!(f, "guard-socket"), - }, - _ => write!(f, "guard-socket (locked)"), - }, + } + } } } } @@ -168,7 +142,8 @@ impl InodeValFilePollGuard { pub(crate) struct InodeValFilePollGuardJoin<'a> { mode: &'a mut InodeValFilePollGuardMode, fd: u32, - subscriptions: HashMap, + peb: PollEventSet, + subscription: Subscription, } impl<'a> InodeValFilePollGuardJoin<'a> { @@ -176,7 +151,8 @@ impl<'a> InodeValFilePollGuardJoin<'a> { Self { mode: &mut guard.mode, fd: guard.fd, - subscriptions: guard.subscriptions.clone(), + peb: guard.peb, + subscription: guard.subscription, } } pub(crate) fn fd(&self) -> u32 { @@ -185,37 +161,34 @@ impl<'a> InodeValFilePollGuardJoin<'a> { } impl<'a> Future for InodeValFilePollGuardJoin<'a> { - type Output = Vec; + type Output = Event; fn poll(mut self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll { - let mut has_read = None; - let mut has_write = None; - let mut has_close = None; + let mut has_read = false; + let mut has_write = false; + let mut has_close = false; let mut has_hangup = false; - let mut ret = Vec::new(); - for (set, s) in self.subscriptions.iter() { - for in_event in iterate_poll_events(*set) { - match in_event { - PollEvent::PollIn => { - has_read = Some(*s); - } - PollEvent::PollOut => { - has_write = Some(*s); - } - PollEvent::PollHangUp => { - has_hangup = true; - has_close = Some(*s); - } - PollEvent::PollError | PollEvent::PollInvalid => { - if !has_hangup { - has_close = Some(*s); - } + for in_event in iterate_poll_events(self.peb) { + match in_event { + PollEvent::PollIn => { + has_read = true; + } + PollEvent::PollOut => { + has_write = true; + } + PollEvent::PollHangUp => { + has_hangup = true; + has_close = true; + } + PollEvent::PollError | PollEvent::PollInvalid => { + if !has_hangup { + has_close = true; } } } } - if let Some(s) = has_close.as_ref() { + if has_close { let is_closed = match &mut self.mode { InodeValFilePollGuardMode::File(file) => { let mut guard = file.write().unwrap(); @@ -223,29 +196,11 @@ impl<'a> Future for InodeValFilePollGuardJoin<'a> { file.poll_shutdown(cx).is_ready() } InodeValFilePollGuardMode::EventNotifications { .. } => false, - InodeValFilePollGuardMode::Socket { - ref inner, - ref mut lock_state, - .. - } => { - let guard = match lock_state { - InodeValFilePollGuardSocketLocking::Locking(locking) => { - match locking.as_mut().poll(cx) { - Poll::Ready(guard) => { - *lock_state = InodeValFilePollGuardSocketLocking::Locked(guard); - match lock_state { - InodeValFilePollGuardSocketLocking::Locked(guard) => guard, - _ => unreachable!(), - } - } - Poll::Pending => return Poll::Pending, - } - } - InodeValFilePollGuardSocketLocking::Locked(guard) => guard, - }; + InodeValFilePollGuardMode::Socket { ref inner } => { + let mut guard = inner.write().unwrap(); let is_closed = if let InodeSocketKind::Closed = guard.kind { true - } else if has_read.is_some() || has_write.is_some() { + } else if has_read || has_write { // this will be handled in the read/write poll instead false } else { @@ -262,21 +217,15 @@ impl<'a> Future for InodeValFilePollGuardJoin<'a> { _ => false, } }; - // Release the lock so we don't cause any blocking issues - drop(guard); - *lock_state = InodeValFilePollGuardSocketLocking::Locking(Box::pin( - inner.clone().write_owned(), - )); - is_closed } }; if is_closed { - ret.push(Event { - userdata: s.userdata, + return Poll::Ready(Event { + userdata: self.subscription.userdata, error: Errno::Success, - type_: s.type_, - u: match s.type_ { + type_: self.subscription.type_, + u: match self.subscription.type_ { Eventtype::FdRead | Eventtype::FdWrite => EventUnion { fd_readwrite: EventFdReadwrite { nbytes: 0, @@ -292,7 +241,7 @@ impl<'a> Future for InodeValFilePollGuardJoin<'a> { }); } } - if let Some(s) = has_read { + if has_read { let mut poll_result = match &mut self.mode { InodeValFilePollGuardMode::File(file) => { let mut guard = file.write().unwrap(); @@ -318,37 +267,12 @@ impl<'a> Future for InodeValFilePollGuardJoin<'a> { }) } } - InodeValFilePollGuardMode::Socket { - ref inner, - ref mut lock_state, - } => { - let guard = match lock_state { - InodeValFilePollGuardSocketLocking::Locking(locking) => { - match locking.as_mut().poll(cx) { - Poll::Ready(guard) => { - *lock_state = InodeValFilePollGuardSocketLocking::Locked(guard); - match lock_state { - InodeValFilePollGuardSocketLocking::Locked(guard) => guard, - _ => unreachable!(), - } - } - Poll::Pending => return Poll::Pending, - } - } - InodeValFilePollGuardSocketLocking::Locked(guard) => guard, - }; - let res = guard.poll_read_ready(cx).map_err(net_error_into_io_err); - - // drop the lock so we don't block things - drop(guard); - *lock_state = InodeValFilePollGuardSocketLocking::Locking(Box::pin( - inner.clone().write_owned(), - )); - - res + InodeValFilePollGuardMode::Socket { ref inner } => { + let mut guard = inner.write().unwrap(); + guard.poll_read_ready(cx).map_err(net_error_into_io_err) } }; - if let Some(s) = has_close.as_ref() { + if has_close { poll_result = match poll_result { Poll::Ready(Err(err)) if err.kind() == std::io::ErrorKind::ConnectionAborted @@ -358,11 +282,11 @@ impl<'a> Future for InodeValFilePollGuardJoin<'a> { || err.kind() == std::io::ErrorKind::NotConnected || err.kind() == std::io::ErrorKind::UnexpectedEof => { - ret.push(Event { - userdata: s.userdata, + return Poll::Ready(Event { + userdata: self.subscription.userdata, error: Errno::Success, - type_: s.type_, - u: match s.type_ { + type_: self.subscription.type_, + u: match self.subscription.type_ { Eventtype::FdRead | Eventtype::FdWrite => EventUnion { fd_readwrite: EventFdReadwrite { nbytes: 0, @@ -376,7 +300,6 @@ impl<'a> Future for InodeValFilePollGuardJoin<'a> { Eventtype::Clock => EventUnion { clock: 0 }, }, }); - Poll::Pending } a => a, }; @@ -390,11 +313,11 @@ impl<'a> Future for InodeValFilePollGuardJoin<'a> { 0 } }; - ret.push(Event { - userdata: s.userdata, + return Poll::Ready(Event { + userdata: self.subscription.userdata, error, - type_: s.type_, - u: match s.type_ { + type_: self.subscription.type_, + u: match self.subscription.type_ { Eventtype::FdRead | Eventtype::FdWrite => EventUnion { fd_readwrite: EventFdReadwrite { nbytes: bytes_available as u64, @@ -410,7 +333,7 @@ impl<'a> Future for InodeValFilePollGuardJoin<'a> { }); } } - if let Some(s) = has_write { + if has_write { let mut poll_result = match &mut self.mode { InodeValFilePollGuardMode::File(file) => { let mut guard = file.write().unwrap(); @@ -436,37 +359,12 @@ impl<'a> Future for InodeValFilePollGuardJoin<'a> { }) } } - InodeValFilePollGuardMode::Socket { - ref inner, - ref mut lock_state, - } => { - let guard = match lock_state { - InodeValFilePollGuardSocketLocking::Locking(locking) => { - match locking.as_mut().poll(cx) { - Poll::Ready(guard) => { - *lock_state = InodeValFilePollGuardSocketLocking::Locked(guard); - match lock_state { - InodeValFilePollGuardSocketLocking::Locked(guard) => guard, - _ => unreachable!(), - } - } - Poll::Pending => return Poll::Pending, - } - } - InodeValFilePollGuardSocketLocking::Locked(guard) => guard, - }; - let res = guard.poll_write_ready(cx).map_err(net_error_into_io_err); - - // drop the lock so we don't block things - drop(guard); - *lock_state = InodeValFilePollGuardSocketLocking::Locking(Box::pin( - inner.clone().write_owned(), - )); - - res + InodeValFilePollGuardMode::Socket { ref inner } => { + let mut guard = inner.write().unwrap(); + guard.poll_write_ready(cx).map_err(net_error_into_io_err) } }; - if let Some(s) = has_close.as_ref() { + if has_close { poll_result = match poll_result { Poll::Ready(Err(err)) if err.kind() == std::io::ErrorKind::ConnectionAborted @@ -476,11 +374,11 @@ impl<'a> Future for InodeValFilePollGuardJoin<'a> { || err.kind() == std::io::ErrorKind::NotConnected || err.kind() == std::io::ErrorKind::UnexpectedEof => { - ret.push(Event { - userdata: s.userdata, + return Poll::Ready(Event { + userdata: self.subscription.userdata, error: Errno::Success, - type_: s.type_, - u: match s.type_ { + type_: self.subscription.type_, + u: match self.subscription.type_ { Eventtype::FdRead | Eventtype::FdWrite => EventUnion { fd_readwrite: EventFdReadwrite { nbytes: 0, @@ -494,7 +392,6 @@ impl<'a> Future for InodeValFilePollGuardJoin<'a> { Eventtype::Clock => EventUnion { clock: 0 }, }, }); - Poll::Pending } a => a, }; @@ -508,11 +405,11 @@ impl<'a> Future for InodeValFilePollGuardJoin<'a> { 0 } }; - ret.push(Event { - userdata: s.userdata, + return Poll::Ready(Event { + userdata: self.subscription.userdata, error, - type_: s.type_, - u: match s.type_ { + type_: self.subscription.type_, + u: match self.subscription.type_ { Eventtype::FdRead | Eventtype::FdWrite => EventUnion { fd_readwrite: EventFdReadwrite { nbytes: bytes_available as u64, @@ -528,11 +425,7 @@ impl<'a> Future for InodeValFilePollGuardJoin<'a> { }); } } - if !ret.is_empty() { - Poll::Ready(ret) - } else { - Poll::Pending - } + Poll::Pending } } @@ -557,11 +450,13 @@ impl InodeValFileReadGuard { pub fn into_poll_guard( self, fd: u32, - subscriptions: HashMap, + peb: PollEventSet, + subscription: Subscription, ) -> InodeValFilePollGuard { InodeValFilePollGuard { fd, - subscriptions, + peb, + subscription, mode: InodeValFilePollGuardMode::File(self.file), } } diff --git a/lib/wasi/src/fs/mod.rs b/lib/wasi/src/fs/mod.rs index 3fdb686bc97..c8ef24fde15 100644 --- a/lib/wasi/src/fs/mod.rs +++ b/lib/wasi/src/fs/mod.rs @@ -515,7 +515,9 @@ impl WasiFs { if path.starts_with("./") { let current_dir = self.current_dir.lock().unwrap(); path = format!("{}{}", current_dir.as_str(), &path[1..]); - path = path.replace("//", "/"); + if path.contains("//") { + path = path.replace("//", "/"); + } } path } diff --git a/lib/wasi/src/net/socket.rs b/lib/wasi/src/net/socket.rs index 8be78835fa0..1e6add59d67 100644 --- a/lib/wasi/src/net/socket.rs +++ b/lib/wasi/src/net/socket.rs @@ -1,27 +1,25 @@ use std::{ future::Future, + mem::MaybeUninit, net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}, - ops::DerefMut, pin::Pin, - sync::Arc, + sync::{Arc, RwLock}, task::Poll, time::Duration, }; -use bytes::{Buf, Bytes}; #[cfg(feature = "enable-serde")] use serde_derive::{Deserialize, Serialize}; -use tokio::sync::OwnedRwLockWriteGuard; use wasmer_types::MemorySize; use wasmer_vnet::{ - DynVirtualNetworking, TimeType, VirtualConnectedSocket, VirtualIcmpSocket, VirtualRawSocket, - VirtualTcpListener, VirtualTcpSocket, VirtualUdpSocket, VirtualWebSocket, + VirtualIcmpSocket, VirtualNetworking, VirtualRawSocket, VirtualTcpListener, VirtualTcpSocket, + VirtualUdpSocket, VirtualWebSocket, }; use wasmer_wasi_types::wasi::{ Addressfamily, Errno, Fdflags, Rights, SockProto, Sockoption, Socktype, }; -use crate::net::net_error_into_wasi_err; +use crate::{net::net_error_into_wasi_err, VirtualTaskManager}; #[derive(Debug)] #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] @@ -45,20 +43,29 @@ pub enum InodeSocketKind { only_v6: bool, reuse_port: bool, reuse_addr: bool, - nonblocking: bool, send_buf_size: Option, recv_buf_size: Option, - send_timeout: Option, - recv_timeout: Option, - connect_timeout: Option, + write_timeout: Option, + read_timeout: Option, accept_timeout: Option, + connect_timeout: Option, }, WebSocket(Box), Icmp(Box), Raw(Box), - TcpListener(Box), - TcpStream(Box), - UdpSocket(Box), + TcpListener { + socket: Box, + accept_timeout: Option, + }, + TcpStream { + socket: Box, + write_timeout: Option, + read_timeout: Option, + }, + UdpSocket { + socket: Box, + peer: Option, + }, Closed, } @@ -135,236 +142,227 @@ pub enum WasiSocketStatus { Failed, } +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum TimeType { + ReadTimeout, + WriteTimeout, + AcceptTimeout, + ConnectTimeout, + BindTimeout, + Linger, +} + #[derive(Debug)] //#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] pub(crate) struct InodeSocketInner { pub kind: InodeSocketKind, - pub read_buffer: Option, - pub read_addr: Option, } #[derive(Debug, Clone)] //#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] pub struct InodeSocket { - pub(crate) inner: Arc>, + pub(crate) inner: Arc>, } impl InodeSocket { pub fn new(kind: InodeSocketKind) -> Self { Self { - inner: Arc::new(tokio::sync::RwLock::new(InodeSocketInner { - kind, - read_buffer: None, - read_addr: None, - })), + inner: Arc::new(RwLock::new(InodeSocketInner { kind })), } } pub async fn bind( &self, - net: DynVirtualNetworking, + tasks: &dyn VirtualTaskManager, + net: &dyn VirtualNetworking, set_addr: SocketAddr, ) -> Result, Errno> { - let mut inner = self.inner.write().await; - match &mut inner.kind { - InodeSocketKind::PreSocket { - family, - ty, - addr, - reuse_port, - reuse_addr, - nonblocking, - .. - } => { - match *family { - Addressfamily::Inet4 => { - if !set_addr.is_ipv4() { - return Err(Errno::Inval); + let timeout = self + .opt_time(TimeType::BindTimeout) + .ok() + .flatten() + .unwrap_or(Duration::from_secs(30)); + + let socket = { + let mut inner = self.inner.write().unwrap(); + match &mut inner.kind { + InodeSocketKind::PreSocket { + family, + ty, + addr, + reuse_port, + reuse_addr, + .. + } => { + match *family { + Addressfamily::Inet4 => { + if !set_addr.is_ipv4() { + return Err(Errno::Inval); + } } - } - Addressfamily::Inet6 => { - if !set_addr.is_ipv6() { - return Err(Errno::Inval); + Addressfamily::Inet6 => { + if !set_addr.is_ipv6() { + return Err(Errno::Inval); + } + } + _ => { + return Err(Errno::Notsup); } } - _ => { - return Err(Errno::Notsup); - } - } - addr.replace(set_addr); - let addr = (*addr).unwrap(); + addr.replace(set_addr); + let addr = (*addr).unwrap(); - Ok(match *ty { - Socktype::Stream => { - // we already set the socket address - next we need a bind or connect so nothing - // more to do at this time - None - } - Socktype::Dgram => { - let mut socket = net - .bind_udp(addr, *reuse_port, *reuse_addr) - .await - .map_err(net_error_into_wasi_err)?; - socket - .set_nonblocking(*nonblocking) - .map_err(net_error_into_wasi_err)?; - Some(InodeSocket::new(InodeSocketKind::UdpSocket(socket))) + match *ty { + Socktype::Stream => { + // we already set the socket address - next we need a bind or connect so nothing + // more to do at this time + return Ok(None); + } + Socktype::Dgram => { + let reuse_port = *reuse_port; + let reuse_addr = *reuse_addr; + drop(inner); + + net.bind_udp(addr, reuse_port, reuse_addr) + } + _ => return Err(Errno::Inval), } - _ => return Err(Errno::Inval), - }) + } + _ => return Err(Errno::Notsup), } - _ => Err(Errno::Notsup), + }; + + tokio::select! { + socket = socket => { + let socket = socket.map_err(net_error_into_wasi_err)?; + Ok(Some(InodeSocket::new(InodeSocketKind::UdpSocket { socket, peer: None }))) + }, + _ = tasks.sleep_now(timeout) => Err(Errno::Timedout) } } pub async fn listen( &self, - net: DynVirtualNetworking, + tasks: &dyn VirtualTaskManager, + net: &dyn VirtualNetworking, _backlog: usize, ) -> Result, Errno> { - let inner = self.inner.read().await; - match &inner.kind { - InodeSocketKind::PreSocket { - ty, - addr, - only_v6, - reuse_port, - reuse_addr, - accept_timeout, - nonblocking, - .. - } => Ok(match *ty { - Socktype::Stream => { - if addr.is_none() { - tracing::warn!("wasi[?]::sock_listen - failed - address not set"); - return Err(Errno::Inval); + let timeout = self + .opt_time(TimeType::AcceptTimeout) + .ok() + .flatten() + .unwrap_or(Duration::from_secs(30)); + + let socket = { + let inner = self.inner.read().unwrap(); + match &inner.kind { + InodeSocketKind::PreSocket { + ty, + addr, + only_v6, + reuse_port, + reuse_addr, + .. + } => match *ty { + Socktype::Stream => { + if addr.is_none() { + tracing::warn!("wasi[?]::sock_listen - failed - address not set"); + return Err(Errno::Inval); + } + let addr = *addr.as_ref().unwrap(); + let only_v6 = *only_v6; + let reuse_port = *reuse_port; + let reuse_addr = *reuse_addr; + drop(inner); + + net.listen_tcp(addr, only_v6, reuse_port, reuse_addr) } - let addr = *addr.as_ref().unwrap(); - let mut socket = net - .listen_tcp(addr, *only_v6, *reuse_port, *reuse_addr) - .await - .map_err(|err| { - tracing::warn!("wasi[?]::sock_listen - failed - {}", err); - net_error_into_wasi_err(err) - })?; - socket - .set_nonblocking(*nonblocking) - .map_err(net_error_into_wasi_err)?; - if let Some(accept_timeout) = accept_timeout { - socket - .set_timeout(Some(*accept_timeout)) - .map_err(net_error_into_wasi_err)?; + _ => { + tracing::warn!("wasi[?]::sock_listen - failed - not supported(1)"); + return Err(Errno::Notsup); } - Some(InodeSocket::new(InodeSocketKind::TcpListener(socket))) + }, + InodeSocketKind::Closed => { + tracing::warn!("wasi[?]::sock_listen - failed - socket closed"); + return Err(Errno::Io); } _ => { - tracing::warn!("wasi[?]::sock_listen - failed - not supported(1)"); + tracing::warn!("wasi[?]::sock_listen - failed - not supported(2)"); return Err(Errno::Notsup); } - }), - InodeSocketKind::Closed => { - tracing::warn!("wasi[?]::sock_listen - failed - socket closed"); - Err(Errno::Io) - } - _ => { - tracing::warn!("wasi[?]::sock_listen - failed - not supported(2)"); - Err(Errno::Notsup) } + }; + + tokio::select! { + socket = socket => { + let socket = socket.map_err(net_error_into_wasi_err)?; + Ok(Some(InodeSocket::new(InodeSocketKind::TcpListener { + socket, + accept_timeout: Some(timeout), + }))) + }, + _ = tasks.sleep_now(timeout) => Err(Errno::Timedout) } } pub async fn accept( &self, + tasks: &dyn VirtualTaskManager, fd_flags: Fdflags, ) -> Result<(Box, SocketAddr), Errno> { let nonblocking = fd_flags.contains(Fdflags::NONBLOCK); - let timeout = self.opt_time(TimeType::AcceptTimeout).await?; + let timeout = self + .opt_time(TimeType::AcceptTimeout) + .ok() + .flatten() + .unwrap_or(Duration::from_secs(30)); + struct SocketAccepter<'a> { sock: &'a InodeSocket, nonblocking: bool, - next_lock: - Option>>>>, } impl<'a> Future for SocketAccepter<'a> { type Output = Result<(Box, SocketAddr), Errno>; fn poll( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, cx: &mut std::task::Context<'_>, ) -> std::task::Poll { - if self.next_lock.is_none() { - let inner = self.sock.inner.clone(); - self.next_lock.replace(Box::pin(inner.write_owned())); - } - let next_lock = self.next_lock.as_mut().unwrap().as_mut(); - match next_lock.poll(cx) { - Poll::Ready(mut inner) => { - self.next_lock.take(); - match &mut inner.kind { - InodeSocketKind::TcpListener(sock) => { - if self.nonblocking { - match sock.try_accept() { - Some(Ok((mut child, addr))) => { - if let Err(err) = child.set_nonblocking(true) { - child.close().ok(); - return Poll::Ready(Err(net_error_into_wasi_err( - err, - ))); - } - Poll::Ready(Ok((child, addr))) - } - Some(Err(err)) => { - Poll::Ready(Err(net_error_into_wasi_err(err))) - } - None => Poll::Ready(Err(Errno::Again)), - } - } else { - sock.poll_accept(cx).map_err(net_error_into_wasi_err) - } + let mut inner = self.sock.inner.write().unwrap(); + match &mut inner.kind { + InodeSocketKind::TcpListener { socket, .. } => { + if self.nonblocking { + match socket.try_accept() { + Some(Ok((child, addr))) => Poll::Ready(Ok((child, addr))), + Some(Err(err)) => Poll::Ready(Err(net_error_into_wasi_err(err))), + None => Poll::Ready(Err(Errno::Again)), } - InodeSocketKind::PreSocket { .. } => Poll::Ready(Err(Errno::Notconn)), - InodeSocketKind::Closed => Poll::Ready(Err(Errno::Io)), - _ => Poll::Ready(Err(Errno::Notsup)), + } else { + socket.poll_accept(cx).map_err(net_error_into_wasi_err) } } - Poll::Pending => Poll::Pending, + InodeSocketKind::PreSocket { .. } => Poll::Ready(Err(Errno::Notconn)), + InodeSocketKind::Closed => Poll::Ready(Err(Errno::Io)), + _ => Poll::Ready(Err(Errno::Notsup)), } } } - if let Some(timeout) = timeout { - #[cfg(not(feature = "js"))] - tokio::select! { - res = SocketAccepter { sock: self, nonblocking, next_lock: None } => res, - _ = tokio::time::sleep(timeout) => Err(Errno::Timedout) - } - // FIXME: enable timeouts for JS! (sleep not available in js tokio) - #[cfg(feature = "js")] - { - let _ = timeout; - - tokio::select! { - res = SocketAccepter { sock: self, next_lock: None, nonblocking } => res, - } - } - } else { - tokio::select! { - res = SocketAccepter { sock: self, nonblocking, next_lock: None } => res, - } + tokio::select! { + res = SocketAccepter { sock: self, nonblocking } => res, + _ = tasks.sleep_now(timeout) => Err(Errno::Timedout) } } - pub async fn close(&self) -> Result<(), Errno> { - let mut inner = self.inner.write().await; + pub fn close(&self) -> Result<(), Errno> { + let mut inner = self.inner.write().unwrap(); match &mut inner.kind { - InodeSocketKind::TcpListener(_) => {} - InodeSocketKind::TcpStream(sock) => { - sock.close().map_err(net_error_into_wasi_err)?; + InodeSocketKind::TcpListener { .. } => {} + InodeSocketKind::TcpStream { socket, .. } => { + socket.close().map_err(net_error_into_wasi_err)?; } InodeSocketKind::Icmp(_) => {} - InodeSocketKind::UdpSocket(sock) => { - sock.close().map_err(net_error_into_wasi_err)?; - } + InodeSocketKind::UdpSocket { .. } => {} InodeSocketKind::WebSocket(_) => {} InodeSocketKind::Raw(_) => {} InodeSocketKind::PreSocket { .. } => return Err(Errno::Notconn), @@ -373,102 +371,121 @@ impl InodeSocket { Ok(()) } - pub async fn flush(&self) -> Result<(), Errno> { - let mut inner = self.inner.write().await; - match &mut inner.kind { - InodeSocketKind::TcpListener(_) => {} - InodeSocketKind::TcpStream(sock) => { - VirtualConnectedSocket::flush(sock.deref_mut()) - .await - .map_err(net_error_into_wasi_err)?; - } - InodeSocketKind::Icmp(_) => {} - InodeSocketKind::UdpSocket(sock) => { - VirtualConnectedSocket::flush(sock.as_mut()) - .await - .map_err(net_error_into_wasi_err)?; - } - InodeSocketKind::WebSocket(_) => {} - InodeSocketKind::Raw(sock) => { - VirtualRawSocket::flush(sock.deref_mut()) - .await - .map_err(net_error_into_wasi_err)?; + pub async fn flush(&self, tasks: &dyn VirtualTaskManager) -> Result<(), Errno> { + let timeout = self + .opt_time(TimeType::WriteTimeout) + .ok() + .flatten() + .unwrap_or(Duration::from_secs(30)); + + #[derive(Debug)] + struct SocketFlusher<'a> { + sock: &'a RwLock, + } + impl<'a> Future for SocketFlusher<'a> { + type Output = Result<(), Errno>; + fn poll(self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll { + let mut inner = self.sock.write().unwrap(); + match &mut inner.kind { + InodeSocketKind::TcpListener { .. } => Poll::Ready(Ok(())), + InodeSocketKind::TcpStream { socket, .. } => { + socket.poll_flush(cx).map_err(net_error_into_wasi_err) + } + InodeSocketKind::Icmp(_) => Poll::Ready(Ok(())), + InodeSocketKind::UdpSocket { .. } => Poll::Ready(Ok(())), + InodeSocketKind::WebSocket(_) => Poll::Ready(Ok(())), + InodeSocketKind::Raw(_) => Poll::Ready(Ok(())), + InodeSocketKind::PreSocket { .. } => Poll::Ready(Err(Errno::Notconn)), + InodeSocketKind::Closed => Poll::Ready(Err(Errno::Notconn)), + } } - InodeSocketKind::PreSocket { .. } => return Err(Errno::Notconn), - InodeSocketKind::Closed => return Err(Errno::Notconn), - }; - Ok(()) + } + + tokio::select! { + res = SocketFlusher { sock: &self.inner } => res, + _ = tasks.sleep_now(timeout) => Err(Errno::Timedout) + } } pub async fn connect( &mut self, - net: DynVirtualNetworking, + tasks: &dyn VirtualTaskManager, + net: &dyn VirtualNetworking, peer: SocketAddr, + timeout: Option, ) -> Result, Errno> { - let mut inner = self.inner.write().await; - match &mut inner.kind { - InodeSocketKind::PreSocket { - ty, - addr, - send_timeout, - recv_timeout, - connect_timeout, - .. - } => Ok(match *ty { - Socktype::Stream => { - let addr = match addr { - Some(a) => *a, - None => { - let ip = match peer.is_ipv4() { - true => IpAddr::V4(Ipv4Addr::UNSPECIFIED), - false => IpAddr::V6(Ipv6Addr::UNSPECIFIED), + let new_write_timeout; + let new_read_timeout; + + let timeout = timeout.unwrap_or(Duration::from_secs(30)); + + let connect = { + let mut inner = self.inner.write().unwrap(); + match &mut inner.kind { + InodeSocketKind::PreSocket { + ty, + addr, + write_timeout, + read_timeout, + .. + } => { + new_write_timeout = *write_timeout; + new_read_timeout = *read_timeout; + match *ty { + Socktype::Stream => { + let addr = match addr { + Some(a) => *a, + None => { + let ip = match peer.is_ipv4() { + true => IpAddr::V4(Ipv4Addr::UNSPECIFIED), + false => IpAddr::V6(Ipv6Addr::UNSPECIFIED), + }; + SocketAddr::new(ip, 0) + } }; - SocketAddr::new(ip, 0) + net.connect_tcp(addr, peer) } - }; - let mut socket = net - .connect_tcp(addr, peer, *connect_timeout) - .await - .map_err(net_error_into_wasi_err)?; - if let Some(timeout) = send_timeout { - socket - .set_opt_time(TimeType::WriteTimeout, Some(*timeout)) - .map_err(net_error_into_wasi_err)?; + Socktype::Dgram => return Err(Errno::Inval), + _ => return Err(Errno::Notsup), } - if let Some(timeout) = recv_timeout { - socket - .set_opt_time(TimeType::ReadTimeout, Some(*timeout)) - .map_err(net_error_into_wasi_err)?; - } - Some(InodeSocket::new(InodeSocketKind::TcpStream(socket))) } - Socktype::Dgram => return Err(Errno::Inval), + InodeSocketKind::UdpSocket { + peer: target_peer, .. + } => { + target_peer.replace(peer); + return Ok(None); + } + InodeSocketKind::Closed => return Err(Errno::Io), _ => return Err(Errno::Notsup), - }), - InodeSocketKind::UdpSocket(sock) => { - sock.connect(peer).await.map_err(net_error_into_wasi_err)?; - Ok(None) } - InodeSocketKind::Closed => Err(Errno::Io), - _ => Err(Errno::Notsup), - } + }; + + let socket = tokio::select! { + res = connect => res.map_err(net_error_into_wasi_err)?, + _ = tasks.sleep_now(timeout) => return Err(Errno::Timedout) + }; + Ok(Some(InodeSocket::new(InodeSocketKind::TcpStream { + socket, + write_timeout: new_write_timeout, + read_timeout: new_read_timeout, + }))) } - pub async fn status(&self) -> Result { - let inner = self.inner.read().await; + pub fn status(&self) -> Result { + let inner = self.inner.read().unwrap(); Ok(match &inner.kind { InodeSocketKind::PreSocket { .. } => WasiSocketStatus::Opening, InodeSocketKind::WebSocket(_) => WasiSocketStatus::Opened, - InodeSocketKind::TcpListener(_) => WasiSocketStatus::Opened, - InodeSocketKind::TcpStream(_) => WasiSocketStatus::Opened, - InodeSocketKind::UdpSocket(_) => WasiSocketStatus::Opened, + InodeSocketKind::TcpListener { .. } => WasiSocketStatus::Opened, + InodeSocketKind::TcpStream { .. } => WasiSocketStatus::Opened, + InodeSocketKind::UdpSocket { .. } => WasiSocketStatus::Opened, InodeSocketKind::Closed => WasiSocketStatus::Closed, _ => WasiSocketStatus::Failed, }) } - pub async fn addr_local(&self) -> Result { - let inner = self.inner.read().await; + pub fn addr_local(&self) -> Result { + let inner = self.inner.read().unwrap(); Ok(match &inner.kind { InodeSocketKind::PreSocket { family, addr, .. } => { if let Some(addr) = addr { @@ -485,22 +502,22 @@ impl InodeSocket { } } InodeSocketKind::Icmp(sock) => sock.addr_local().map_err(net_error_into_wasi_err)?, - InodeSocketKind::TcpListener(sock) => { - sock.addr_local().map_err(net_error_into_wasi_err)? + InodeSocketKind::TcpListener { socket, .. } => { + socket.addr_local().map_err(net_error_into_wasi_err)? } - InodeSocketKind::TcpStream(sock) => { - sock.addr_local().map_err(net_error_into_wasi_err)? + InodeSocketKind::TcpStream { socket, .. } => { + socket.addr_local().map_err(net_error_into_wasi_err)? } - InodeSocketKind::UdpSocket(sock) => { - sock.addr_local().map_err(net_error_into_wasi_err)? + InodeSocketKind::UdpSocket { socket, .. } => { + socket.addr_local().map_err(net_error_into_wasi_err)? } InodeSocketKind::Closed => return Err(Errno::Io), _ => return Err(Errno::Notsup), }) } - pub async fn addr_peer(&self) -> Result { - let inner = self.inner.read().await; + pub fn addr_peer(&self) -> Result { + let inner = self.inner.read().unwrap(); Ok(match &inner.kind { InodeSocketKind::PreSocket { family, .. } => SocketAddr::new( match *family { @@ -510,15 +527,16 @@ impl InodeSocket { }, 0, ), - InodeSocketKind::TcpStream(sock) => { - sock.addr_peer().map_err(net_error_into_wasi_err)? + InodeSocketKind::TcpStream { socket, .. } => { + socket.addr_peer().map_err(net_error_into_wasi_err)? } - InodeSocketKind::UdpSocket(sock) => sock + InodeSocketKind::UdpSocket { socket, .. } => socket .addr_peer() .map_err(net_error_into_wasi_err)? .map(Ok) .unwrap_or_else(|| { - sock.addr_local() + socket + .addr_local() .map_err(net_error_into_wasi_err) .map(|addr| { SocketAddr::new( @@ -535,8 +553,8 @@ impl InodeSocket { }) } - pub async fn set_opt_flag(&mut self, option: WasiSocketOption, val: bool) -> Result<(), Errno> { - let mut inner = self.inner.write().await; + pub fn set_opt_flag(&mut self, option: WasiSocketOption, val: bool) -> Result<(), Errno> { + let mut inner = self.inner.write().unwrap(); match &mut inner.kind { InodeSocketKind::PreSocket { only_v6, @@ -552,27 +570,25 @@ impl InodeSocket { }; } InodeSocketKind::Raw(sock) => match option { - WasiSocketOption::Promiscuous => sock - .set_promiscuous(val) - .await - .map_err(net_error_into_wasi_err)?, + WasiSocketOption::Promiscuous => { + sock.set_promiscuous(val).map_err(net_error_into_wasi_err)? + } _ => return Err(Errno::Inval), }, - InodeSocketKind::TcpStream(sock) => match option { - WasiSocketOption::NoDelay => sock - .set_nodelay(val) - .await - .map_err(net_error_into_wasi_err)?, + InodeSocketKind::TcpStream { socket, .. } => match option { + WasiSocketOption::NoDelay => { + socket.set_nodelay(val).map_err(net_error_into_wasi_err)? + } _ => return Err(Errno::Inval), }, - InodeSocketKind::UdpSocket(sock) => match option { + InodeSocketKind::UdpSocket { socket, .. } => match option { WasiSocketOption::Broadcast => { - sock.set_broadcast(val).map_err(net_error_into_wasi_err)? + socket.set_broadcast(val).map_err(net_error_into_wasi_err)? } - WasiSocketOption::MulticastLoopV4 => sock + WasiSocketOption::MulticastLoopV4 => socket .set_multicast_loop_v4(val) .map_err(net_error_into_wasi_err)?, - WasiSocketOption::MulticastLoopV6 => sock + WasiSocketOption::MulticastLoopV6 => socket .set_multicast_loop_v6(val) .map_err(net_error_into_wasi_err)?, _ => return Err(Errno::Inval), @@ -583,8 +599,8 @@ impl InodeSocket { Ok(()) } - pub async fn get_opt_flag(&self, option: WasiSocketOption) -> Result { - let mut inner = self.inner.write().await; + pub fn get_opt_flag(&self, option: WasiSocketOption) -> Result { + let mut inner = self.inner.write().unwrap(); Ok(match &mut inner.kind { InodeSocketKind::PreSocket { only_v6, @@ -603,18 +619,20 @@ impl InodeSocket { } _ => return Err(Errno::Inval), }, - InodeSocketKind::TcpStream(sock) => match option { - WasiSocketOption::NoDelay => sock.nodelay().map_err(net_error_into_wasi_err)?, + InodeSocketKind::TcpStream { socket, .. } => match option { + WasiSocketOption::NoDelay => socket.nodelay().map_err(net_error_into_wasi_err)?, _ => return Err(Errno::Inval), }, - InodeSocketKind::UdpSocket(sock) => match option { - WasiSocketOption::Broadcast => sock.broadcast().map_err(net_error_into_wasi_err)?, - WasiSocketOption::MulticastLoopV4 => { - sock.multicast_loop_v4().map_err(net_error_into_wasi_err)? - } - WasiSocketOption::MulticastLoopV6 => { - sock.multicast_loop_v6().map_err(net_error_into_wasi_err)? + InodeSocketKind::UdpSocket { socket, .. } => match option { + WasiSocketOption::Broadcast => { + socket.broadcast().map_err(net_error_into_wasi_err)? } + WasiSocketOption::MulticastLoopV4 => socket + .multicast_loop_v4() + .map_err(net_error_into_wasi_err)?, + WasiSocketOption::MulticastLoopV6 => socket + .multicast_loop_v6() + .map_err(net_error_into_wasi_err)?, _ => return Err(Errno::Inval), }, InodeSocketKind::Closed => return Err(Errno::Io), @@ -622,14 +640,15 @@ impl InodeSocket { }) } - pub async fn set_send_buf_size(&mut self, size: usize) -> Result<(), Errno> { - let mut inner = self.inner.write().await; + pub fn set_send_buf_size(&mut self, size: usize) -> Result<(), Errno> { + let mut inner = self.inner.write().unwrap(); match &mut inner.kind { InodeSocketKind::PreSocket { send_buf_size, .. } => { *send_buf_size = Some(size); } - InodeSocketKind::TcpStream(sock) => { - sock.set_send_buf_size(size) + InodeSocketKind::TcpStream { socket, .. } => { + socket + .set_send_buf_size(size) .map_err(net_error_into_wasi_err)?; } InodeSocketKind::Closed => return Err(Errno::Io), @@ -638,28 +657,29 @@ impl InodeSocket { Ok(()) } - pub async fn send_buf_size(&self) -> Result { - let inner = self.inner.read().await; + pub fn send_buf_size(&self) -> Result { + let inner = self.inner.read().unwrap(); match &inner.kind { InodeSocketKind::PreSocket { send_buf_size, .. } => { Ok((*send_buf_size).unwrap_or_default()) } - InodeSocketKind::TcpStream(sock) => { - sock.send_buf_size().map_err(net_error_into_wasi_err) + InodeSocketKind::TcpStream { socket, .. } => { + socket.send_buf_size().map_err(net_error_into_wasi_err) } InodeSocketKind::Closed => Err(Errno::Io), _ => Err(Errno::Notsup), } } - pub async fn set_recv_buf_size(&mut self, size: usize) -> Result<(), Errno> { - let mut inner = self.inner.write().await; + pub fn set_recv_buf_size(&mut self, size: usize) -> Result<(), Errno> { + let mut inner = self.inner.write().unwrap(); match &mut inner.kind { InodeSocketKind::PreSocket { recv_buf_size, .. } => { *recv_buf_size = Some(size); } - InodeSocketKind::TcpStream(sock) => { - sock.set_recv_buf_size(size) + InodeSocketKind::TcpStream { socket, .. } => { + socket + .set_recv_buf_size(size) .map_err(net_error_into_wasi_err)?; } InodeSocketKind::Closed => return Err(Errno::Io), @@ -668,25 +688,25 @@ impl InodeSocket { Ok(()) } - pub async fn recv_buf_size(&self) -> Result { - let inner = self.inner.read().await; + pub fn recv_buf_size(&self) -> Result { + let inner = self.inner.read().unwrap(); match &inner.kind { InodeSocketKind::PreSocket { recv_buf_size, .. } => { Ok((*recv_buf_size).unwrap_or_default()) } - InodeSocketKind::TcpStream(sock) => { - sock.recv_buf_size().map_err(net_error_into_wasi_err) + InodeSocketKind::TcpStream { socket, .. } => { + socket.recv_buf_size().map_err(net_error_into_wasi_err) } InodeSocketKind::Closed => Err(Errno::Io), _ => Err(Errno::Notsup), } } - pub async fn set_linger(&mut self, linger: Option) -> Result<(), Errno> { - let mut inner = self.inner.write().await; + pub fn set_linger(&mut self, linger: Option) -> Result<(), Errno> { + let mut inner = self.inner.write().unwrap(); match &mut inner.kind { - InodeSocketKind::TcpStream(sock) => { - sock.set_linger(linger).map_err(net_error_into_wasi_err) + InodeSocketKind::TcpStream { socket, .. } => { + socket.set_linger(linger).map_err(net_error_into_wasi_err) } InodeSocketKind::PreSocket { .. } => Err(Errno::Io), InodeSocketKind::Closed => Err(Errno::Io), @@ -694,132 +714,92 @@ impl InodeSocket { } } - pub async fn nonblocking(&self) -> Result { - let inner = self.inner.read().await; - Ok(match &inner.kind { - InodeSocketKind::TcpStream(sock) => { - sock.nonblocking().map_err(net_error_into_wasi_err)? - } - InodeSocketKind::TcpListener(sock, ..) => { - sock.nonblocking().map_err(net_error_into_wasi_err)? - } - InodeSocketKind::UdpSocket(sock, ..) => { - sock.nonblocking().map_err(net_error_into_wasi_err)? - } - InodeSocketKind::Raw(sock, ..) => { - sock.nonblocking().map_err(net_error_into_wasi_err)? - } - InodeSocketKind::Icmp(sock, ..) => { - sock.nonblocking().map_err(net_error_into_wasi_err)? - } - InodeSocketKind::PreSocket { nonblocking, .. } => *nonblocking, - _ => { - return Err(Errno::Notsup); - } - }) - } - - pub async fn set_nonblocking(&self, val: bool) -> Result<(), Errno> { - let mut inner = self.inner.write().await; - match &mut inner.kind { - InodeSocketKind::TcpStream(sock) => { - sock.set_nonblocking(val).map_err(net_error_into_wasi_err) - } - InodeSocketKind::TcpListener(sock, ..) => { - sock.set_nonblocking(val).map_err(net_error_into_wasi_err) - } - InodeSocketKind::UdpSocket(sock, ..) => { - sock.set_nonblocking(val).map_err(net_error_into_wasi_err) - } - InodeSocketKind::Raw(sock, ..) => { - sock.set_nonblocking(val).map_err(net_error_into_wasi_err) - } - InodeSocketKind::Icmp(sock, ..) => { - sock.set_nonblocking(val).map_err(net_error_into_wasi_err) - } - InodeSocketKind::PreSocket { nonblocking, .. } => { - (*nonblocking) = val; - Ok(()) - } - _ => Err(Errno::Notsup), - } - } - - pub async fn linger(&self) -> Result, Errno> { - let inner = self.inner.read().await; + pub fn linger(&self) -> Result, Errno> { + let inner = self.inner.read().unwrap(); match &inner.kind { - InodeSocketKind::TcpStream(sock) => sock.linger().map_err(net_error_into_wasi_err), + InodeSocketKind::TcpStream { socket, .. } => { + socket.linger().map_err(net_error_into_wasi_err) + } InodeSocketKind::PreSocket { .. } => Err(Errno::Io), InodeSocketKind::Closed => Err(Errno::Io), _ => Err(Errno::Notsup), } } - pub async fn set_opt_time( + pub fn set_opt_time( &self, ty: TimeType, timeout: Option, ) -> Result<(), Errno> { - let mut inner = self.inner.write().await; + let mut inner = self.inner.write().unwrap(); match &mut inner.kind { - InodeSocketKind::TcpStream(sock) => sock - .set_opt_time(ty, timeout) - .map_err(net_error_into_wasi_err), - InodeSocketKind::TcpListener(sock) => match ty { - TimeType::AcceptTimeout => { - sock.set_timeout(timeout).map_err(net_error_into_wasi_err) + InodeSocketKind::TcpStream { + write_timeout, + read_timeout, + .. + } => { + match ty { + TimeType::WriteTimeout => *write_timeout = timeout, + TimeType::ReadTimeout => *read_timeout = timeout, + _ => return Err(Errno::Inval), } - _ => Err(Errno::Inval), - }, + Ok(()) + } + InodeSocketKind::TcpListener { accept_timeout, .. } => { + match ty { + TimeType::AcceptTimeout => *accept_timeout = timeout, + _ => return Err(Errno::Inval), + } + Ok(()) + } InodeSocketKind::PreSocket { - recv_timeout, - send_timeout, + read_timeout, + write_timeout, connect_timeout, accept_timeout, .. - } => match ty { - TimeType::ConnectTimeout => { - *connect_timeout = timeout; - Ok(()) - } - TimeType::AcceptTimeout => { - *accept_timeout = timeout; - Ok(()) - } - TimeType::ReadTimeout => { - *recv_timeout = timeout; - Ok(()) - } - TimeType::WriteTimeout => { - *send_timeout = timeout; - Ok(()) + } => { + match ty { + TimeType::ConnectTimeout => *connect_timeout = timeout, + TimeType::AcceptTimeout => *accept_timeout = timeout, + TimeType::ReadTimeout => *read_timeout = timeout, + TimeType::WriteTimeout => *write_timeout = timeout, + _ => return Err(Errno::Io), } - _ => Err(Errno::Io), - }, + Ok(()) + } InodeSocketKind::Closed => Err(Errno::Io), _ => Err(Errno::Notsup), } } - pub async fn opt_time(&self, ty: TimeType) -> Result, Errno> { - let inner = self.inner.read().await; + pub fn opt_time(&self, ty: TimeType) -> Result, Errno> { + let inner = self.inner.read().unwrap(); match &inner.kind { - InodeSocketKind::TcpStream(sock) => sock.opt_time(ty).map_err(net_error_into_wasi_err), - InodeSocketKind::TcpListener(sock) => match ty { - TimeType::AcceptTimeout => sock.timeout().map_err(net_error_into_wasi_err), - _ => Err(Errno::Inval), - }, + InodeSocketKind::TcpStream { + read_timeout, + write_timeout, + .. + } => Ok(match ty { + TimeType::ReadTimeout => *read_timeout, + TimeType::WriteTimeout => *write_timeout, + _ => return Err(Errno::Inval), + }), + InodeSocketKind::TcpListener { accept_timeout, .. } => Ok(match ty { + TimeType::AcceptTimeout => *accept_timeout, + _ => return Err(Errno::Inval), + }), InodeSocketKind::PreSocket { - recv_timeout, - send_timeout, + read_timeout, + write_timeout, connect_timeout, accept_timeout, .. } => match ty { TimeType::ConnectTimeout => Ok(*connect_timeout), TimeType::AcceptTimeout => Ok(*accept_timeout), - TimeType::ReadTimeout => Ok(*recv_timeout), - TimeType::WriteTimeout => Ok(*send_timeout), + TimeType::ReadTimeout => Ok(*read_timeout), + TimeType::WriteTimeout => Ok(*write_timeout), _ => Err(Errno::Inval), }, InodeSocketKind::Closed => Err(Errno::Io), @@ -827,14 +807,14 @@ impl InodeSocket { } } - pub async fn set_ttl(&self, ttl: u32) -> Result<(), Errno> { - let mut inner = self.inner.write().await; + pub fn set_ttl(&self, ttl: u32) -> Result<(), Errno> { + let mut inner = self.inner.write().unwrap(); match &mut inner.kind { - InodeSocketKind::TcpStream(sock) => { - sock.set_ttl(ttl).await.map_err(net_error_into_wasi_err) + InodeSocketKind::TcpStream { socket, .. } => { + socket.set_ttl(ttl).map_err(net_error_into_wasi_err) } - InodeSocketKind::UdpSocket(sock) => { - sock.set_ttl(ttl).await.map_err(net_error_into_wasi_err) + InodeSocketKind::UdpSocket { socket, .. } => { + socket.set_ttl(ttl).map_err(net_error_into_wasi_err) } InodeSocketKind::PreSocket { .. } => Err(Errno::Io), InodeSocketKind::Closed => Err(Errno::Io), @@ -842,21 +822,25 @@ impl InodeSocket { } } - pub async fn ttl(&self) -> Result { - let inner = self.inner.read().await; + pub fn ttl(&self) -> Result { + let inner = self.inner.read().unwrap(); match &inner.kind { - InodeSocketKind::TcpStream(sock) => sock.ttl().map_err(net_error_into_wasi_err), - InodeSocketKind::UdpSocket(sock) => sock.ttl().map_err(net_error_into_wasi_err), + InodeSocketKind::TcpStream { socket, .. } => { + socket.ttl().map_err(net_error_into_wasi_err) + } + InodeSocketKind::UdpSocket { socket, .. } => { + socket.ttl().map_err(net_error_into_wasi_err) + } InodeSocketKind::PreSocket { .. } => Err(Errno::Io), InodeSocketKind::Closed => Err(Errno::Io), _ => Err(Errno::Notsup), } } - pub async fn set_multicast_ttl_v4(&self, ttl: u32) -> Result<(), Errno> { - let mut inner = self.inner.write().await; + pub fn set_multicast_ttl_v4(&self, ttl: u32) -> Result<(), Errno> { + let mut inner = self.inner.write().unwrap(); match &mut inner.kind { - InodeSocketKind::UdpSocket(sock) => sock + InodeSocketKind::UdpSocket { socket, .. } => socket .set_multicast_ttl_v4(ttl) .map_err(net_error_into_wasi_err), InodeSocketKind::PreSocket { .. } => Err(Errno::Io), @@ -865,11 +849,11 @@ impl InodeSocket { } } - pub async fn multicast_ttl_v4(&self) -> Result { - let inner = self.inner.read().await; + pub fn multicast_ttl_v4(&self) -> Result { + let inner = self.inner.read().unwrap(); match &inner.kind { - InodeSocketKind::UdpSocket(sock) => { - sock.multicast_ttl_v4().map_err(net_error_into_wasi_err) + InodeSocketKind::UdpSocket { socket, .. } => { + socket.multicast_ttl_v4().map_err(net_error_into_wasi_err) } InodeSocketKind::PreSocket { .. } => Err(Errno::Io), InodeSocketKind::Closed => Err(Errno::Io), @@ -877,14 +861,10 @@ impl InodeSocket { } } - pub async fn join_multicast_v4( - &self, - multiaddr: Ipv4Addr, - iface: Ipv4Addr, - ) -> Result<(), Errno> { - let mut inner = self.inner.write().await; + pub fn join_multicast_v4(&self, multiaddr: Ipv4Addr, iface: Ipv4Addr) -> Result<(), Errno> { + let mut inner = self.inner.write().unwrap(); match &mut inner.kind { - InodeSocketKind::UdpSocket(sock) => sock + InodeSocketKind::UdpSocket { socket, .. } => socket .join_multicast_v4(multiaddr, iface) .map_err(net_error_into_wasi_err), InodeSocketKind::PreSocket { .. } => Err(Errno::Io), @@ -893,14 +873,10 @@ impl InodeSocket { } } - pub async fn leave_multicast_v4( - &self, - multiaddr: Ipv4Addr, - iface: Ipv4Addr, - ) -> Result<(), Errno> { - let mut inner = self.inner.write().await; + pub fn leave_multicast_v4(&self, multiaddr: Ipv4Addr, iface: Ipv4Addr) -> Result<(), Errno> { + let mut inner = self.inner.write().unwrap(); match &mut inner.kind { - InodeSocketKind::UdpSocket(sock) => sock + InodeSocketKind::UdpSocket { socket, .. } => socket .leave_multicast_v4(multiaddr, iface) .map_err(net_error_into_wasi_err), InodeSocketKind::PreSocket { .. } => Err(Errno::Io), @@ -909,10 +885,10 @@ impl InodeSocket { } } - pub async fn join_multicast_v6(&self, multiaddr: Ipv6Addr, iface: u32) -> Result<(), Errno> { - let mut inner = self.inner.write().await; + pub fn join_multicast_v6(&self, multiaddr: Ipv6Addr, iface: u32) -> Result<(), Errno> { + let mut inner = self.inner.write().unwrap(); match &mut inner.kind { - InodeSocketKind::UdpSocket(sock) => sock + InodeSocketKind::UdpSocket { socket, .. } => socket .join_multicast_v6(multiaddr, iface) .map_err(net_error_into_wasi_err), InodeSocketKind::PreSocket { .. } => Err(Errno::Io), @@ -921,14 +897,10 @@ impl InodeSocket { } } - pub async fn leave_multicast_v6( - &mut self, - multiaddr: Ipv6Addr, - iface: u32, - ) -> Result<(), Errno> { - let mut inner = self.inner.write().await; + pub fn leave_multicast_v6(&mut self, multiaddr: Ipv6Addr, iface: u32) -> Result<(), Errno> { + let mut inner = self.inner.write().unwrap(); match &mut inner.kind { - InodeSocketKind::UdpSocket(sock) => sock + InodeSocketKind::UdpSocket { socket, .. } => socket .leave_multicast_v6(multiaddr, iface) .map_err(net_error_into_wasi_err), InodeSocketKind::PreSocket { .. } => Err(Errno::Io), @@ -937,147 +909,309 @@ impl InodeSocket { } } - pub async fn send(&self, buf: Vec) -> Result { - let buf_len = buf.len(); - let mut inner = self.inner.write().await; + pub async fn send( + &self, + tasks: &dyn VirtualTaskManager, + buf: &[u8], + fd_flags: Fdflags, + ) -> Result { + let nonblocking = fd_flags.contains(Fdflags::NONBLOCK); + let timeout = self + .opt_time(TimeType::WriteTimeout) + .ok() + .flatten() + .unwrap_or(Duration::from_secs(30)); - let ret = match &mut inner.kind { - InodeSocketKind::WebSocket(sock) => sock - .send(Bytes::from(buf)) - .await - .map(|_| buf_len) - .map_err(net_error_into_wasi_err), - InodeSocketKind::Raw(sock) => sock - .send(Bytes::from(buf)) - .await - .map_err(net_error_into_wasi_err), - InodeSocketKind::TcpStream(sock) => sock - .send(Bytes::from(buf)) - .await - .map_err(net_error_into_wasi_err), - InodeSocketKind::UdpSocket(sock) => sock - .send(Bytes::from(buf)) - .await - .map_err(net_error_into_wasi_err), - InodeSocketKind::PreSocket { .. } => Err(Errno::Notconn), - InodeSocketKind::Closed => Err(Errno::Io), - _ => Err(Errno::Notsup), + #[derive(Debug)] + struct SocketSender<'a, 'b> { + sock: &'a RwLock, + data: &'b [u8], + nonblocking: bool, + } + impl<'a, 'b> Future for SocketSender<'a, 'b> { + type Output = Result; + fn poll(self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll { + let mut inner = self.sock.write().unwrap(); + match &mut inner.kind { + InodeSocketKind::WebSocket(sock) => sock + .poll_send(cx, self.data) + .map_err(net_error_into_wasi_err), + InodeSocketKind::Raw(sock) => { + if self.nonblocking { + match sock.try_send(self.data) { + Ok(amt) => Poll::Ready(Ok(amt)), + Err(err) => Poll::Ready(Err(net_error_into_wasi_err(err))), + } + } else { + sock.poll_send(cx, self.data) + .map_err(net_error_into_wasi_err) + } + } + InodeSocketKind::TcpStream { socket, .. } => { + if self.nonblocking { + match socket.try_send(self.data) { + Ok(amt) => Poll::Ready(Ok(amt)), + Err(err) => Poll::Ready(Err(net_error_into_wasi_err(err))), + } + } else { + socket + .poll_send(cx, self.data) + .map_err(net_error_into_wasi_err) + } + } + InodeSocketKind::UdpSocket { socket, peer } => { + if let Some(peer) = peer { + if self.nonblocking { + match socket.try_send_to(self.data, *peer) { + Ok(amt) => Poll::Ready(Ok(amt)), + Err(err) => Poll::Ready(Err(net_error_into_wasi_err(err))), + } + } else { + socket + .poll_send_to(cx, self.data, *peer) + .map_err(net_error_into_wasi_err) + } + } else { + Poll::Ready(Err(Errno::Notconn)) + } + } + InodeSocketKind::PreSocket { .. } => Poll::Ready(Err(Errno::Notconn)), + InodeSocketKind::Closed => Poll::Ready(Err(Errno::Io)), + _ => Poll::Ready(Err(Errno::Notsup)), + } + } + } + + tokio::select! { + res = SocketSender { sock: &self.inner, data: buf, nonblocking } => res, + _ = tasks.sleep_now(timeout) => Err(Errno::Timedout) } - .map(|_| buf_len)?; - Ok(ret) } pub async fn send_to( &self, - buf: Vec, + tasks: &dyn VirtualTaskManager, + buf: &[u8], addr: SocketAddr, + fd_flags: Fdflags, ) -> Result { - let buf_len = buf.len(); - let mut inner = self.inner.write().await; - let ret = match &mut inner.kind { - InodeSocketKind::Icmp(sock) => sock - .send_to(Bytes::from(buf), addr) - .await - .map_err(net_error_into_wasi_err), - InodeSocketKind::UdpSocket(sock) => sock - .send_to(Bytes::from(buf), addr) - .await - .map_err(net_error_into_wasi_err), - InodeSocketKind::PreSocket { .. } => Err(Errno::Notconn), - InodeSocketKind::Closed => Err(Errno::Io), - _ => Err(Errno::Notsup), - } - .map(|_| buf_len)?; - Ok(ret) - } + let nonblocking = fd_flags.contains(Fdflags::NONBLOCK); + let timeout = self + .opt_time(TimeType::WriteTimeout) + .ok() + .flatten() + .unwrap_or(Duration::from_secs(30)); - pub async fn recv(&self, max_size: usize) -> Result { - let mut inner = self.inner.write().await; - loop { - let is_tcp = matches!(&inner.kind, InodeSocketKind::TcpStream(..)); - if let Some(buf) = inner.read_buffer.as_mut() { - let buf_len = buf.len(); - if buf_len > 0 { - let read = buf_len.min(max_size); - let ret = buf.slice(..read); - if is_tcp { - buf.advance(read); - } else { - buf.clear(); + #[derive(Debug)] + struct SocketSender<'a, 'b> { + sock: &'a RwLock, + data: &'b [u8], + addr: SocketAddr, + nonblocking: bool, + } + impl<'a, 'b> Future for SocketSender<'a, 'b> { + type Output = Result; + fn poll(self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll { + let mut inner = self.sock.write().unwrap(); + match &mut inner.kind { + InodeSocketKind::Icmp(sock) => { + if self.nonblocking { + match sock.try_send_to(self.data, self.addr) { + Ok(amt) => Poll::Ready(Ok(amt)), + Err(err) => Poll::Ready(Err(net_error_into_wasi_err(err))), + } + } else { + sock.poll_send_to(cx, self.data, self.addr) + .map_err(net_error_into_wasi_err) + } + } + InodeSocketKind::UdpSocket { socket, .. } => { + if self.nonblocking { + match socket.try_send_to(self.data, self.addr) { + Ok(amt) => Poll::Ready(Ok(amt)), + Err(err) => Poll::Ready(Err(net_error_into_wasi_err(err))), + } + } else { + socket + .poll_send_to(cx, self.data, self.addr) + .map_err(net_error_into_wasi_err) + } } - return Ok(ret); + InodeSocketKind::PreSocket { .. } => Poll::Ready(Err(Errno::Notconn)), + InodeSocketKind::Closed => Poll::Ready(Err(Errno::Io)), + _ => Poll::Ready(Err(Errno::Notsup)), } } - let data = match &mut inner.kind { - InodeSocketKind::WebSocket(sock) => { - let read = sock.recv().await.map_err(net_error_into_wasi_err)?; - read.data - } - InodeSocketKind::Raw(sock) => { - let read = sock.recv().await.map_err(net_error_into_wasi_err)?; - read.data - } - InodeSocketKind::TcpStream(sock) => { - let read = sock.recv().await.map_err(net_error_into_wasi_err)?; - read.data - } - InodeSocketKind::UdpSocket(sock) => { - let read = sock.recv().await.map_err(net_error_into_wasi_err)?; - read.data + } + + tokio::select! { + res = SocketSender { sock: &self.inner, data: buf, addr, nonblocking } => res, + _ = tasks.sleep_now(timeout) => Err(Errno::Timedout) + } + } + + pub async fn recv( + &self, + tasks: &dyn VirtualTaskManager, + buf: &mut [MaybeUninit], + fd_flags: Fdflags, + ) -> Result { + let nonblocking = fd_flags.contains(Fdflags::NONBLOCK); + let timeout = self + .opt_time(TimeType::ReadTimeout) + .ok() + .flatten() + .unwrap_or(Duration::from_secs(30)); + + #[derive(Debug)] + struct SocketReceiver<'a, 'b> { + sock: &'a RwLock, + data: &'b mut [MaybeUninit], + nonblocking: bool, + } + impl<'a, 'b> Future for SocketReceiver<'a, 'b> { + type Output = Result; + fn poll( + mut self: Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> Poll { + let mut inner = self.sock.write().unwrap(); + match &mut inner.kind { + InodeSocketKind::WebSocket(sock) => sock + .poll_recv(cx, self.data) + .map_err(net_error_into_wasi_err), + InodeSocketKind::Raw(sock) => { + if self.nonblocking { + match sock.try_recv(self.data) { + Ok(amt) => Poll::Ready(Ok(amt)), + Err(err) => Poll::Ready(Err(net_error_into_wasi_err(err))), + } + } else { + sock.poll_recv(cx, self.data) + .map_err(net_error_into_wasi_err) + } + } + InodeSocketKind::TcpStream { socket, .. } => { + if self.nonblocking { + match socket.try_recv(self.data) { + Ok(amt) => Poll::Ready(Ok(amt)), + Err(err) => Poll::Ready(Err(net_error_into_wasi_err(err))), + } + } else { + socket + .poll_recv(cx, self.data) + .map_err(net_error_into_wasi_err) + } + } + InodeSocketKind::UdpSocket { socket, peer } => { + if let Some(peer) = peer { + if self.nonblocking { + loop { + match socket + .try_recv_from(self.data) + .map_err(net_error_into_wasi_err) + { + Ok((_, addr)) if addr != *peer => continue, + Ok((amt, _)) => return Poll::Ready(Ok(amt)), + Err(err) => return Poll::Ready(Err(err)), + } + } + } else { + loop { + match socket + .poll_recv_from(cx, self.data) + .map_err(net_error_into_wasi_err) + { + Poll::Ready(Ok((_, addr))) if addr != *peer => continue, + res => return res.map_ok(|a| a.0), + } + } + } + } else { + Poll::Ready(Err(Errno::Notconn)) + } + } + InodeSocketKind::PreSocket { .. } => Poll::Ready(Err(Errno::Notconn)), + InodeSocketKind::Closed => Poll::Ready(Err(Errno::Io)), + _ => Poll::Ready(Err(Errno::Notsup)), } - InodeSocketKind::PreSocket { .. } => return Err(Errno::Notconn), - InodeSocketKind::Closed => return Ok(Bytes::new()), - _ => return Err(Errno::Notsup), - }; - if data.is_empty() { - return Ok(Bytes::new()); } - inner.read_buffer.replace(data); - inner.read_addr.take(); + } + + tokio::select! { + res = SocketReceiver { sock: &self.inner, data: buf, nonblocking } => res, + _ = tasks.sleep_now(timeout) => Err(Errno::Timedout) } } - pub async fn recv_from(&self, max_size: usize) -> Result<(Bytes, SocketAddr), Errno> { - let mut inner = self.inner.write().await; - loop { - let is_tcp = matches!(&inner.kind, InodeSocketKind::TcpStream(..)); - if let Some(buf) = inner.read_buffer.as_mut() { - if !buf.is_empty() { - let buf_len = buf.len(); - let read = buf_len.min(max_size); - let ret = buf.slice(..read); - if is_tcp { - buf.advance(read); - } else { - buf.clear(); + pub async fn recv_from( + &self, + tasks: &dyn VirtualTaskManager, + buf: &mut [MaybeUninit], + fd_flags: Fdflags, + ) -> Result<(usize, SocketAddr), Errno> { + let nonblocking = fd_flags.contains(Fdflags::NONBLOCK); + let timeout = self + .opt_time(TimeType::ReadTimeout) + .ok() + .flatten() + .unwrap_or(Duration::from_secs(30)); + + #[derive(Debug)] + struct SocketReceiver<'a, 'b> { + sock: &'a RwLock, + data: &'b mut [MaybeUninit], + nonblocking: bool, + } + impl<'a, 'b> Future for SocketReceiver<'a, 'b> { + type Output = Result<(usize, SocketAddr), Errno>; + fn poll( + mut self: Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> Poll { + let mut inner = self.sock.write().unwrap(); + match &mut inner.kind { + InodeSocketKind::Icmp(sock) => { + if self.nonblocking { + match sock.try_recv_from(self.data) { + Ok(res) => Poll::Ready(Ok(res)), + Err(err) => Poll::Ready(Err(net_error_into_wasi_err(err))), + } + } else { + sock.poll_recv_from(cx, self.data) + .map_err(net_error_into_wasi_err) + } } - let peer = inner - .read_addr - .unwrap_or_else(|| SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), 0)); - return Ok((ret, peer)); + InodeSocketKind::UdpSocket { socket, .. } => { + if self.nonblocking { + match socket.try_recv_from(self.data) { + Ok(res) => Poll::Ready(Ok(res)), + Err(err) => Poll::Ready(Err(net_error_into_wasi_err(err))), + } + } else { + socket + .poll_recv_from(cx, self.data) + .map_err(net_error_into_wasi_err) + } + } + InodeSocketKind::PreSocket { .. } => Poll::Ready(Err(Errno::Notconn)), + InodeSocketKind::Closed => Poll::Ready(Err(Errno::Io)), + _ => Poll::Ready(Err(Errno::Notsup)), } } - let rcv = match &mut inner.kind { - InodeSocketKind::Icmp(sock) => { - sock.recv_from().await.map_err(net_error_into_wasi_err)? - } - InodeSocketKind::UdpSocket(sock) => { - sock.recv_from().await.map_err(net_error_into_wasi_err)? - } - InodeSocketKind::PreSocket { .. } => return Err(Errno::Notconn), - InodeSocketKind::Closed => return Err(Errno::Io), - _ => return Err(Errno::Notsup), - }; - inner.read_buffer.replace(rcv.data); - inner.read_addr.replace(rcv.addr); + } + + tokio::select! { + res = SocketReceiver { sock: &self.inner, data: buf, nonblocking } => res, + _ = tasks.sleep_now(timeout) => Err(Errno::Timedout) } } - pub async fn shutdown(&mut self, how: std::net::Shutdown) -> Result<(), Errno> { - let mut inner = self.inner.write().await; + pub fn shutdown(&mut self, how: std::net::Shutdown) -> Result<(), Errno> { + let mut inner = self.inner.write().unwrap(); match &mut inner.kind { - InodeSocketKind::TcpStream(sock) => { - sock.shutdown(how).await.map_err(net_error_into_wasi_err)?; + InodeSocketKind::TcpStream { socket, .. } => { + socket.shutdown(how).map_err(net_error_into_wasi_err)?; } InodeSocketKind::PreSocket { .. } => return Err(Errno::Notconn), InodeSocketKind::Closed => return Err(Errno::Io), @@ -1090,8 +1224,8 @@ impl InodeSocket { if let Ok(mut guard) = self.inner.try_write() { #[allow(clippy::match_like_matches_macro)] match &mut guard.kind { - InodeSocketKind::TcpStream(..) - | InodeSocketKind::UdpSocket(..) + InodeSocketKind::TcpStream { .. } + | InodeSocketKind::UdpSocket { .. } | InodeSocketKind::Raw(..) | InodeSocketKind::WebSocket(..) => true, _ => false, @@ -1108,9 +1242,9 @@ impl InodeSocketInner { cx: &mut std::task::Context<'_>, ) -> std::task::Poll> { match &mut self.kind { - InodeSocketKind::TcpListener(socket) => socket.poll_accept_ready(cx), - InodeSocketKind::TcpStream(socket) => socket.poll_read_ready(cx), - InodeSocketKind::UdpSocket(socket) => socket.poll_read_ready(cx), + InodeSocketKind::TcpListener { socket, .. } => socket.poll_accept_ready(cx), + InodeSocketKind::TcpStream { socket, .. } => socket.poll_read_ready(cx), + InodeSocketKind::UdpSocket { socket, .. } => socket.poll_read_ready(cx), InodeSocketKind::Raw(socket) => socket.poll_read_ready(cx), InodeSocketKind::WebSocket(socket) => socket.poll_read_ready(cx), InodeSocketKind::Icmp(socket) => socket.poll_read_ready(cx), @@ -1128,9 +1262,9 @@ impl InodeSocketInner { cx: &mut std::task::Context<'_>, ) -> std::task::Poll> { match &mut self.kind { - InodeSocketKind::TcpListener(_) => std::task::Poll::Pending, - InodeSocketKind::TcpStream(socket) => socket.poll_write_ready(cx), - InodeSocketKind::UdpSocket(socket) => socket.poll_write_ready(cx), + InodeSocketKind::TcpListener { .. } => std::task::Poll::Pending, + InodeSocketKind::TcpStream { socket, .. } => socket.poll_write_ready(cx), + InodeSocketKind::UdpSocket { socket, .. } => socket.poll_write_ready(cx), InodeSocketKind::Raw(socket) => socket.poll_write_ready(cx), InodeSocketKind::WebSocket(socket) => socket.poll_write_ready(cx), InodeSocketKind::Icmp(socket) => socket.poll_write_ready(cx), diff --git a/lib/wasi/src/os/task/thread.rs b/lib/wasi/src/os/task/thread.rs index 69b235772c7..1d494689175 100644 --- a/lib/wasi/src/os/task/thread.rs +++ b/lib/wasi/src/os/task/thread.rs @@ -2,6 +2,7 @@ use std::{ collections::HashMap, ops::{Deref, DerefMut}, sync::{Arc, Mutex, RwLock}, + task::Waker, }; use bytes::{Bytes, BytesMut}; @@ -88,7 +89,7 @@ struct WasiThreadState { is_main: bool, pid: WasiProcessId, id: WasiThreadId, - signals: Mutex<(Vec, tokio::sync::broadcast::Sender<()>)>, + signals: Mutex<(Vec, Vec)>, stack: Mutex, finished: Arc, @@ -113,7 +114,7 @@ impl WasiThread { pid, id, finished, - signals: Mutex::new((Vec::new(), tokio::sync::broadcast::channel(1).0)), + signals: Mutex::new((Vec::new(), Vec::new())), stack: Mutex::new(ThreadStack::default()), _task_count_guard: guard, }), @@ -136,7 +137,7 @@ impl WasiThread { } // TODO: this should be private, access should go through utility methods. - pub fn signals(&self) -> &Mutex<(Vec, tokio::sync::broadcast::Sender<()>)> { + pub fn signals(&self) -> &Mutex<(Vec, Vec)> { &self.state.signals } @@ -162,7 +163,7 @@ impl WasiThread { if !guard.0.contains(&signal) { guard.0.push(signal); } - let _ = guard.1.send(()); + guard.1.drain(..).for_each(|w| w.wake()); } /// Returns all the signals that are waiting to be processed @@ -177,16 +178,39 @@ impl WasiThread { } /// Returns all the signals that are waiting to be processed - pub fn pop_signals_or_subscribe( - &self, - ) -> Result, tokio::sync::broadcast::Receiver<()>> { + pub fn pop_signals_or_subscribe(&self, waker: &Waker) -> Option> { let mut guard = self.state.signals.lock().unwrap(); let mut ret = Vec::new(); std::mem::swap(&mut ret, &mut guard.0); match ret.is_empty() { - true => Err(guard.1.subscribe()), - false => Ok(ret), + true => { + if guard.1.iter().any(|w| w.will_wake(waker)) == false { + guard.1.push(waker.clone()); + } + None + } + false => Some(ret), + } + } + + /// Returns all the signals that are waiting to be processed + pub fn has_signals_or_subscribe(&self, waker: &Waker) -> bool { + let mut guard = self.state.signals.lock().unwrap(); + let has_signals = !guard.0.is_empty(); + if has_signals == false { + if guard.1.iter().any(|w| w.will_wake(waker)) == false { + guard.1.push(waker.clone()); + } } + has_signals + } + + /// Returns all the signals that are waiting to be processed + pub fn pop_signals(&self) -> Vec { + let mut guard = self.state.signals.lock().unwrap(); + let mut ret = Vec::new(); + std::mem::swap(&mut ret, &mut guard.0); + ret } /// Adds a stack snapshot and removes dead ones diff --git a/lib/wasi/src/runtime/task_manager/mod.rs b/lib/wasi/src/runtime/task_manager/mod.rs index d7d6ab9e39d..75e69c69299 100644 --- a/lib/wasi/src/runtime/task_manager/mod.rs +++ b/lib/wasi/src/runtime/task_manager/mod.rs @@ -2,7 +2,7 @@ #[cfg(feature = "sys-thread")] pub mod tokio; -use std::pin::Pin; +use std::{pin::Pin, time::Duration}; use ::tokio::runtime::Runtime; use futures::Future; @@ -10,7 +10,7 @@ use wasmer::{vm::VMMemory, MemoryType, Module, Store}; #[cfg(feature = "sys")] use wasmer_types::MemoryStyle; -use crate::{os::task::thread::WasiThreadError, WasiCallingId}; +use crate::os::task::thread::WasiThreadError; #[derive(Debug)] pub struct SpawnedMemory { @@ -28,16 +28,13 @@ pub enum SpawnType { } /// An implementation of task management +#[async_trait::async_trait] #[allow(unused_variables)] pub trait VirtualTaskManager: std::fmt::Debug + Send + Sync + 'static { /// Invokes whenever a WASM thread goes idle. In some runtimes (like singlethreaded /// execution environments) they will need to do asynchronous work whenever the main /// thread goes idle and this is the place to hook for that. - fn sleep_now( - &self, - _id: WasiCallingId, - ms: u128, - ) -> Pin + Send + Sync + 'static>>; + async fn sleep_now(&self, time: Duration); /// Starts an asynchronous task that will run on a shared worker pool /// This task must not block the execution or it could cause a deadlock @@ -82,19 +79,15 @@ pub trait VirtualTaskManager: std::fmt::Debug + Send + Sync + 'static { #[derive(Clone, Debug)] pub struct StubTaskManager; +#[async_trait::async_trait] impl VirtualTaskManager for StubTaskManager { #[allow(unused_variables)] - fn sleep_now( - &self, - id: WasiCallingId, - ms: u128, - ) -> Pin + Send + Sync + 'static>> { - if ms == 0 { + async fn sleep_now(&self, time: Duration) { + if time == Duration::ZERO { std::thread::yield_now(); } else { - std::thread::sleep(std::time::Duration::from_millis(ms as u64)); + std::thread::sleep(time); } - Box::pin(async move {}) } #[allow(unused_variables)] diff --git a/lib/wasi/src/runtime/task_manager/tokio.rs b/lib/wasi/src/runtime/task_manager/tokio.rs index 9e1f9ae868f..eeb5d0583f0 100644 --- a/lib/wasi/src/runtime/task_manager/tokio.rs +++ b/lib/wasi/src/runtime/task_manager/tokio.rs @@ -1,11 +1,11 @@ -use std::pin::Pin; +use std::{pin::Pin, time::Duration}; use futures::Future; #[cfg(feature = "sys-thread")] use tokio::runtime::{Builder, Runtime}; use wasmer::{vm::VMMemory, Module, Store}; -use crate::{os::task::thread::WasiThreadError, WasiCallingId}; +use crate::os::task::thread::WasiThreadError; use super::{SpawnType, VirtualTaskManager}; @@ -39,20 +39,15 @@ impl<'g> Drop for TokioRuntimeGuard<'g> { fn drop(&mut self) {} } +#[async_trait::async_trait] impl VirtualTaskManager for TokioTaskManager { /// See [`VirtualTaskManager::sleep_now`]. - fn sleep_now( - &self, - _id: WasiCallingId, - ms: u128, - ) -> Pin + Send + Sync + 'static>> { - Box::pin(async move { - if ms == 0 { - tokio::task::yield_now().await; - } else { - tokio::time::sleep(std::time::Duration::from_millis(ms as u64)).await; - } - }) + async fn sleep_now(&self, time: Duration) { + if time == Duration::ZERO { + tokio::task::yield_now().await; + } else { + tokio::time::sleep(time).await; + } } /// See [`VirtualTaskManager::task_shared`]. diff --git a/lib/wasi/src/state/env.rs b/lib/wasi/src/state/env.rs index f88c46802d8..e7af10fd8ed 100644 --- a/lib/wasi/src/state/env.rs +++ b/lib/wasi/src/state/env.rs @@ -338,20 +338,17 @@ impl WasiEnv { // differently let env = ctx.data(); if !env.inner().signal_set { - if let Ok(signals) = env.thread.pop_signals_or_subscribe() { - let signal_cnt = signals.len(); - for sig in signals { - if sig == Signal::Sigint || sig == Signal::Sigquit || sig == Signal::Sigkill { - env.thread.terminate(Errno::Intr as u32); - return Err(WasiError::Exit(Errno::Intr as u32)); - } else { - trace!("wasi[{}]::signal-ignored: {:?}", env.pid(), sig); - } + let signals = env.thread.pop_signals(); + let signal_cnt = signals.len(); + for sig in signals { + if sig == Signal::Sigint || sig == Signal::Sigquit || sig == Signal::Sigkill { + env.thread.terminate(Errno::Intr as u32); + return Err(WasiError::Exit(Errno::Intr as u32)); + } else { + trace!("wasi[{}]::signal-ignored: {:?}", env.pid(), sig); } - return Ok(Ok(signal_cnt > 0)); - } else { - return Ok(Ok(false)); } + return Ok(Ok(signal_cnt > 0)); } // Check for forced exit @@ -381,67 +378,80 @@ impl WasiEnv { // Check for any signals that we need to trigger // (but only if a signal handler is registered) + if let Some(_) = env.inner().signal.as_ref() { + let signals = env.thread.pop_signals(); + Ok(Ok(Self::process_signals_internal(ctx, signals)?)) + } else { + Ok(Ok(false)) + } + } + + pub fn process_signals_internal( + ctx: &mut FunctionEnvMut<'_, Self>, + mut signals: Vec, + ) -> Result { + let env = ctx.data(); if let Some(handler) = env.inner().signal.clone() { - if let Ok(mut signals) = env.thread.pop_signals_or_subscribe() { - // We might also have signals that trigger on timers - let mut now = 0; - let has_signal_interval = { - let mut any = false; - let inner = env.process.inner.read().unwrap(); - if !inner.signal_intervals.is_empty() { - now = platform_clock_time_get(Snapshot0Clockid::Monotonic, 1_000_000) - .unwrap() as u128; - for signal in inner.signal_intervals.values() { - let elapsed = now - signal.last_signal; - if elapsed >= signal.interval.as_nanos() { - any = true; - break; - } - } - } - any - }; - if has_signal_interval { - let mut inner = env.process.inner.write().unwrap(); - for signal in inner.signal_intervals.values_mut() { + // We might also have signals that trigger on timers + let mut now = 0; + let has_signal_interval = { + let mut any = false; + let inner = env.process.inner.read().unwrap(); + if !inner.signal_intervals.is_empty() { + now = platform_clock_time_get(Snapshot0Clockid::Monotonic, 1_000_000).unwrap() + as u128; + for signal in inner.signal_intervals.values() { let elapsed = now - signal.last_signal; if elapsed >= signal.interval.as_nanos() { - signal.last_signal = now; - signals.push(signal.signal); + any = true; + break; } } } + any + }; + if has_signal_interval { + let mut inner = env.process.inner.write().unwrap(); + for signal in inner.signal_intervals.values_mut() { + let elapsed = now - signal.last_signal; + if elapsed >= signal.interval.as_nanos() { + signal.last_signal = now; + signals.push(signal.signal); + } + } + } - for signal in signals { - tracing::trace!( - "wasi[{}]::processing-signal: {:?}", - ctx.data().pid(), - signal - ); - if let Err(err) = handler.call(ctx, signal as i32) { - match err.downcast::() { - Ok(wasi_err) => { - warn!( - "wasi[{}]::signal handler wasi error - {}", - ctx.data().pid(), - wasi_err - ); - return Err(wasi_err); - } - Err(runtime_err) => { - warn!( - "wasi[{}]::signal handler runtime error - {}", - ctx.data().pid(), - runtime_err - ); - return Err(WasiError::Exit(Errno::Intr as ExitCode)); - } + for signal in signals { + tracing::trace!( + "wasi[{}]::processing-signal: {:?}", + ctx.data().pid(), + signal + ); + if let Err(err) = handler.call(ctx, signal as i32) { + match err.downcast::() { + Ok(wasi_err) => { + warn!( + "wasi[{}]::signal handler wasi error - {}", + ctx.data().pid(), + wasi_err + ); + return Err(wasi_err); + } + Err(runtime_err) => { + warn!( + "wasi[{}]::signal handler runtime error - {}", + ctx.data().pid(), + runtime_err + ); + return Err(WasiError::Exit(Errno::Intr as ExitCode)); } } } } + Ok(true) + } else { + Ok(false) } - Ok(Ok(true)) } /// Returns an exit code if the thread or process has been forced to exit diff --git a/lib/wasi/src/state/mod.rs b/lib/wasi/src/state/mod.rs index c4463fb2b8f..3a246ec19e8 100644 --- a/lib/wasi/src/state/mod.rs +++ b/lib/wasi/src/state/mod.rs @@ -25,7 +25,7 @@ use std::{ cell::RefCell, collections::HashMap, path::Path, - sync::{atomic::AtomicU32, Arc, Mutex, MutexGuard, RwLock}, + sync::{Arc, Mutex, MutexGuard, RwLock}, task::Waker, time::Duration, }; @@ -102,9 +102,7 @@ impl WasiState { } pub(crate) fn fs_new_open_options(&self) -> OpenOptions { - OpenOptions::new(Box::new(WasiStateOpener { - root_fs: self.fs.root_fs.clone(), - })) + self.fs.root_fs.new_open_options() } } @@ -114,7 +112,7 @@ struct WasiStateOpener { impl FileOpener for WasiStateOpener { fn open( - &mut self, + &self, path: &Path, conf: &wasmer_vfs::OpenOptionsConfig, ) -> wasmer_vfs::Result> { @@ -155,8 +153,7 @@ pub(crate) struct WasiStateThreading { /// CPU efficient manner #[derive(Debug)] pub struct WasiFutex { - pub(crate) refcnt: AtomicU32, - pub(crate) waker: tokio::sync::broadcast::Sender<()>, + pub(crate) wakers: Vec, } #[derive(Debug)] @@ -250,7 +247,7 @@ pub struct WasiState { // TODO: review allow... #[allow(dead_code)] pub(crate) threading: RwLock, - pub(crate) futexs: RwLock>, + pub(crate) futexs: Mutex>, pub(crate) clock_offset: Mutex>, pub(crate) bus: WasiBusState, pub args: Vec, diff --git a/lib/wasi/src/syscalls/legacy/snapshot0.rs b/lib/wasi/src/syscalls/legacy/snapshot0.rs index 5177be5f1a9..82e81d23639 100644 --- a/lib/wasi/src/syscalls/legacy/snapshot0.rs +++ b/lib/wasi/src/syscalls/legacy/snapshot0.rs @@ -6,8 +6,12 @@ use wasmer_wasi_types::wasi::{ }; use crate::{ - mem_error_to_wasi, os::task::thread::WasiThread, syscalls, syscalls::types, Memory32, - MemorySize, WasiEnv, WasiError, + mem_error_to_wasi, + os::task::thread::WasiThread, + state::{PollEventBuilder, PollEventSet}, + syscalls, + syscalls::types, + Memory32, MemorySize, WasiEnv, WasiError, }; /// Wrapper around `syscalls::fd_filestat_get` with extra logic to handle the size @@ -141,7 +145,11 @@ pub fn poll_oneoff( let in_origs = wasi_try_mem_ok!(in_.slice(&memory, nsubscriptions)); let in_origs = wasi_try_mem_ok!(in_origs.read_to_vec()); for in_orig in in_origs { - subscriptions.push(Into::::into(in_orig)); + subscriptions.push(( + None, + PollEventSet::default(), + Into::::into(in_orig), + )); } // make the call diff --git a/lib/wasi/src/syscalls/mod.rs b/lib/wasi/src/syscalls/mod.rs index 91c13687f97..e0341009184 100644 --- a/lib/wasi/src/syscalls/mod.rs +++ b/lib/wasi/src/syscalls/mod.rs @@ -19,12 +19,14 @@ pub mod windows; pub mod wasi; pub mod wasix; +use bytes::{Buf, BufMut}; use futures::Future; pub use wasi::*; pub use wasix::*; pub mod legacy; +use std::mem::MaybeUninit; pub(crate) use std::{ borrow::{Borrow, Cow}, cell::RefCell, @@ -158,6 +160,60 @@ pub(crate) fn write_bytes( result } +pub(crate) fn copy_to_slice( + memory: &MemoryView, + iovs_arr_cell: WasmSlice<__wasi_ciovec_t>, + mut write_loc: &mut [MaybeUninit], +) -> Result { + let mut bytes_written = 0usize; + for iov in iovs_arr_cell.iter() { + let iov_inner = iov.read().map_err(mem_error_to_wasi)?; + + let amt = from_offset::(iov_inner.buf_len)?; + + let (left, right) = write_loc.split_at_mut(amt); + let bytes = WasmPtr::::new(iov_inner.buf) + .slice(memory, iov_inner.buf_len) + .map_err(mem_error_to_wasi)?; + + if amt != bytes.copy_to_slice(left).map_err(mem_error_to_wasi)? { + return Err(Errno::Fault); + } + + write_loc = right; + bytes_written += amt; + } + Ok(bytes_written) +} + +pub(crate) fn copy_from_slice( + mut read_loc: &[u8], + memory: &MemoryView, + iovs_arr: WasmSlice<__wasi_iovec_t>, +) -> Result { + let mut bytes_read = 0usize; + + for iov in iovs_arr.iter() { + let iov_inner = iov.read().map_err(mem_error_to_wasi)?; + + let to_read = from_offset::(iov_inner.buf_len)?; + let to_read = to_read.min(read_loc.len()); + if to_read == 0 { + break; + } + let (left, right) = read_loc.split_at(to_read); + + let buf = WasmPtr::::new(iov_inner.buf) + .slice(memory, to_read.try_into().map_err(|_| Errno::Overflow)?) + .map_err(mem_error_to_wasi)?; + buf.write_slice(left).map_err(mem_error_to_wasi)?; + + read_loc = right; + bytes_read += to_read; + } + Ok(bytes_read) +} + pub(crate) fn read_bytes( mut reader: T, memory: &MemoryView, @@ -167,7 +223,7 @@ pub(crate) fn read_bytes( // We allocate the raw_bytes first once instead of // N times in the loop. - let mut raw_bytes: Vec = vec![0; 1024]; + let mut raw_bytes: Vec = vec![0; 10240]; for iov in iovs_arr.iter() { let iov_inner = iov.read().map_err(mem_error_to_wasi)?; @@ -221,35 +277,107 @@ where return Err(WasiError::Exit(exit_code)); } - // Fast path (inline synchronous) - let pinned_work = { - let _guard = env.tasks.runtime_enter(); + // 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> + where + Fut: Future>, + { + ctx: &'a mut FunctionEnvMut<'b, WasiEnv>, + pinned_work: Pin>, + } + impl<'a, 'b, Fut, T> Future for WorkWithSignalPoller<'a, 'b, Fut, T> + where + Fut: Future>, + { + type Output = Result; + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + if let Poll::Ready(res) = Pin::new(&mut self.pinned_work).poll(cx) { + return Poll::Ready(Ok(res)); + } + if let Some(exit_code) = self.ctx.data().should_exit() { + return Poll::Ready(Err(WasiError::Exit(exit_code))); + } + if let Some(signals) = self.ctx.data().thread.pop_signals_or_subscribe(cx.waker()) { + if let Err(err) = WasiEnv::process_signals_internal(self.ctx, signals) { + return Poll::Ready(Err(err)); + } + return Poll::Ready(Ok(Err(Errno::Intr))); + } + Poll::Pending + } + } + + // Define the work function + let tasks = env.tasks.clone(); + 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), + }) + }; + + // 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(i) = pinned_work.as_mut().poll(&mut cx) { - return Ok(i); + if let Poll::Ready(res) = pinned_work.as_mut().poll(&mut cx) { + return res; } - pinned_work - }; + return Ok(Err(Errno::Again)); + } - // Slow path (will may put the thread to sleep) - //let mut env = ctx.data(); - let tasks = env.tasks.clone(); + // 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 +/// synchronous IO engine +pub(crate) fn __asyncify_light<'a, T, Fut>( + env: &'a WasiEnv, + timeout: Option, + work: Fut, +) -> Result, WasiError> +where + T: 'static, + Fut: std::future::Future>, +{ // Create the timeout let mut nonblocking = false; if timeout == Some(Duration::ZERO) { nonblocking = true; } let timeout = { - let tasks_inner = tasks.clone(); - async move { + async { if let Some(timeout) = timeout { if !nonblocking { - tasks_inner - .sleep_now(current_caller_id(), timeout.as_millis()) - .await + env.tasks.sleep_now(timeout).await } else { InfiniteSleep::default().await } @@ -259,51 +387,58 @@ where } }; - let mut signaler = { - let signals = env.thread.signals().lock().unwrap(); - let signaler = signals.1.subscribe(); - if !signals.0.is_empty() { - drop(signals); - match WasiEnv::process_signals(ctx)? { - Err(err) => return Ok(Err(err)), - Ok(processed) if processed => return Ok(Err(Errno::Intr)), - Ok(_) => {} + // This poller will process any signals when the main working function is idle + struct WorkWithSignalPoller<'a, Fut, T> + where + Fut: Future>, + { + env: &'a WasiEnv, + pinned_work: Pin>, + } + impl<'a, Fut, T> Future for WorkWithSignalPoller<'a, Fut, T> + where + Fut: Future>, + { + type Output = Result; + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + if let Poll::Ready(res) = Pin::new(&mut self.pinned_work).poll(cx) { + return Poll::Ready(Ok(res)); + } + if let Some(exit_code) = self.env.should_exit() { + return Poll::Ready(Err(WasiError::Exit(exit_code))); } - env = ctx.data(); + if let Some(signals) = self.env.thread.pop_signals_or_subscribe(cx.waker()) { + return Poll::Ready(Ok(Err(Errno::Intr))); + } + Poll::Pending } - signaler - }; + } // Define the work function + let mut pinned_work = Box::pin(work); let work = async move { Ok(tokio::select! { // The main work we are doing - ret = pinned_work => ret, - // If a signaler is triggered then we interrupt the main process - _ = signaler.recv() => { - WasiEnv::process_signals(ctx)?; - Err(Errno::Intr) - }, + res = WorkWithSignalPoller { env, pinned_work } => res?, // Optional timeout _ = timeout => Err(Errno::Timedout), }) }; - // If we are in nonblocking mode then we register a fake waker - // and poll then return immediately with a timeout if nothing happened + // 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) { - res - } else { - Ok(Err(Errno::Again)) + return res; } - } else { - // Block on the work and process process - tasks.block_on(work) + return Ok(Err(Errno::Again)); } + + // Slow path, block on the work and process process + env.tasks.block_on(work) } // This should be compiled away, it will simply wait forever however its never @@ -318,22 +453,18 @@ impl std::future::Future for InfiniteSleep { } } -/// Performs an immuatble operation on the socket while running in an asynchronous runtime +/// Performs an immutable operation on the socket while running in an asynchronous runtime /// This has built in signal support -pub(crate) fn __sock_actor( - ctx: &mut FunctionEnvMut<'_, WasiEnv>, +pub(crate) fn __sock_asyncify<'a, T, F, Fut>( + env: &'a WasiEnv, sock: WasiFd, rights: Rights, actor: F, ) -> Result where - T: 'static, - F: FnOnce(crate::net::socket::InodeSocket) -> Fut + 'static, + F: FnOnce(crate::net::socket::InodeSocket, Fd) -> Fut, Fut: std::future::Future>, { - let env = ctx.data(); - let tasks = env.tasks.clone(); - let state = env.state.clone(); let inodes = state.inodes.clone(); @@ -356,7 +487,7 @@ where drop(guard); // Start the work using the socket - actor(socket) + actor(socket, fd_entry) } _ => { return Err(Errno::Notsock); @@ -365,21 +496,64 @@ where }; // Block on the work and process it - tasks.block_on(work) + env.tasks.block_on(work) } /// Performs mutable work on a socket under an asynchronous runtime with /// built in signal processing -pub(crate) fn __sock_actor_mut<'a, T, F, Fut>( - ctx: &'a mut FunctionEnvMut<'_, WasiEnv>, +pub(crate) fn __sock_asyncify_mut( + ctx: &'_ mut FunctionEnvMut<'_, WasiEnv>, + sock: WasiFd, + rights: Rights, + actor: F, +) -> Result +where + F: FnOnce(crate::net::socket::InodeSocket, Fd) -> Fut, + Fut: std::future::Future>, +{ + let env = ctx.data(); + let tasks = env.tasks.clone(); + + let state = env.state.clone(); + let inodes = state.inodes.clone(); + + let fd_entry = state.fs.get_fd(sock)?; + if !rights.is_empty() && !fd_entry.rights.contains(rights) { + return Err(Errno::Access); + } + + let inode_idx = fd_entry.inode; + let inodes_guard = inodes.read().unwrap(); + let inode = &inodes_guard.arena[inode_idx]; + let mut guard = inode.write(); + match guard.deref_mut() { + Kind::Socket { socket } => { + // Clone the socket and release the lock + let socket = socket.clone(); + drop(guard); + drop(inodes_guard); + + // Start the work using the socket + let work = actor(socket, fd_entry); + + // Block on the work and process it + tasks.block_on(work) + } + _ => Err(Errno::Notsock), + } +} + +/// Performs an immutable operation on the socket while running in an asynchronous runtime +/// This has built in signal support +pub(crate) fn __sock_actor( + ctx: &mut FunctionEnvMut<'_, WasiEnv>, sock: WasiFd, rights: Rights, actor: F, ) -> Result where T: 'static, - F: FnOnce(crate::net::socket::InodeSocket) -> Fut, - Fut: std::future::Future> + 'a, + F: FnOnce(crate::net::socket::InodeSocket, Fd) -> Result, { let env = ctx.data(); let tasks = env.tasks.clone(); @@ -392,27 +566,65 @@ where return Err(Errno::Access); } + let inodes_guard = inodes.read().unwrap(); + let inode_idx = fd_entry.inode; + let inode = &inodes_guard.arena[inode_idx]; + let tasks = env.tasks.clone(); - { - let inode_idx = fd_entry.inode; - let inodes_guard = inodes.read().unwrap(); - let inode = &inodes_guard.arena[inode_idx]; - let mut guard = inode.write(); - match guard.deref_mut() { - Kind::Socket { socket } => { - // Clone the socket and release the lock - let socket = socket.clone(); - drop(guard); - drop(inodes_guard); + let mut guard = inode.read(); + match guard.deref() { + Kind::Socket { socket } => { + // Clone the socket and release the lock + let socket = socket.clone(); + drop(guard); + + // Start the work using the socket + actor(socket, fd_entry) + } + _ => { + return Err(Errno::Notsock); + } + } +} - // Start the work using the socket - let work = actor(socket); +/// Performs mutable work on a socket under an asynchronous runtime with +/// built in signal processing +pub(crate) fn __sock_actor_mut<'a, T, F>( + ctx: &'a mut FunctionEnvMut<'_, WasiEnv>, + sock: WasiFd, + rights: Rights, + actor: F, +) -> Result +where + T: 'static, + F: FnOnce(crate::net::socket::InodeSocket, Fd) -> Result, +{ + let env = ctx.data(); + let tasks = env.tasks.clone(); - // Block on the work and process it - tasks.block_on(work) - } - _ => Err(Errno::Notsock), + let state = env.state.clone(); + let inodes = state.inodes.clone(); + + let fd_entry = state.fs.get_fd(sock)?; + if !rights.is_empty() && !fd_entry.rights.contains(rights) { + return Err(Errno::Access); + } + + let inode_idx = fd_entry.inode; + let inodes_guard = inodes.read().unwrap(); + let inode = &inodes_guard.arena[inode_idx]; + let mut guard = inode.write(); + match guard.deref_mut() { + Kind::Socket { socket } => { + // Clone the socket and release the lock + let socket = socket.clone(); + drop(guard); + drop(inodes_guard); + + // Start the work using the socket + actor(socket, fd_entry) } + _ => Err(Errno::Notsock), } } diff --git a/lib/wasi/src/syscalls/wasi/fd_close.rs b/lib/wasi/src/syscalls/wasi/fd_close.rs index ceecc06c14a..86998d4c556 100644 --- a/lib/wasi/src/syscalls/wasi/fd_close.rs +++ b/lib/wasi/src/syscalls/wasi/fd_close.rs @@ -36,7 +36,7 @@ pub fn fd_close(mut ctx: FunctionEnvMut<'_, WasiEnv>, fd: WasiFd) -> Result( return Ok(Errno::Access); } - let is_non_blocking = fd_entry.flags.contains(Fdflags::NONBLOCK); let inode_idx = fd_entry.inode; + let fd_flags = fd_entry.flags; let max_size = { let memory = env.memory_view(&ctx); @@ -152,7 +152,7 @@ fn fd_read_internal( let data = wasi_try_ok!(__asyncify( &mut ctx, - if is_non_blocking { + if fd_flags.contains(Fdflags::NONBLOCK) { Some(Duration::ZERO) } else { None @@ -206,14 +206,30 @@ fn fd_read_internal( drop(guard); drop(inodes); + let tasks = env.tasks.clone(); let res = __asyncify( &mut ctx, - if is_non_blocking { + if fd_flags.contains(Fdflags::NONBLOCK) { Some(Duration::ZERO) } else { None }, - async move { socket.recv(max_size).await }, + async { + let mut buf = Vec::with_capacity(max_size); + unsafe { + buf.set_len(max_size); + } + socket + .recv(tasks.deref(), &mut buf, fd_flags) + .await + .map(|amt| { + unsafe { + buf.set_len(amt); + } + let buf: Vec = unsafe { std::mem::transmute(buf) }; + buf + }) + }, )? .map_err(|err| match err { Errno::Timedout => Errno::Again, @@ -244,7 +260,7 @@ fn fd_read_internal( let data = wasi_try_ok!(__asyncify( &mut ctx, - if is_non_blocking { + if fd_flags.contains(Fdflags::NONBLOCK) { Some(Duration::ZERO) } else { None @@ -317,7 +333,7 @@ fn fd_read_internal( } // If its none blocking then exit - if is_non_blocking { + if fd_flags.contains(Fdflags::NONBLOCK) { return Ok(Errno::Again); } diff --git a/lib/wasi/src/syscalls/wasi/fd_write.rs b/lib/wasi/src/syscalls/wasi/fd_write.rs index 709b996a911..98ba4585662 100644 --- a/lib/wasi/src/syscalls/wasi/fd_write.rs +++ b/lib/wasi/src/syscalls/wasi/fd_write.rs @@ -112,7 +112,7 @@ fn fd_write_internal( return Ok(Errno::Access); } - let is_non_blocking = fd_entry.flags.contains(Fdflags::NONBLOCK); + let fd_flags = fd_entry.flags; let inode_idx = fd_entry.inode; let (bytes_written, can_update_cursor) = { @@ -138,12 +138,12 @@ fn fd_write_internal( let written = wasi_try_ok!(__asyncify( &mut ctx, - if is_non_blocking { + if fd_entry.flags.contains(Fdflags::NONBLOCK) { Some(Duration::ZERO) } else { None }, - async move { + async { let mut handle = handle.write().unwrap(); if !is_stdio { handle @@ -179,8 +179,9 @@ fn fd_write_internal( let mut buf = Vec::with_capacity(buf_len); wasi_try_ok!(write_bytes(&mut buf, &memory, iovs_arr)); + let tasks = env.tasks.clone(); let written = wasi_try_ok!(__asyncify(&mut ctx, None, async move { - socket.send(buf).await + socket.send(tasks.deref(), &buf, fd_flags).await })?); (written, false) } @@ -208,12 +209,13 @@ fn fd_write_internal( immediate, .. } => { - let mut val = 0u64.to_ne_bytes(); - let written = wasi_try_ok!(write_bytes(&mut val[..], &memory, iovs_arr)); + let mut val: [MaybeUninit; 8] = + unsafe { MaybeUninit::uninit().assume_init() }; + let written = wasi_try_ok!(copy_to_slice(&memory, iovs_arr, &mut val[..])); if written != val.len() { return Ok(Errno::Inval); } - let val = u64::from_ne_bytes(val); + let val = u64::from_ne_bytes(unsafe { std::mem::transmute(val) }); counter.fetch_add(val, Ordering::AcqRel); { diff --git a/lib/wasi/src/syscalls/wasi/poll_oneoff.rs b/lib/wasi/src/syscalls/wasi/poll_oneoff.rs index 1a83911e6a6..d7d11c4715d 100644 --- a/lib/wasi/src/syscalls/wasi/poll_oneoff.rs +++ b/lib/wasi/src/syscalls/wasi/poll_oneoff.rs @@ -3,6 +3,7 @@ use wasmer_wasi_types::wasi::SubscriptionClock; use super::*; use crate::{ fs::{InodeValFilePollGuard, InodeValFilePollGuardJoin}, + state::PollEventSet, syscalls::*, }; @@ -30,11 +31,11 @@ pub fn poll_oneoff( let mut env = ctx.data(); let mut memory = env.memory_view(&ctx); - let mut subscriptions = Vec::new(); let subscription_array = wasi_try_mem_ok!(in_.slice(&memory, nsubscriptions)); + let mut subscriptions = Vec::with_capacity(subscription_array.len() as usize); for sub in subscription_array.iter() { let s = wasi_try_mem_ok!(sub.read()); - subscriptions.push(s); + subscriptions.push((None, PollEventSet::default(), s)); } // Poll and receive all the events that triggered @@ -70,6 +71,7 @@ pub fn poll_oneoff( struct PollBatch<'a> { pid: WasiProcessId, tid: WasiThreadId, + evts: Vec, joins: Vec>, } impl<'a> PollBatch<'a> { @@ -77,6 +79,7 @@ impl<'a> PollBatch<'a> { Self { pid, tid, + evts: Vec::new(), joins: fds.iter_mut().map(InodeValFilePollGuardJoin::new).collect(), } } @@ -94,24 +97,22 @@ impl<'a> Future for PollBatch<'a> { let mut guard = Pin::new(join); match guard.poll(cx) { Poll::Pending => {} - Poll::Ready(mut res) => { - for evt in res.iter() { - tracing::trace!( - "wasi[{}:{}]::poll_oneoff triggered_fd (fd={}, userdata={}, type={:?})", - pid, - tid, - fd, - evt.userdata, - evt.type_, - ); - } - evts.append(&mut res); + Poll::Ready(evt) => { + tracing::trace!( + "wasi[{}:{}]::poll_oneoff triggered_fd (fd={}, userdata={}, type={:?})", + pid, + tid, + fd, + evt.userdata, + evt.type_, + ); + evts.push(evt); done = true; } } } - if done { + if !evts.is_empty() { return Poll::Ready(Ok(evts)); } @@ -133,7 +134,7 @@ impl<'a> Future for PollBatch<'a> { /// The number of events seen pub(crate) fn poll_oneoff_internal( ctx: &mut FunctionEnvMut<'_, WasiEnv>, - subs: Vec, + mut subs: Vec<(Option, PollEventSet, Subscription)>, ) -> Result, Errno>, WasiError> { let pid = ctx.data().pid(); let tid = ctx.data().tid(); @@ -150,16 +151,17 @@ pub(crate) fn poll_oneoff_internal( // These are used when we capture what clocks (timeouts) are being // subscribed too - let mut clock_subs: Vec<(SubscriptionClock, u64)> = vec![]; + let clock_cnt = subs + .iter() + .filter(|a| a.2.type_ == Eventtype::Clock) + .count(); + let mut clock_subs: Vec<(SubscriptionClock, u64)> = Vec::with_capacity(subs.len()); let mut time_to_sleep = None; // First we extract all the subscriptions into an array so that they // can be processed let mut memory = env.memory_view(&ctx); - let mut subscriptions = HashMap::new(); - for s in subs { - let mut peb = PollEventBuilder::new(); - let mut in_events = HashMap::new(); + for (fd, peb, s) in subs.iter_mut() { let fd = match s.type_ { Eventtype::FdRead => { let file_descriptor = unsafe { s.data.fd_readwrite.file_descriptor }; @@ -175,7 +177,8 @@ pub(crate) fn poll_oneoff_internal( } } } - in_events.insert(peb.add(PollEvent::PollIn).build(), s); + *fd = Some(file_descriptor); + *peb = *peb | (PollEvent::PollIn as PollEventSet); file_descriptor } Eventtype::FdWrite => { @@ -192,7 +195,8 @@ pub(crate) fn poll_oneoff_internal( } } } - in_events.insert(peb.add(PollEvent::PollOut).build(), s); + *fd = Some(file_descriptor); + *peb = *peb | (PollEvent::PollOut as PollEventSet); file_descriptor } Eventtype::Clock => { @@ -232,11 +236,6 @@ pub(crate) fn poll_oneoff_internal( } } }; - - let entry = subscriptions - .entry(fd) - .or_insert_with(HashMap::::default); - entry.extend(in_events.into_iter()); } let mut events_seen: u32 = 0; @@ -250,56 +249,58 @@ pub(crate) fn poll_oneoff_internal( // and open a read lock on them all let inodes = state.inodes.clone(); let inodes = inodes.read().unwrap(); - let mut fd_guards = vec![]; + let mut fd_guards = Vec::with_capacity(subs.len()); #[allow(clippy::significant_drop_in_scrutinee)] - for (fd, in_events) in subscriptions { - let wasi_file_ref = match fd { - __WASI_STDERR_FILENO => { - wasi_try_ok_ok!(inodes - .stderr(&state.fs.fd_map) - .map(|g| g.into_poll_guard(fd, in_events)) - .map_err(fs_error_into_wasi_err)) - } - __WASI_STDIN_FILENO => { - wasi_try_ok_ok!(inodes - .stdin(&state.fs.fd_map) - .map(|g| g.into_poll_guard(fd, in_events)) - .map_err(fs_error_into_wasi_err)) - } - __WASI_STDOUT_FILENO => { - wasi_try_ok_ok!(inodes - .stdout(&state.fs.fd_map) - .map(|g| g.into_poll_guard(fd, in_events)) - .map_err(fs_error_into_wasi_err)) - } - _ => { - let fd_entry = wasi_try_ok_ok!(state.fs.get_fd(fd)); - if !fd_entry.rights.contains(Rights::POLL_FD_READWRITE) { - return Ok(Err(Errno::Access)); + for (fd, peb, s) in subs { + if let Some(fd) = fd { + let wasi_file_ref = match fd { + __WASI_STDERR_FILENO => { + wasi_try_ok_ok!(inodes + .stderr(&state.fs.fd_map) + .map(|g| g.into_poll_guard(fd, peb, s)) + .map_err(fs_error_into_wasi_err)) } - let inode = fd_entry.inode; + __WASI_STDIN_FILENO => { + wasi_try_ok_ok!(inodes + .stdin(&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!(inodes + .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)); + if !fd_entry.rights.contains(Rights::POLL_FD_READWRITE) { + return Ok(Err(Errno::Access)); + } + let inode = fd_entry.inode; - { - let guard = inodes.arena[inode].read(); - if let Some(guard) = - crate::fs::InodeValFilePollGuard::new(fd, guard.deref(), in_events) { - guard - } else { - return Ok(Err(Errno::Badf)); + let guard = inodes.arena[inode].read(); + if let Some(guard) = + crate::fs::InodeValFilePollGuard::new(fd, peb, s, guard.deref()) + { + guard + } else { + return Ok(Err(Errno::Badf)); + } } } - } - }; - tracing::trace!( - "wasi[{}:{}]::poll_oneoff wait_for_fd={} type={:?}", - pid, - tid, - fd, - wasi_file_ref - ); - fd_guards.push(wasi_file_ref); + }; + tracing::trace!( + "wasi[{}:{}]::poll_oneoff wait_for_fd={} type={:?}", + pid, + tid, + fd, + wasi_file_ref + ); + fd_guards.push(wasi_file_ref); + } } fd_guards diff --git a/lib/wasi/src/syscalls/wasix/futex_wait.rs b/lib/wasi/src/syscalls/wasix/futex_wait.rs index a516f5994ba..79283d4e7c3 100644 --- a/lib/wasi/src/syscalls/wasix/futex_wait.rs +++ b/lib/wasi/src/syscalls/wasix/futex_wait.rs @@ -1,6 +1,62 @@ +use std::task::Waker; + use super::*; use crate::syscalls::*; +struct FutexPoller<'a, M> +where + M: MemorySize, +{ + env: &'a WasiEnv, + view: MemoryView<'a>, + futex_idx: u64, + futex_ptr: WasmPtr, + expected: u32, +} +impl<'a, M> Future for FutexPoller<'a, M> +where + M: MemorySize, +{ + type Output = Result<(), Errno>; + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let waker = cx.waker(); + let mut guard = self.env.state.futexs.lock().unwrap(); + + { + let val = match self.futex_ptr.read(&self.view) { + Ok(a) => a, + Err(err) => return Poll::Ready(Err(mem_error_to_wasi(err))), + }; + if val != self.expected { + return Poll::Ready(Ok(())); + } + } + + let futex = guard + .entry(self.futex_idx) + .or_insert_with(|| WasiFutex { wakers: vec![] }); + if futex.wakers.iter().any(|w| w.will_wake(waker)) == false { + futex.wakers.push(waker.clone()); + } + + Poll::Pending + } +} +impl<'a, M> Drop for FutexPoller<'a, M> +where + M: MemorySize, +{ + fn drop(&mut self) { + let futex = { + let mut guard = self.env.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. /// Returns false on timeout, and true in all other cases. @@ -29,7 +85,7 @@ pub fn futex_wait( let mut env = ctx.data(); let state = env.state.clone(); - let pointer: u64 = wasi_try_ok!(futex_ptr.offset().try_into().map_err(|_| Errno::Overflow)); + let futex_idx: u64 = wasi_try_ok!(futex_ptr.offset().try_into().map_err(|_| Errno::Overflow)); // Determine the timeout let timeout = { @@ -37,74 +93,36 @@ pub fn futex_wait( wasi_try_mem_ok!(timeout.read(&memory)) }; let timeout = match timeout.tag { - OptionTag::Some => Some(timeout.u as u128), + OptionTag::Some => Some(Duration::from_nanos(timeout.u as u64)), _ => None, }; - // Loop until we either hit a yield error or the futex is woken - let mut woken = Bool::False; - let start = platform_clock_time_get(Snapshot0Clockid::Monotonic, 1).unwrap() as u128; - loop { - // Register the waiting futex (if its not already registered) - let mut rx = { - use std::collections::hash_map::Entry; - let mut guard = state.futexs.write().unwrap(); - guard.entry(pointer).or_insert_with(|| WasiFutex { - refcnt: AtomicU32::new(1), - waker: tokio::sync::broadcast::channel(1).0, - }); - let futex = guard.get_mut(&pointer).unwrap(); - - // If the value of the memory is no longer the expected value - // then terminate from the loop (we do this under a futex lock - // so that its protected) - let rx = futex.waker.subscribe(); - { - let view = env.memory_view(&ctx); - let val = wasi_try_mem_ok!(futex_ptr.read(&view)); - if val != expected { - woken = Bool::True; - break; - } - } - rx - }; - - // Check if we have timed out - let mut sub_timeout = None; - if let Some(timeout) = timeout.as_ref() { - let now = platform_clock_time_get(Snapshot0Clockid::Monotonic, 1).unwrap() as u128; - let delta = now.saturating_sub(start); - if delta >= *timeout { - break; - } - let remaining = *timeout - delta; - sub_timeout = Some(Duration::from_nanos(remaining as u64)); - } - //sub_timeout.replace(sub_timeout.map(|a| a.min(Duration::from_millis(10))).unwrap_or(Duration::from_millis(10))); + // 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, + futex_idx, + futex_ptr, + expected, + }; - // Now wait for it to be triggered - __asyncify(&mut ctx, sub_timeout, async move { - rx.recv().await.ok(); - Ok(()) - })?; - env = ctx.data(); - } + // Wait for the futex to trigger or a timeout to occur + let res = __asyncify_light(env, timeout, poller)?; - // Drop the reference count to the futex (and remove it if the refcnt hits zero) - { - let mut guard = state.futexs.write().unwrap(); - if guard - .get(&pointer) - .map(|futex| futex.refcnt.fetch_sub(1, Ordering::AcqRel) == 1) - .unwrap_or(false) - { - guard.remove(&pointer); + // 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, + }; let memory = env.memory_view(&ctx); - wasi_try_mem_ok!(ret_woken.write(&memory, woken)); - - Ok(Errno::Success) + let mut env = ctx.data(); + wasi_try_mem_ok!(ret_woken.write(&memory, Bool::False)); + Ok(ret) } diff --git a/lib/wasi/src/syscalls/wasix/futex_wake.rs b/lib/wasi/src/syscalls/wasix/futex_wake.rs index f1b63ffe95b..7202c8b617e 100644 --- a/lib/wasi/src/syscalls/wasix/futex_wake.rs +++ b/lib/wasi/src/syscalls/wasix/futex_wake.rs @@ -20,10 +20,19 @@ pub fn futex_wake( let pointer: u64 = wasi_try!(futex_ptr.offset().try_into().map_err(|_| Errno::Overflow)); let mut woken = false; - let mut guard = state.futexs.read().unwrap(); - if let Some(futex) = guard.get(&pointer) { - woken = futex.waker.receiver_count() > 0; - let _ = futex.waker.send(()); + let woken = { + let mut guard = state.futexs.lock().unwrap(); + if let Some(futex) = guard.get_mut(&pointer) { + futex.wakers.pop().map(|w| w.wake()); + if futex.wakers.is_empty() { + guard.remove(&pointer); + } + true + } else { + false + } + }; + if woken { trace!( %woken, "wasi[{}:{}]::futex_wake(offset={})", diff --git a/lib/wasi/src/syscalls/wasix/futex_wake_all.rs b/lib/wasi/src/syscalls/wasix/futex_wake_all.rs index d54ca24be9a..10cb7803513 100644 --- a/lib/wasi/src/syscalls/wasix/futex_wake_all.rs +++ b/lib/wasi/src/syscalls/wasix/futex_wake_all.rs @@ -18,20 +18,26 @@ pub fn futex_wake_all( let pointer: u64 = wasi_try!(futex_ptr.offset().try_into().map_err(|_| Errno::Overflow)); let mut woken = false; - let mut guard = state.futexs.read().unwrap(); - if let Some(futex) = guard.get(&pointer) { - woken = futex.waker.receiver_count() > 0; - let _ = futex.waker.send(()); + let woken = { + let mut guard = state.futexs.lock().unwrap(); + if let Some(futex) = guard.remove(&pointer) { + futex.wakers.into_iter().for_each(|w| w.wake()); + true + } else { + false + } + }; + if woken { trace!( %woken, - "wasi[{}:{}]::futex_wake_all(offset={})", + "wasi[{}:{}]::futex_wake(offset={})", ctx.data().pid(), ctx.data().tid(), futex_ptr.offset() ); } else { trace!( - "wasi[{}:{}]::futex_wake_all(offset={}) - nothing waiting", + "wasi[{}:{}]::futex_wake(offset={}) - nothing waiting", ctx.data().pid(), ctx.data().tid(), futex_ptr.offset() diff --git a/lib/wasi/src/syscalls/wasix/port_addr_add.rs b/lib/wasi/src/syscalls/wasix/port_addr_add.rs index 29e0f6d7237..9295097205c 100644 --- a/lib/wasi/src/syscalls/wasix/port_addr_add.rs +++ b/lib/wasi/src/syscalls/wasix/port_addr_add.rs @@ -20,9 +20,8 @@ pub fn port_addr_add( let memory = env.memory_view(&ctx); let cidr = wasi_try_ok!(crate::net::read_cidr(&memory, ip)); let net = env.net(); - wasi_try_ok!(__asyncify(&mut ctx, None, async move { + wasi_try_ok!(__asyncify(&mut ctx, None, async { net.ip_add(cidr.ip, cidr.prefix) - .await .map_err(net_error_into_wasi_err) })?); Ok(Errno::Success) diff --git a/lib/wasi/src/syscalls/wasix/port_addr_clear.rs b/lib/wasi/src/syscalls/wasix/port_addr_clear.rs index f41a56bb09e..e7dae45634e 100644 --- a/lib/wasi/src/syscalls/wasix/port_addr_clear.rs +++ b/lib/wasi/src/syscalls/wasix/port_addr_clear.rs @@ -11,8 +11,8 @@ pub fn port_addr_clear(mut ctx: FunctionEnvMut<'_, WasiEnv>) -> Result( let max_addrs: u64 = wasi_try_ok!(max_addrs.try_into().map_err(|_| Errno::Overflow)); let net = env.net(); - let addrs = wasi_try_ok!(__asyncify(&mut ctx, None, async move { - net.ip_list().await.map_err(net_error_into_wasi_err) + let addrs = wasi_try_ok!(__asyncify(&mut ctx, None, async { + net.ip_list().map_err(net_error_into_wasi_err) })?); let env = ctx.data(); let memory = env.memory_view(&ctx); diff --git a/lib/wasi/src/syscalls/wasix/port_addr_remove.rs b/lib/wasi/src/syscalls/wasix/port_addr_remove.rs index de34907a2b1..4f25777f5ef 100644 --- a/lib/wasi/src/syscalls/wasix/port_addr_remove.rs +++ b/lib/wasi/src/syscalls/wasix/port_addr_remove.rs @@ -20,8 +20,8 @@ pub fn port_addr_remove( let memory = env.memory_view(&ctx); let ip = wasi_try_ok!(crate::net::read_ip(&memory, ip)); let net = env.net(); - wasi_try_ok!(__asyncify(&mut ctx, None, async move { - net.ip_remove(ip).await.map_err(net_error_into_wasi_err) + wasi_try_ok!(__asyncify(&mut ctx, None, async { + net.ip_remove(ip).map_err(net_error_into_wasi_err) })?); Ok(Errno::Success) } diff --git a/lib/wasi/src/syscalls/wasix/port_gateway_set.rs b/lib/wasi/src/syscalls/wasix/port_gateway_set.rs index 39452a02e50..fb6eb13ee6f 100644 --- a/lib/wasi/src/syscalls/wasix/port_gateway_set.rs +++ b/lib/wasi/src/syscalls/wasix/port_gateway_set.rs @@ -21,8 +21,8 @@ pub fn port_gateway_set( let ip = wasi_try_ok!(crate::net::read_ip(&memory, ip)); let net = env.net(); - wasi_try_ok!(__asyncify(&mut ctx, None, async move { - net.gateway_set(ip).await.map_err(net_error_into_wasi_err) + wasi_try_ok!(__asyncify(&mut ctx, None, async { + net.gateway_set(ip).map_err(net_error_into_wasi_err) })?); Ok(Errno::Success) } diff --git a/lib/wasi/src/syscalls/wasix/port_mac.rs b/lib/wasi/src/syscalls/wasix/port_mac.rs index 4c629273ed3..3d57f2d823d 100644 --- a/lib/wasi/src/syscalls/wasix/port_mac.rs +++ b/lib/wasi/src/syscalls/wasix/port_mac.rs @@ -12,8 +12,8 @@ pub fn port_mac( let mut memory = env.memory_view(&ctx); let net = env.net(); - let mac = wasi_try_ok!(__asyncify(&mut ctx, None, async move { - net.mac().await.map_err(net_error_into_wasi_err) + let mac = wasi_try_ok!(__asyncify(&mut ctx, None, async { + net.mac().map_err(net_error_into_wasi_err) })?); let env = ctx.data(); let memory = env.memory_view(&ctx); diff --git a/lib/wasi/src/syscalls/wasix/port_route_add.rs b/lib/wasi/src/syscalls/wasix/port_route_add.rs index 2d273c436e5..51f593e0360 100644 --- a/lib/wasi/src/syscalls/wasix/port_route_add.rs +++ b/lib/wasi/src/syscalls/wasix/port_route_add.rs @@ -33,9 +33,8 @@ pub fn port_route_add( }; let net = env.net(); - wasi_try_ok!(__asyncify(&mut ctx, None, async move { + wasi_try_ok!(__asyncify(&mut ctx, None, async { net.route_add(cidr, via_router, preferred_until, expires_at) - .await .map_err(net_error_into_wasi_err) })?); Ok(Errno::Success) diff --git a/lib/wasi/src/syscalls/wasix/port_route_clear.rs b/lib/wasi/src/syscalls/wasix/port_route_clear.rs index c93b20d5a43..5ba8d7f2e2d 100644 --- a/lib/wasi/src/syscalls/wasix/port_route_clear.rs +++ b/lib/wasi/src/syscalls/wasix/port_route_clear.rs @@ -11,8 +11,8 @@ pub fn port_route_clear(mut ctx: FunctionEnvMut<'_, WasiEnv>) -> Result( wasi_try_mem_ok!(routes_ptr.slice(&memory, wasi_try_ok!(to_offset::(max_routes)))); let net = env.net(); - let routes = wasi_try_ok!(__asyncify(&mut ctx, None, async move { - net.route_list().await.map_err(net_error_into_wasi_err) + let routes = wasi_try_ok!(__asyncify(&mut ctx, None, async { + net.route_list().map_err(net_error_into_wasi_err) })?); let env = ctx.data(); let memory = env.memory_view(&ctx); diff --git a/lib/wasi/src/syscalls/wasix/port_route_remove.rs b/lib/wasi/src/syscalls/wasix/port_route_remove.rs index 709dedb2379..74d8b563e16 100644 --- a/lib/wasi/src/syscalls/wasix/port_route_remove.rs +++ b/lib/wasi/src/syscalls/wasix/port_route_remove.rs @@ -17,8 +17,8 @@ pub fn port_route_remove( let ip = wasi_try_ok!(crate::net::read_ip(&memory, ip)); let net = env.net(); - wasi_try_ok!(__asyncify(&mut ctx, None, async move { - net.route_remove(ip).await.map_err(net_error_into_wasi_err) + wasi_try_ok!(__asyncify(&mut ctx, None, async { + net.route_remove(ip).map_err(net_error_into_wasi_err) })?); Ok(Errno::Success) diff --git a/lib/wasi/src/syscalls/wasix/proc_exec.rs b/lib/wasi/src/syscalls/wasix/proc_exec.rs index 72891e39245..e304c6ef476 100644 --- a/lib/wasi/src/syscalls/wasix/proc_exec.rs +++ b/lib/wasi/src/syscalls/wasix/proc_exec.rs @@ -280,7 +280,7 @@ pub fn proc_exec( let tasks_inner = tasks.clone(); tasks.block_on(Box::pin(async move { loop { - tasks_inner.sleep_now(current_caller_id(), 5).await; + tasks_inner.sleep_now(Duration::from_millis(5)).await; if let Some(exit_code) = process.inst.exit_code() { tx.send(exit_code).unwrap(); break; diff --git a/lib/wasi/src/syscalls/wasix/sched_yield.rs b/lib/wasi/src/syscalls/wasix/sched_yield.rs index 87674501b4e..6c07db5bdcf 100644 --- a/lib/wasi/src/syscalls/wasix/sched_yield.rs +++ b/lib/wasi/src/syscalls/wasix/sched_yield.rs @@ -5,12 +5,5 @@ use crate::syscalls::*; /// Yields execution of the thread pub fn sched_yield(mut ctx: FunctionEnvMut<'_, WasiEnv>) -> Result { //trace!("wasi[{}:{}]::sched_yield", ctx.data().pid(), ctx.data().tid()); - let env = ctx.data(); - let tasks = env.tasks.clone(); - wasi_try_ok!(__asyncify(&mut ctx, None, async move { - tasks.sleep_now(current_caller_id(), 0).await; - Ok(()) - })?); - wasi_try_ok!(WasiEnv::process_signals_and_exit(&mut ctx)?); - Ok(Errno::Success) + thread_sleep_internal(ctx, 0) } diff --git a/lib/wasi/src/syscalls/wasix/sock_accept.rs b/lib/wasi/src/syscalls/wasix/sock_accept.rs index 06a2112c588..1ab01c3d413 100644 --- a/lib/wasi/src/syscalls/wasix/sock_accept.rs +++ b/lib/wasi/src/syscalls/wasix/sock_accept.rs @@ -16,7 +16,7 @@ use crate::syscalls::*; pub fn sock_accept( mut ctx: FunctionEnvMut<'_, WasiEnv>, sock: WasiFd, - fd_flags: Fdflags, + mut fd_flags: Fdflags, ro_fd: WasmPtr, ro_addr: WasmPtr<__wasi_addr_port_t, M>, ) -> Result { @@ -30,28 +30,44 @@ pub fn sock_accept( wasi_try_ok!(WasiEnv::process_signals_and_exit(&mut ctx)?); - let (child, addr) = wasi_try_ok!(__sock_actor( - &mut ctx, + let tasks = ctx.data().tasks.clone(); + let (child, addr, fd_flags) = wasi_try_ok!(__sock_asyncify( + ctx.data(), sock, Rights::SOCK_ACCEPT, - move |socket| async move { socket.accept(fd_flags).await } + move |socket, fd| async move { + if fd.flags.contains(Fdflags::NONBLOCK) { + fd_flags.set(Fdflags::NONBLOCK, true); + } + socket + .accept(tasks.deref(), fd_flags) + .await + .map(|a| (a.0, a.1, fd_flags)) + } )); let env = ctx.data(); let (memory, state, mut inodes) = env.get_memory_and_wasi_state_and_inodes_mut(&ctx, 0); let kind = Kind::Socket { - socket: InodeSocket::new(InodeSocketKind::TcpStream(child)), + socket: InodeSocket::new(InodeSocketKind::TcpStream { + socket: child, + write_timeout: None, + read_timeout: None, + }), }; let inode = state .fs .create_inode_with_default_stat(inodes.deref_mut(), kind, false, "socket".into()); + let mut new_flags = Fdflags::empty(); + if fd_flags.contains(Fdflags::NONBLOCK) { + new_flags.set(Fdflags::NONBLOCK, true); + } + let rights = Rights::all_socket(); - let fd = wasi_try_ok!(state - .fs - .create_fd(rights, rights, Fdflags::empty(), 0, inode)); + let fd = wasi_try_ok!(state.fs.create_fd(rights, rights, new_flags, 0, inode)); debug!( "wasi[{}:{}]::sock_accept (ret=ESUCCESS, peer={})", diff --git a/lib/wasi/src/syscalls/wasix/sock_addr_local.rs b/lib/wasi/src/syscalls/wasix/sock_addr_local.rs index cb522042c34..3dcb7096f69 100644 --- a/lib/wasi/src/syscalls/wasix/sock_addr_local.rs +++ b/lib/wasi/src/syscalls/wasix/sock_addr_local.rs @@ -28,7 +28,7 @@ pub fn sock_addr_local( &mut ctx, sock, Rights::empty(), - move |socket| async move { socket.addr_local().await } + |socket, _| socket.addr_local() )); let memory = ctx.data().memory_view(&ctx); wasi_try!(crate::net::write_ip_port( diff --git a/lib/wasi/src/syscalls/wasix/sock_addr_peer.rs b/lib/wasi/src/syscalls/wasix/sock_addr_peer.rs index 173c9ddcb0f..177db7c58eb 100644 --- a/lib/wasi/src/syscalls/wasix/sock_addr_peer.rs +++ b/lib/wasi/src/syscalls/wasix/sock_addr_peer.rs @@ -28,7 +28,7 @@ pub fn sock_addr_peer( &mut ctx, sock, Rights::empty(), - move |socket| async move { socket.addr_peer().await } + |socket, _| socket.addr_peer() )); let env = ctx.data(); diff --git a/lib/wasi/src/syscalls/wasix/sock_bind.rs b/lib/wasi/src/syscalls/wasix/sock_bind.rs index a5f069ad6a5..b7a9f100b5d 100644 --- a/lib/wasi/src/syscalls/wasix/sock_bind.rs +++ b/lib/wasi/src/syscalls/wasix/sock_bind.rs @@ -26,11 +26,14 @@ pub fn sock_bind( let addr = wasi_try!(crate::net::read_ip_port(&memory, addr)); let addr = SocketAddr::new(addr.0, addr.1); let net = env.net(); + + let tasks = ctx.data().tasks.clone(); wasi_try!(__sock_upgrade( &mut ctx, sock, Rights::SOCK_BIND, - move |socket| async move { socket.bind(net, addr).await } + move |socket| async move { socket.bind(tasks.deref(), net.deref(), addr).await } )); + Errno::Success } diff --git a/lib/wasi/src/syscalls/wasix/sock_connect.rs b/lib/wasi/src/syscalls/wasix/sock_connect.rs index 24a4572cc3a..402489b722a 100644 --- a/lib/wasi/src/syscalls/wasix/sock_connect.rs +++ b/lib/wasi/src/syscalls/wasix/sock_connect.rs @@ -31,11 +31,13 @@ pub fn sock_connect( let addr = wasi_try!(crate::net::read_ip_port(&memory, addr)); let addr = SocketAddr::new(addr.0, addr.1); + let tasks = ctx.data().tasks.clone(); wasi_try!(__sock_upgrade( &mut ctx, sock, Rights::SOCK_CONNECT, - move |mut socket| async move { socket.connect(net, addr).await } + move |mut socket| async move { socket.connect(tasks.deref(), net.deref(), addr, None).await } )); + Errno::Success } diff --git a/lib/wasi/src/syscalls/wasix/sock_get_opt_flag.rs b/lib/wasi/src/syscalls/wasix/sock_get_opt_flag.rs index 5afd3350f7c..350f00f3035 100644 --- a/lib/wasi/src/syscalls/wasix/sock_get_opt_flag.rs +++ b/lib/wasi/src/syscalls/wasix/sock_get_opt_flag.rs @@ -28,7 +28,7 @@ pub fn sock_get_opt_flag( &mut ctx, sock, Rights::empty(), - move |socket| async move { socket.get_opt_flag(option).await } + |socket, _| socket.get_opt_flag(option) )); let env = ctx.data(); diff --git a/lib/wasi/src/syscalls/wasix/sock_get_opt_size.rs b/lib/wasi/src/syscalls/wasix/sock_get_opt_size.rs index 45a12a2632f..f24f9b0b44b 100644 --- a/lib/wasi/src/syscalls/wasix/sock_get_opt_size.rs +++ b/lib/wasi/src/syscalls/wasix/sock_get_opt_size.rs @@ -26,16 +26,14 @@ pub fn sock_get_opt_size( &mut ctx, sock, Rights::empty(), - move |socket| async move { - match opt { - Sockoption::RecvBufSize => socket.recv_buf_size().await.map(|a| a as Filesize), - Sockoption::SendBufSize => socket.send_buf_size().await.map(|a| a as Filesize), - Sockoption::Ttl => socket.ttl().await.map(|a| a as Filesize), - Sockoption::MulticastTtlV4 => { - socket.multicast_ttl_v4().await.map(|a| a as Filesize) - } - _ => Err(Errno::Inval), + |socket, _| match opt { + Sockoption::RecvBufSize => socket.recv_buf_size().map(|a| a as Filesize), + Sockoption::SendBufSize => socket.send_buf_size().map(|a| a as Filesize), + Sockoption::Ttl => socket.ttl().map(|a| a as Filesize), + Sockoption::MulticastTtlV4 => { + socket.multicast_ttl_v4().map(|a| a as Filesize) } + _ => Err(Errno::Inval), } )); diff --git a/lib/wasi/src/syscalls/wasix/sock_get_opt_time.rs b/lib/wasi/src/syscalls/wasix/sock_get_opt_time.rs index a6650b2d0b0..78bf0d784ca 100644 --- a/lib/wasi/src/syscalls/wasix/sock_get_opt_time.rs +++ b/lib/wasi/src/syscalls/wasix/sock_get_opt_time.rs @@ -1,5 +1,5 @@ use super::*; -use crate::syscalls::*; +use crate::{net::socket::TimeType, syscalls::*}; /// ### `sock_get_opt_time()` /// Retrieve one of the times on the socket @@ -23,11 +23,11 @@ pub fn sock_get_opt_time( ); let ty = match opt { - Sockoption::RecvTimeout => wasmer_vnet::TimeType::ReadTimeout, - Sockoption::SendTimeout => wasmer_vnet::TimeType::WriteTimeout, - Sockoption::ConnectTimeout => wasmer_vnet::TimeType::ConnectTimeout, - Sockoption::AcceptTimeout => wasmer_vnet::TimeType::AcceptTimeout, - Sockoption::Linger => wasmer_vnet::TimeType::Linger, + Sockoption::RecvTimeout => TimeType::ReadTimeout, + Sockoption::SendTimeout => TimeType::WriteTimeout, + Sockoption::ConnectTimeout => TimeType::ConnectTimeout, + Sockoption::AcceptTimeout => TimeType::AcceptTimeout, + Sockoption::Linger => TimeType::Linger, _ => return Errno::Inval, }; @@ -35,7 +35,7 @@ pub fn sock_get_opt_time( &mut ctx, sock, Rights::empty(), - move |socket| async move { socket.opt_time(ty).await } + |socket, _| socket.opt_time(ty) )); let env = ctx.data(); diff --git a/lib/wasi/src/syscalls/wasix/sock_join_multicast_v4.rs b/lib/wasi/src/syscalls/wasix/sock_join_multicast_v4.rs index 407a6ee983d..758cbeb4e1f 100644 --- a/lib/wasi/src/syscalls/wasix/sock_join_multicast_v4.rs +++ b/lib/wasi/src/syscalls/wasix/sock_join_multicast_v4.rs @@ -30,7 +30,7 @@ pub fn sock_join_multicast_v4( &mut ctx, sock, Rights::empty(), - move |socket| async move { socket.join_multicast_v4(multiaddr, iface).await } + |socket, _| socket.join_multicast_v4(multiaddr, iface) )); Errno::Success } diff --git a/lib/wasi/src/syscalls/wasix/sock_join_multicast_v6.rs b/lib/wasi/src/syscalls/wasix/sock_join_multicast_v6.rs index 40660c6fcf6..ddfc5fcf71a 100644 --- a/lib/wasi/src/syscalls/wasix/sock_join_multicast_v6.rs +++ b/lib/wasi/src/syscalls/wasix/sock_join_multicast_v6.rs @@ -29,7 +29,7 @@ pub fn sock_join_multicast_v6( &mut ctx, sock, Rights::empty(), - move |socket| async move { socket.join_multicast_v6(multiaddr, iface).await } + |socket, _| socket.join_multicast_v6(multiaddr, iface) )); Errno::Success } diff --git a/lib/wasi/src/syscalls/wasix/sock_leave_multicast_v4.rs b/lib/wasi/src/syscalls/wasix/sock_leave_multicast_v4.rs index c3a60408333..32d713405f1 100644 --- a/lib/wasi/src/syscalls/wasix/sock_leave_multicast_v4.rs +++ b/lib/wasi/src/syscalls/wasix/sock_leave_multicast_v4.rs @@ -30,7 +30,7 @@ pub fn sock_leave_multicast_v4( &mut ctx, sock, Rights::empty(), - move |socket| async move { socket.leave_multicast_v4(multiaddr, iface).await } + |socket, _| socket.leave_multicast_v4(multiaddr, iface) )); Errno::Success } diff --git a/lib/wasi/src/syscalls/wasix/sock_leave_multicast_v6.rs b/lib/wasi/src/syscalls/wasix/sock_leave_multicast_v6.rs index 3aee62a3283..4a824fd6474 100644 --- a/lib/wasi/src/syscalls/wasix/sock_leave_multicast_v6.rs +++ b/lib/wasi/src/syscalls/wasix/sock_leave_multicast_v6.rs @@ -29,7 +29,7 @@ pub fn sock_leave_multicast_v6( &mut ctx, sock, Rights::empty(), - move |mut socket| async move { socket.leave_multicast_v6(multiaddr, iface).await } + |mut socket, _| socket.leave_multicast_v6(multiaddr, iface) )); Errno::Success } diff --git a/lib/wasi/src/syscalls/wasix/sock_listen.rs b/lib/wasi/src/syscalls/wasix/sock_listen.rs index eb26f157ed8..f0111d1f5ba 100644 --- a/lib/wasi/src/syscalls/wasix/sock_listen.rs +++ b/lib/wasi/src/syscalls/wasix/sock_listen.rs @@ -28,11 +28,14 @@ pub fn sock_listen( let env = ctx.data(); let net = env.net(); let backlog: usize = wasi_try!(backlog.try_into().map_err(|_| Errno::Inval)); + + let tasks = ctx.data().tasks.clone(); wasi_try!(__sock_upgrade( &mut ctx, sock, Rights::SOCK_LISTEN, - move |socket| async move { socket.listen(net, backlog).await } + |socket| async move { socket.listen(tasks.deref(), net.deref(), backlog).await } )); + Errno::Success } diff --git a/lib/wasi/src/syscalls/wasix/sock_open.rs b/lib/wasi/src/syscalls/wasix/sock_open.rs index 733ea924462..6f577cfc9d2 100644 --- a/lib/wasi/src/syscalls/wasix/sock_open.rs +++ b/lib/wasi/src/syscalls/wasix/sock_open.rs @@ -42,13 +42,12 @@ pub fn sock_open( only_v6: false, reuse_port: false, reuse_addr: false, - nonblocking: false, send_buf_size: None, recv_buf_size: None, - send_timeout: None, - recv_timeout: None, - connect_timeout: None, + write_timeout: None, + read_timeout: None, accept_timeout: None, + connect_timeout: None, }), }, _ => return Errno::Notsup, diff --git a/lib/wasi/src/syscalls/wasix/sock_recv.rs b/lib/wasi/src/syscalls/wasix/sock_recv.rs index 9c3f3e07e4a..932d88c3c56 100644 --- a/lib/wasi/src/syscalls/wasix/sock_recv.rs +++ b/lib/wasi/src/syscalls/wasix/sock_recv.rs @@ -1,3 +1,5 @@ +use std::mem::MaybeUninit; + use super::*; use crate::syscalls::*; @@ -26,10 +28,10 @@ pub fn sock_recv( wasi_try_ok!(WasiEnv::process_signals_and_exit(&mut ctx)?); let mut env = ctx.data(); + let memory = env.memory_view(&ctx); + let iovs_arr = wasi_try_mem_ok!(ri_data.slice(&memory, ri_data_len)); let max_size = { - let memory = env.memory_view(&ctx); - let iovs_arr = wasi_try_mem_ok!(ri_data.slice(&memory, ri_data_len)); let mut max_size = 0usize; for iovs in iovs_arr.iter() { let iovs = wasi_try_mem_ok!(iovs.read()); @@ -39,23 +41,55 @@ pub fn sock_recv( max_size }; - let data = wasi_try_ok!(__sock_actor_mut( - &mut ctx, - sock, - Rights::SOCK_RECV, - move |socket| async move { socket.recv(max_size).await }, - )); - env = ctx.data(); + let bytes_read = { + if max_size <= 10240 { + let mut buf: [MaybeUninit; 10240] = unsafe { MaybeUninit::uninit().assume_init() }; + let writer = &mut buf[..max_size]; + let amt = wasi_try_ok!(__sock_asyncify( + env, + sock, + Rights::SOCK_RECV, + |socket, fd| async move { socket.recv(env.tasks.deref(), writer, fd.flags).await }, + )); - let memory = env.memory_view(&ctx); + if amt > 0 { + let buf: &[MaybeUninit] = &buf[..amt]; + let buf: &[u8] = unsafe { std::mem::transmute(buf) }; + wasi_try_ok!(copy_from_slice(buf, &memory, iovs_arr).map(|_| amt)) + } else { + 0 + } + } else { + let data = wasi_try_ok!(__sock_asyncify( + env, + sock, + Rights::SOCK_RECV, + |socket, fd| async move { + let mut buf = Vec::with_capacity(max_size); + unsafe { + buf.set_len(max_size); + } + socket + .recv(env.tasks.deref(), &mut buf, fd.flags) + .await + .map(|amt| { + unsafe { + buf.set_len(amt); + } + let buf: Vec = unsafe { std::mem::transmute(buf) }; + buf + }) + }, + )); - let data_len = data.len(); - let bytes_read = if data_len > 0 { - let mut reader = &data[..]; - let iovs_arr = wasi_try_mem_ok!(ri_data.slice(&memory, ri_data_len)); - wasi_try_ok!(read_bytes(reader, &memory, iovs_arr).map(|_| data_len)) - } else { - 0 + let data_len = data.len(); + if data_len > 0 { + let mut reader = &data[..]; + wasi_try_ok!(read_bytes(reader, &memory, iovs_arr).map(|_| data_len)) + } else { + 0 + } + } }; debug!( diff --git a/lib/wasi/src/syscalls/wasix/sock_recv_from.rs b/lib/wasi/src/syscalls/wasix/sock_recv_from.rs index a29e1631938..3349a380e84 100644 --- a/lib/wasi/src/syscalls/wasix/sock_recv_from.rs +++ b/lib/wasi/src/syscalls/wasix/sock_recv_from.rs @@ -1,3 +1,5 @@ +use std::mem::MaybeUninit; + use super::*; use crate::syscalls::*; @@ -34,10 +36,10 @@ pub fn sock_recv_from( wasi_try_ok!(WasiEnv::process_signals_and_exit(&mut ctx)?); let mut env = ctx.data(); + let memory = env.memory_view(&ctx); + let iovs_arr = wasi_try_mem_ok!(ri_data.slice(&memory, ri_data_len)); let max_size = { - let memory = env.memory_view(&ctx); - let iovs_arr = wasi_try_mem_ok!(ri_data.slice(&memory, ri_data_len)); let mut max_size = 0usize; for iovs in iovs_arr.iter() { let iovs = wasi_try_mem_ok!(iovs.read()); @@ -47,23 +49,60 @@ pub fn sock_recv_from( max_size }; - let (data, peer) = wasi_try_ok!(__sock_actor_mut( - &mut ctx, - sock, - Rights::SOCK_RECV_FROM, - move |socket| async move { socket.recv_from(max_size).await } - )); - env = ctx.data(); + let (bytes_read, peer) = { + if max_size <= 10240 { + let mut buf: [MaybeUninit; 10240] = unsafe { MaybeUninit::uninit().assume_init() }; + let writer = &mut buf[..max_size]; + let (amt, peer) = wasi_try_ok!(__sock_asyncify( + env, + sock, + Rights::SOCK_RECV, + |socket, fd| async move { socket.recv_from(env.tasks.deref(), writer, fd.flags).await }, + )); + + if amt > 0 { + let buf: &[MaybeUninit] = &buf[..amt]; + let buf: &[u8] = unsafe { std::mem::transmute(buf) }; + wasi_try_ok!(copy_from_slice(buf, &memory, iovs_arr).map(|_| (amt, peer))) + } else { + (amt, peer) + } + } else { + let (data, peer) = wasi_try_ok!(__sock_asyncify( + env, + sock, + Rights::SOCK_RECV_FROM, + |socket, fd| async move { + let mut buf = Vec::with_capacity(max_size); + unsafe { + buf.set_len(max_size); + } + socket + .recv_from(env.tasks.deref(), &mut buf, fd.flags) + .await + .map(|(amt, addr)| { + unsafe { + buf.set_len(amt); + } + let buf: Vec = unsafe { std::mem::transmute(buf) }; + (buf, addr) + }) + } + )); + + let data_len = data.len(); + if data_len > 0 { + let mut reader = &data[..]; + wasi_try_ok!(read_bytes(reader, &memory, iovs_arr).map(|_| (data_len, peer))) + } else { + (0, peer) + } + } + }; - let memory = env.memory_view(&ctx); - let iovs_arr = wasi_try_mem_ok!(ri_data.slice(&memory, ri_data_len)); wasi_try_ok!(write_ip_port(&memory, ro_addr, peer.ip(), peer.port())); - let data_len = data.len(); - let mut reader = &data[..]; - let bytes_read = wasi_try_ok!(read_bytes(reader, &memory, iovs_arr).map(|_| data_len)); let bytes_read: M::Offset = wasi_try_ok!(bytes_read.try_into().map_err(|_| Errno::Overflow)); - wasi_try_mem_ok!(ro_flags.write(&memory, 0)); wasi_try_mem_ok!(ro_data_len.write(&memory, bytes_read)); diff --git a/lib/wasi/src/syscalls/wasix/sock_send.rs b/lib/wasi/src/syscalls/wasix/sock_send.rs index 4adacd2a283..b62e0bb2333 100644 --- a/lib/wasi/src/syscalls/wasix/sock_send.rs +++ b/lib/wasi/src/syscalls/wasix/sock_send.rs @@ -1,3 +1,5 @@ +use std::mem::MaybeUninit; + use super::*; use crate::syscalls::*; @@ -22,14 +24,12 @@ pub fn sock_send( si_flags: SiFlags, ret_data_len: WasmPtr, ) -> Result { - wasi_try_ok!(WasiEnv::process_signals_and_exit(&mut ctx)?); - - let mut env = ctx.data(); + let env = ctx.data(); + let memory = env.memory_view(&ctx); + let iovs_arr = wasi_try_mem_ok!(si_data.slice(&memory, si_data_len)); let runtime = env.runtime.clone(); let buf_len: M::Offset = { - let memory = env.memory_view(&ctx); - let iovs_arr = wasi_try_mem_ok!(si_data.slice(&memory, si_data_len)); iovs_arr .iter() .filter_map(|a| a.read().ok()) @@ -45,24 +45,38 @@ pub fn sock_send( si_flags ); let buf_len: usize = wasi_try_ok!(buf_len.try_into().map_err(|_| Errno::Inval)); - let mut buf = Vec::with_capacity(buf_len); - { - let memory = env.memory_view(&ctx); - let iovs_arr = wasi_try_mem_ok!(si_data.slice(&memory, si_data_len)); - wasi_try_ok!(write_bytes(&mut buf, &memory, iovs_arr)); - } - let bytes_written = wasi_try_ok!(__sock_actor_mut( - &mut ctx, - sock, - Rights::SOCK_SEND, - move |socket| async move { socket.send(buf).await }, - )); - env = ctx.data(); + let bytes_written = { + if buf_len <= 10240 { + let mut buf: [MaybeUninit; 10240] = unsafe { MaybeUninit::uninit().assume_init() }; + let writer = &mut buf[..buf_len]; + let written = wasi_try_ok!(copy_to_slice(&memory, iovs_arr, writer)); + + let reader = &buf[..written]; + let reader: &[u8] = unsafe { std::mem::transmute(reader) }; + + wasi_try_ok!(__sock_asyncify( + env, + sock, + Rights::SOCK_SEND, + |socket, fd| async move { socket.send(env.tasks.deref(), reader, fd.flags).await }, + )) + } else { + let mut buf = Vec::with_capacity(buf_len); + wasi_try_ok!(write_bytes(&mut buf, &memory, iovs_arr)); + + let reader = &buf; + wasi_try_ok!(__sock_asyncify( + env, + sock, + Rights::SOCK_SEND, + |socket, fd| async move { socket.send(env.tasks.deref(), reader, fd.flags).await }, + )) + } + }; let bytes_written: M::Offset = wasi_try_ok!(bytes_written.try_into().map_err(|_| Errno::Overflow)); - let memory = env.memory_view(&ctx); wasi_try_mem_ok!(ret_data_len.write(&memory, bytes_written)); Ok(Errno::Success) diff --git a/lib/wasi/src/syscalls/wasix/sock_send_file.rs b/lib/wasi/src/syscalls/wasix/sock_send_file.rs index 5789eda7f28..916174a6308 100644 --- a/lib/wasi/src/syscalls/wasix/sock_send_file.rs +++ b/lib/wasi/src/syscalls/wasix/sock_send_file.rs @@ -53,6 +53,8 @@ pub fn sock_send_file( count -= sub_count; let fd_entry = wasi_try_ok!(state.fs.get_fd(in_fd)); + let fd_flags = fd_entry.flags; + let data = { let inodes = env.state.inodes.clone(); match in_fd { @@ -116,13 +118,22 @@ pub fn sock_send_file( drop(guard); drop(inodes); - let data = - wasi_try_ok!(__asyncify(&mut ctx, None, async move { - socket - .recv(sub_count as usize) - .await - .map(|a| a.to_vec()) - })?); + let data = wasi_try_ok!(__asyncify(&mut ctx, None, async { + let mut buf = Vec::with_capacity(sub_count as usize); + unsafe { + buf.set_len(sub_count as usize); + } + socket.recv(tasks.deref(), &mut buf, fd_flags).await.map( + |amt| { + unsafe { + buf.set_len(amt); + } + let buf: Vec = + unsafe { std::mem::transmute(buf) }; + buf + }, + ) + })?); env = ctx.data(); data } @@ -177,11 +188,12 @@ pub fn sock_send_file( }; // Write it down to the socket - let bytes_written = wasi_try_ok!(__sock_actor_mut( + let tasks = ctx.data().tasks.clone(); + let bytes_written = wasi_try_ok!(__sock_asyncify_mut( &mut ctx, sock, Rights::SOCK_SEND, - move |socket| async move { socket.send(data).await }, + |socket, fd| async move { socket.send(tasks.deref(), &data, fd.flags).await }, )); env = ctx.data(); diff --git a/lib/wasi/src/syscalls/wasix/sock_send_to.rs b/lib/wasi/src/syscalls/wasix/sock_send_to.rs index a330dca8b03..75e4a995b0a 100644 --- a/lib/wasi/src/syscalls/wasix/sock_send_to.rs +++ b/lib/wasi/src/syscalls/wasix/sock_send_to.rs @@ -30,14 +30,11 @@ pub fn sock_send_to( ctx.data().tid(), sock ); - - wasi_try_ok!(WasiEnv::process_signals_and_exit(&mut ctx)?); - - let mut env = ctx.data(); + let env = ctx.data(); + let memory = env.memory_view(&ctx); + let iovs_arr = wasi_try_mem_ok!(si_data.slice(&memory, si_data_len)); let buf_len: M::Offset = { - let memory = env.memory_view(&ctx); - let iovs_arr = wasi_try_mem_ok!(si_data.slice(&memory, si_data_len)); iovs_arr .iter() .filter_map(|a| a.read().ok()) @@ -45,30 +42,51 @@ pub fn sock_send_to( .sum() }; let buf_len: usize = wasi_try_ok!(buf_len.try_into().map_err(|_| Errno::Inval)); - let mut buf = Vec::with_capacity(buf_len); - { - let memory = env.memory_view(&ctx); - let iovs_arr = wasi_try_mem_ok!(si_data.slice(&memory, si_data_len)); - wasi_try_ok!(write_bytes(&mut buf, &memory, iovs_arr)); - } - let (addr_ip, addr_port) = { let memory = env.memory_view(&ctx); wasi_try_ok!(read_ip_port(&memory, addr)) }; let addr = SocketAddr::new(addr_ip, addr_port); - let bytes_written = wasi_try_ok!(__sock_actor_mut( - &mut ctx, - sock, - Rights::SOCK_SEND_TO, - move |socket| async move { socket.send_to::(buf, addr).await }, - )); - env = ctx.data(); + let bytes_written = { + if buf_len <= 10240 { + let mut buf: [MaybeUninit; 10240] = unsafe { MaybeUninit::uninit().assume_init() }; + let writer = &mut buf[..buf_len]; + let written = wasi_try_ok!(copy_to_slice(&memory, iovs_arr, writer)); + + let reader = &buf[..written]; + let reader: &[u8] = unsafe { std::mem::transmute(reader) }; + + wasi_try_ok!(__sock_asyncify( + env, + sock, + Rights::SOCK_SEND, + |socket, fd| async move { + socket + .send_to::(env.tasks.deref(), reader, addr, fd.flags) + .await + }, + )) + } else { + let mut buf = Vec::with_capacity(buf_len); + wasi_try_ok!(write_bytes(&mut buf, &memory, iovs_arr)); + + let reader = &buf; + wasi_try_ok!(__sock_asyncify( + env, + sock, + Rights::SOCK_SEND_TO, + |socket, fd| async move { + socket + .send_to::(env.tasks.deref(), reader, addr, fd.flags) + .await + }, + )) + } + }; let bytes_written: M::Offset = wasi_try_ok!(bytes_written.try_into().map_err(|_| Errno::Overflow)); - let memory = env.memory_view(&ctx); wasi_try_mem_ok!(ret_data_len.write(&memory, bytes_written as M::Offset)); Ok(Errno::Success) diff --git a/lib/wasi/src/syscalls/wasix/sock_set_opt_flag.rs b/lib/wasi/src/syscalls/wasix/sock_set_opt_flag.rs index 9aba3054b41..766ee5e527f 100644 --- a/lib/wasi/src/syscalls/wasix/sock_set_opt_flag.rs +++ b/lib/wasi/src/syscalls/wasix/sock_set_opt_flag.rs @@ -36,7 +36,7 @@ pub fn sock_set_opt_flag( &mut ctx, sock, Rights::empty(), - move |mut socket| async move { socket.set_opt_flag(option, flag).await } + |mut socket, _| socket.set_opt_flag(option, flag) )); Errno::Success } diff --git a/lib/wasi/src/syscalls/wasix/sock_set_opt_size.rs b/lib/wasi/src/syscalls/wasix/sock_set_opt_size.rs index 05706b0ec65..020d185a4d5 100644 --- a/lib/wasi/src/syscalls/wasix/sock_set_opt_size.rs +++ b/lib/wasi/src/syscalls/wasix/sock_set_opt_size.rs @@ -1,5 +1,5 @@ use super::*; -use crate::syscalls::*; +use crate::{net::socket::TimeType, syscalls::*}; /// ### `sock_set_opt_size() /// Set size of particular option for this socket @@ -25,11 +25,11 @@ pub fn sock_set_opt_size( ); let ty = match opt { - Sockoption::RecvTimeout => wasmer_vnet::TimeType::ReadTimeout, - Sockoption::SendTimeout => wasmer_vnet::TimeType::WriteTimeout, - Sockoption::ConnectTimeout => wasmer_vnet::TimeType::ConnectTimeout, - Sockoption::AcceptTimeout => wasmer_vnet::TimeType::AcceptTimeout, - Sockoption::Linger => wasmer_vnet::TimeType::Linger, + Sockoption::RecvTimeout => TimeType::ReadTimeout, + Sockoption::SendTimeout => TimeType::WriteTimeout, + Sockoption::ConnectTimeout => TimeType::ConnectTimeout, + Sockoption::AcceptTimeout => TimeType::AcceptTimeout, + Sockoption::Linger => TimeType::Linger, _ => return Errno::Inval, }; @@ -38,14 +38,12 @@ pub fn sock_set_opt_size( &mut ctx, sock, Rights::empty(), - move |mut socket| async move { - match opt { - Sockoption::RecvBufSize => socket.set_recv_buf_size(size as usize).await, - Sockoption::SendBufSize => socket.set_send_buf_size(size as usize).await, - Sockoption::Ttl => socket.set_ttl(size as u32).await, - Sockoption::MulticastTtlV4 => socket.set_multicast_ttl_v4(size as u32).await, - _ => Err(Errno::Inval), - } + |mut socket, _| match opt { + Sockoption::RecvBufSize => socket.set_recv_buf_size(size as usize), + Sockoption::SendBufSize => socket.set_send_buf_size(size as usize), + Sockoption::Ttl => socket.set_ttl(size as u32), + Sockoption::MulticastTtlV4 => socket.set_multicast_ttl_v4(size as u32), + _ => Err(Errno::Inval), } )); Errno::Success diff --git a/lib/wasi/src/syscalls/wasix/sock_set_opt_time.rs b/lib/wasi/src/syscalls/wasix/sock_set_opt_time.rs index 3420e0d5cb9..7f4f9fdb113 100644 --- a/lib/wasi/src/syscalls/wasix/sock_set_opt_time.rs +++ b/lib/wasi/src/syscalls/wasix/sock_set_opt_time.rs @@ -1,5 +1,5 @@ use super::*; -use crate::syscalls::*; +use crate::{net::socket::TimeType, syscalls::*}; /// ### `sock_set_opt_time()` /// Sets one of the times the socket @@ -33,11 +33,11 @@ pub fn sock_set_opt_time( }; let ty = match opt { - Sockoption::RecvTimeout => wasmer_vnet::TimeType::ReadTimeout, - Sockoption::SendTimeout => wasmer_vnet::TimeType::WriteTimeout, - Sockoption::ConnectTimeout => wasmer_vnet::TimeType::ConnectTimeout, - Sockoption::AcceptTimeout => wasmer_vnet::TimeType::AcceptTimeout, - Sockoption::Linger => wasmer_vnet::TimeType::Linger, + Sockoption::RecvTimeout => TimeType::ReadTimeout, + Sockoption::SendTimeout => TimeType::WriteTimeout, + Sockoption::ConnectTimeout => TimeType::ConnectTimeout, + Sockoption::AcceptTimeout => TimeType::AcceptTimeout, + Sockoption::Linger => TimeType::Linger, _ => return Errno::Inval, }; @@ -46,7 +46,7 @@ pub fn sock_set_opt_time( &mut ctx, sock, Rights::empty(), - move |socket| async move { socket.set_opt_time(ty, time).await } + |socket, _| socket.set_opt_time(ty, time) )); Errno::Success } diff --git a/lib/wasi/src/syscalls/wasix/sock_shutdown.rs b/lib/wasi/src/syscalls/wasix/sock_shutdown.rs index 6a52140de5c..658a45ce3bb 100644 --- a/lib/wasi/src/syscalls/wasix/sock_shutdown.rs +++ b/lib/wasi/src/syscalls/wasix/sock_shutdown.rs @@ -28,7 +28,7 @@ pub fn sock_shutdown(mut ctx: FunctionEnvMut<'_, WasiEnv>, sock: WasiFd, how: Sd &mut ctx, sock, Rights::SOCK_SHUTDOWN, - move |mut socket| async move { socket.shutdown(how).await } + |mut socket, _| socket.shutdown(how) )); Errno::Success diff --git a/lib/wasi/src/syscalls/wasix/sock_status.rs b/lib/wasi/src/syscalls/wasix/sock_status.rs index 587e56e39aa..cfdf2d49ab9 100644 --- a/lib/wasi/src/syscalls/wasix/sock_status.rs +++ b/lib/wasi/src/syscalls/wasix/sock_status.rs @@ -19,7 +19,7 @@ pub fn sock_status( &mut ctx, sock, Rights::empty(), - move |socket| async move { socket.status().await } + |socket, _| socket.status() )); use crate::net::socket::WasiSocketStatus; diff --git a/lib/wasi/src/syscalls/wasix/thread_sleep.rs b/lib/wasi/src/syscalls/wasix/thread_sleep.rs index d7d37df69a4..e39502a6cf1 100644 --- a/lib/wasi/src/syscalls/wasix/thread_sleep.rs +++ b/lib/wasi/src/syscalls/wasix/thread_sleep.rs @@ -10,6 +10,13 @@ use crate::syscalls::*; pub fn thread_sleep( mut ctx: FunctionEnvMut<'_, WasiEnv>, duration: Timestamp, +) -> Result { + thread_sleep_internal(ctx, duration) +} + +pub(crate) fn thread_sleep_internal( + mut ctx: FunctionEnvMut<'_, WasiEnv>, + duration: Timestamp, ) -> Result { /* trace!( From b1a94df631b494b25206e109ea2eec7ab8f833f9 Mon Sep 17 00:00:00 2001 From: Syrus Akbary Date: Tue, 14 Feb 2023 07:45:09 -0800 Subject: [PATCH 2/8] bump inkwell to 0.1.1 (#3582) * Revert "Revert "bump inkwell to 0.1.0"" * Updated Inkwell to 0.1.1 --------- Co-authored-by: ptitSeb --- Cargo.lock | 38 +++++++-- lib/compiler-llvm/Cargo.toml | 2 +- lib/compiler-llvm/src/abi/aarch64_systemv.rs | 2 +- lib/compiler-llvm/src/abi/x86_64_systemv.rs | 2 +- lib/compiler-llvm/src/trampoline/wasm.rs | 16 ++-- lib/compiler-llvm/src/translator/code.rs | 6 +- .../src/translator/intrinsics.rs | 81 +++++++++---------- 7 files changed, 83 insertions(+), 64 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 189718b2531..b3257713bf3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1736,24 +1736,23 @@ dependencies = [ [[package]] name = "inkwell" -version = "0.1.0-beta.4" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2223d0eba0ae6d40a3e4680c6a3209143471e1f38b41746ea309aa36dde9f90b" +checksum = "bbac11e485159a525867fb7e6aa61981453e6a72f625fde6a4ab3047b0c6dec9" dependencies = [ "either", "inkwell_internals", "libc", "llvm-sys", "once_cell", - "parking_lot", - "regex", + "parking_lot 0.12.1", ] [[package]] name = "inkwell_internals" -version = "0.5.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c7090af3d300424caa81976b8c97bca41cd70e861272c072e188ae082fb49f9" +checksum = "87d00c17e264ce02be5bc23d7bff959188ec7137beddd06b8b6b05a7c680ea85" dependencies = [ "proc-macro2", "quote", @@ -2334,7 +2333,17 @@ checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" dependencies = [ "instant", "lock_api", - "parking_lot_core", + "parking_lot_core 0.8.6", +] + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core 0.9.6", ] [[package]] @@ -2351,6 +2360,19 @@ dependencies = [ "winapi", ] +[[package]] +name = "parking_lot_core" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba1ef8814b5c993410bb3adfad7a5ed269563e4a2f90c41f5d85be7fb47133bf" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "redox_syscall 0.2.16", + "smallvec", + "windows-sys 0.42.0", +] + [[package]] name = "paste" version = "1.0.11" @@ -3262,7 +3284,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e0bccbcf40c8938196944a3da0e133e031a33f4d6b72db3bda3cc556e361905d" dependencies = [ "lazy_static", - "parking_lot", + "parking_lot 0.11.2", "serial_test_derive", ] diff --git a/lib/compiler-llvm/Cargo.toml b/lib/compiler-llvm/Cargo.toml index c7e36216ea4..35303b7a2b5 100644 --- a/lib/compiler-llvm/Cargo.toml +++ b/lib/compiler-llvm/Cargo.toml @@ -27,7 +27,7 @@ rayon = "1.5" [dependencies.inkwell] package = "inkwell" -version = "=0.1.0-beta.4" +version = "0.1.1" default-features = false features = ["llvm12-0", "target-x86", "target-aarch64"] diff --git a/lib/compiler-llvm/src/abi/aarch64_systemv.rs b/lib/compiler-llvm/src/abi/aarch64_systemv.rs index 8bcb65a008c..89e07a80cec 100644 --- a/lib/compiler-llvm/src/abi/aarch64_systemv.rs +++ b/lib/compiler-llvm/src/abi/aarch64_systemv.rs @@ -207,7 +207,7 @@ impl Abi for Aarch64SystemV { .collect::>()?; let sret = context.struct_type(&basic_types, false); - let sret_ptr = sret.ptr_type(AddressSpace::Generic); + let sret_ptr = sret.ptr_type(AddressSpace::default()); let param_types = std::iter::once(Ok(sret_ptr.as_basic_type_enum())).chain(param_types); diff --git a/lib/compiler-llvm/src/abi/x86_64_systemv.rs b/lib/compiler-llvm/src/abi/x86_64_systemv.rs index 7075193080a..bf02a1f4591 100644 --- a/lib/compiler-llvm/src/abi/x86_64_systemv.rs +++ b/lib/compiler-llvm/src/abi/x86_64_systemv.rs @@ -263,7 +263,7 @@ impl Abi for X86_64SystemV { .collect::>()?; let sret = context.struct_type(&basic_types, false); - let sret_ptr = sret.ptr_type(AddressSpace::Generic); + let sret_ptr = sret.ptr_type(AddressSpace::default()); let param_types = std::iter::once(Ok(sret_ptr.as_basic_type_enum())).chain(param_types); diff --git a/lib/compiler-llvm/src/trampoline/wasm.rs b/lib/compiler-llvm/src/trampoline/wasm.rs index c3166dd2bfe..c0eafe8b8a6 100644 --- a/lib/compiler-llvm/src/trampoline/wasm.rs +++ b/lib/compiler-llvm/src/trampoline/wasm.rs @@ -59,9 +59,9 @@ impl FuncTrampoline { .func_type_to_llvm(&self.ctx, &intrinsics, None, ty)?; let trampoline_ty = intrinsics.void_ty.fn_type( &[ - intrinsics.ctx_ptr_ty.into(), // vmctx ptr - callee_ty.ptr_type(AddressSpace::Generic).into(), // callee function address - intrinsics.i128_ptr_ty.into(), // in/out values ptr + intrinsics.ctx_ptr_ty.into(), // vmctx ptr + callee_ty.ptr_type(AddressSpace::default()).into(), // callee function address + intrinsics.i128_ptr_ty.into(), // in/out values ptr ], false, ); @@ -69,7 +69,7 @@ impl FuncTrampoline { let trampoline_func = module.add_function(name, trampoline_ty, Some(Linkage::External)); trampoline_func .as_global_value() - .set_section(FUNCTION_SECTION); + .set_section(Some(FUNCTION_SECTION)); trampoline_func .as_global_value() .set_linkage(Linkage::DLLExport); @@ -189,7 +189,7 @@ impl FuncTrampoline { } trampoline_func .as_global_value() - .set_section(FUNCTION_SECTION); + .set_section(Some(FUNCTION_SECTION)); trampoline_func .as_global_value() .set_linkage(Linkage::DLLExport); @@ -359,7 +359,7 @@ impl FuncTrampoline { ) }; let ptr = - builder.build_pointer_cast(ptr, v.get_type().ptr_type(AddressSpace::Generic), ""); + builder.build_pointer_cast(ptr, v.get_type().ptr_type(AddressSpace::default()), ""); builder.build_store(ptr, *v); if v.get_type() == intrinsics.i128_ty.as_basic_type_enum() { idx += 1; @@ -424,12 +424,12 @@ impl FuncTrampoline { ], false, ) - .ptr_type(AddressSpace::Generic); + .ptr_type(AddressSpace::default()); let vmctx = self.abi.get_vmctx_ptr_param(&trampoline_func); let callee = builder .build_load( builder - .build_bitcast(vmctx, callee_ty.ptr_type(AddressSpace::Generic), "") + .build_bitcast(vmctx, callee_ty.ptr_type(AddressSpace::default()), "") .into_pointer_value(), "", ) diff --git a/lib/compiler-llvm/src/translator/code.rs b/lib/compiler-llvm/src/translator/code.rs index 8bdd065532c..cd99c53f208 100644 --- a/lib/compiler-llvm/src/translator/code.rs +++ b/lib/compiler-llvm/src/translator/code.rs @@ -106,7 +106,7 @@ impl FuncTranslator { func.add_attribute(AttributeLoc::Function, intrinsics.stack_probe); func.set_personality_function(intrinsics.personality); - func.as_global_value().set_section(FUNCTION_SECTION); + func.as_global_value().set_section(Some(FUNCTION_SECTION)); func.set_linkage(Linkage::DLLExport); func.as_global_value() .set_dll_storage_class(DLLStorageClass::Export); @@ -2334,7 +2334,7 @@ impl<'ctx, 'a> LLVMFunctionCodeGenerator<'ctx, 'a> { // element type. let casted_table_base = self.builder.build_pointer_cast( table_base, - self.intrinsics.funcref_ty.ptr_type(AddressSpace::Generic), + self.intrinsics.funcref_ty.ptr_type(AddressSpace::default()), "casted_table_base", ); @@ -2503,7 +2503,7 @@ impl<'ctx, 'a> LLVMFunctionCodeGenerator<'ctx, 'a> { let typed_func_ptr = self.builder.build_pointer_cast( func_ptr, - llvm_func_type.ptr_type(AddressSpace::Generic), + llvm_func_type.ptr_type(AddressSpace::default()), "typed_func_ptr", ); diff --git a/lib/compiler-llvm/src/translator/intrinsics.rs b/lib/compiler-llvm/src/translator/intrinsics.rs index 028b0a37aab..f25ba036562 100644 --- a/lib/compiler-llvm/src/translator/intrinsics.rs +++ b/lib/compiler-llvm/src/translator/intrinsics.rs @@ -40,8 +40,8 @@ pub fn type_to_llvm_ptr<'ctx>( Type::F32 => Ok(intrinsics.f32_ptr_ty), Type::F64 => Ok(intrinsics.f64_ptr_ty), Type::V128 => Ok(intrinsics.i128_ptr_ty), - Type::FuncRef => Ok(intrinsics.funcref_ty.ptr_type(AddressSpace::Generic)), - Type::ExternRef => Ok(intrinsics.externref_ty.ptr_type(AddressSpace::Generic)), + Type::FuncRef => Ok(intrinsics.funcref_ty.ptr_type(AddressSpace::default())), + Type::ExternRef => Ok(intrinsics.externref_ty.ptr_type(AddressSpace::default())), } } @@ -304,14 +304,14 @@ impl<'ctx> Intrinsics<'ctx> { let f64x2_ty = f64_ty.vec_type(2); let i32x8_ty = i32_ty.vec_type(8); - let i8_ptr_ty = i8_ty.ptr_type(AddressSpace::Generic); - let i16_ptr_ty = i16_ty.ptr_type(AddressSpace::Generic); - let i32_ptr_ty = i32_ty.ptr_type(AddressSpace::Generic); - let i64_ptr_ty = i64_ty.ptr_type(AddressSpace::Generic); - let i128_ptr_ty = i128_ty.ptr_type(AddressSpace::Generic); - let isize_ptr_ty = isize_ty.ptr_type(AddressSpace::Generic); - let f32_ptr_ty = f32_ty.ptr_type(AddressSpace::Generic); - let f64_ptr_ty = f64_ty.ptr_type(AddressSpace::Generic); + let i8_ptr_ty = i8_ty.ptr_type(AddressSpace::default()); + let i16_ptr_ty = i16_ty.ptr_type(AddressSpace::default()); + let i32_ptr_ty = i32_ty.ptr_type(AddressSpace::default()); + let i64_ptr_ty = i64_ty.ptr_type(AddressSpace::default()); + let i128_ptr_ty = i128_ty.ptr_type(AddressSpace::default()); + let isize_ptr_ty = isize_ty.ptr_type(AddressSpace::default()); + let f32_ptr_ty = f32_ty.ptr_type(AddressSpace::default()); + let f64_ptr_ty = f64_ty.ptr_type(AddressSpace::default()); let i1_zero = i1_ty.const_int(0, false); let i8_zero = i8_ty.const_int(0, false); @@ -358,7 +358,7 @@ impl<'ctx> Intrinsics<'ctx> { let md_ty_basic_md: BasicMetadataTypeEnum = md_ty.into(); let ctx_ty = i8_ty; - let ctx_ptr_ty = ctx_ty.ptr_type(AddressSpace::Generic); + let ctx_ptr_ty = ctx_ty.ptr_type(AddressSpace::default()); let ctx_ptr_ty_basic = ctx_ptr_ty.as_basic_type_enum(); let ctx_ptr_ty_basic_md: BasicMetadataTypeEnum = ctx_ptr_ty.into(); @@ -368,7 +368,7 @@ impl<'ctx> Intrinsics<'ctx> { &[i8_ptr_ty_basic, sigindex_ty.into(), ctx_ptr_ty_basic], false, ); - let funcref_ty = anyfunc_ty.ptr_type(AddressSpace::Generic); + let funcref_ty = anyfunc_ty.ptr_type(AddressSpace::default()); let externref_ty = funcref_ty; let anyref_ty = i8_ptr_ty; let anyref_ty_basic_md: BasicMetadataTypeEnum = anyref_ty.into(); @@ -1094,13 +1094,13 @@ impl<'ctx> Intrinsics<'ctx> { vmfunction_import_ptr_ty: context .struct_type(&[i8_ptr_ty_basic, i8_ptr_ty_basic], false) - .ptr_type(AddressSpace::Generic), + .ptr_type(AddressSpace::default()), vmfunction_import_body_element: 0, vmfunction_import_vmctx_element: 1, vmmemory_definition_ptr_ty: context .struct_type(&[i8_ptr_ty_basic, isize_ty.into()], false) - .ptr_type(AddressSpace::Generic), + .ptr_type(AddressSpace::default()), vmmemory_definition_base_element: 0, vmmemory_definition_current_length_element: 1, @@ -1109,19 +1109,19 @@ impl<'ctx> Intrinsics<'ctx> { &[ctx_ptr_ty_basic_md, i32_ty_basic_md, i32_ty_basic_md], false, ) - .ptr_type(AddressSpace::Generic), + .ptr_type(AddressSpace::default()), imported_memory32_grow_ptr_ty: i32_ty .fn_type( &[ctx_ptr_ty_basic_md, i32_ty_basic_md, i32_ty_basic_md], false, ) - .ptr_type(AddressSpace::Generic), + .ptr_type(AddressSpace::default()), memory32_size_ptr_ty: i32_ty .fn_type(&[ctx_ptr_ty_basic_md, i32_ty_basic_md], false) - .ptr_type(AddressSpace::Generic), + .ptr_type(AddressSpace::default()), imported_memory32_size_ptr_ty: i32_ty .fn_type(&[ctx_ptr_ty_basic_md, i32_ty_basic_md], false) - .ptr_type(AddressSpace::Generic), + .ptr_type(AddressSpace::default()), memory32_wait32_ptr_ty: i32_ty .fn_type( &[ @@ -1133,7 +1133,7 @@ impl<'ctx> Intrinsics<'ctx> { ], false, ) - .ptr_type(AddressSpace::Generic), + .ptr_type(AddressSpace::default()), imported_memory32_wait32_ptr_ty: i32_ty .fn_type( &[ @@ -1145,7 +1145,7 @@ impl<'ctx> Intrinsics<'ctx> { ], false, ) - .ptr_type(AddressSpace::Generic), + .ptr_type(AddressSpace::default()), memory32_wait64_ptr_ty: i32_ty .fn_type( &[ @@ -1157,7 +1157,7 @@ impl<'ctx> Intrinsics<'ctx> { ], false, ) - .ptr_type(AddressSpace::Generic), + .ptr_type(AddressSpace::default()), imported_memory32_wait64_ptr_ty: i32_ty .fn_type( &[ @@ -1169,7 +1169,7 @@ impl<'ctx> Intrinsics<'ctx> { ], false, ) - .ptr_type(AddressSpace::Generic), + .ptr_type(AddressSpace::default()), memory32_notify_ptr_ty: i32_ty .fn_type( &[ @@ -1180,7 +1180,7 @@ impl<'ctx> Intrinsics<'ctx> { ], false, ) - .ptr_type(AddressSpace::Generic), + .ptr_type(AddressSpace::default()), imported_memory32_notify_ptr_ty: i32_ty .fn_type( &[ @@ -1191,7 +1191,7 @@ impl<'ctx> Intrinsics<'ctx> { ], false, ) - .ptr_type(AddressSpace::Generic), + .ptr_type(AddressSpace::default()), ctx_ptr_ty, }; @@ -1316,7 +1316,7 @@ impl<'ctx, 'a> CtxType<'ctx, 'a> { let memory_definition_ptr_ptr = cache_builder .build_bitcast( memory_definition_ptr_ptr, - intrinsics.i8_ptr_ty.ptr_type(AddressSpace::Generic), + intrinsics.i8_ptr_ty.ptr_type(AddressSpace::default()), "", ) .into_pointer_value(); @@ -1400,7 +1400,7 @@ impl<'ctx, 'a> CtxType<'ctx, 'a> { let ptr_to_base_ptr = cache_builder .build_bitcast( ptr_to_base_ptr, - intrinsics.i8_ptr_ty.ptr_type(AddressSpace::Generic), + intrinsics.i8_ptr_ty.ptr_type(AddressSpace::default()), "", ) .into_pointer_value(); @@ -1426,7 +1426,7 @@ impl<'ctx, 'a> CtxType<'ctx, 'a> { let definition_ptr_ptr = cache_builder .build_bitcast( definition_ptr_ptr, - intrinsics.i8_ptr_ty.ptr_type(AddressSpace::Generic), + intrinsics.i8_ptr_ty.ptr_type(AddressSpace::default()), "", ) .into_pointer_value(); @@ -1448,7 +1448,7 @@ impl<'ctx, 'a> CtxType<'ctx, 'a> { let ptr_to_base_ptr = cache_builder .build_bitcast( ptr_to_base_ptr, - intrinsics.i8_ptr_ty.ptr_type(AddressSpace::Generic), + intrinsics.i8_ptr_ty.ptr_type(AddressSpace::default()), "", ) .into_pointer_value(); @@ -1570,7 +1570,7 @@ impl<'ctx, 'a> CtxType<'ctx, 'a> { let global_ptr_ptr = cache_builder .build_bitcast( global_ptr_ptr, - intrinsics.i32_ptr_ty.ptr_type(AddressSpace::Generic), + intrinsics.i32_ptr_ty.ptr_type(AddressSpace::default()), "", ) .into_pointer_value(); @@ -1710,7 +1710,11 @@ impl<'ctx, 'a> CtxType<'ctx, 'a> { .unwrap(); let body_ptr = cache_builder.build_load(body_ptr_ptr, ""); let body_ptr = cache_builder - .build_bitcast(body_ptr, llvm_func_type.ptr_type(AddressSpace::Generic), "") + .build_bitcast( + body_ptr, + llvm_func_type.ptr_type(AddressSpace::default()), + "", + ) .into_pointer_value(); let vmctx_ptr_ptr = cache_builder .build_struct_gep( @@ -1760,7 +1764,7 @@ impl<'ctx, 'a> CtxType<'ctx, 'a> { let grow_fn_ptr_ptr = cache_builder .build_bitcast( grow_fn_ptr_ptr, - grow_fn_ty.ptr_type(AddressSpace::Generic), + grow_fn_ty.ptr_type(AddressSpace::default()), "", ) .into_pointer_value(); @@ -1801,7 +1805,7 @@ impl<'ctx, 'a> CtxType<'ctx, 'a> { let size_fn_ptr_ptr = cache_builder .build_bitcast( size_fn_ptr_ptr, - size_fn_ty.ptr_type(AddressSpace::Generic), + size_fn_ty.ptr_type(AddressSpace::default()), "", ) .into_pointer_value(); @@ -1843,7 +1847,7 @@ impl<'ctx, 'a> CtxType<'ctx, 'a> { let size_fn_ptr_ptr = cache_builder .build_bitcast( size_fn_ptr_ptr, - size_fn_ty.ptr_type(AddressSpace::Generic), + size_fn_ty.ptr_type(AddressSpace::default()), "", ) .into_pointer_value(); @@ -1885,7 +1889,7 @@ impl<'ctx, 'a> CtxType<'ctx, 'a> { let size_fn_ptr_ptr = cache_builder .build_bitcast( size_fn_ptr_ptr, - size_fn_ty.ptr_type(AddressSpace::Generic), + size_fn_ty.ptr_type(AddressSpace::default()), "", ) .into_pointer_value(); @@ -1927,7 +1931,7 @@ impl<'ctx, 'a> CtxType<'ctx, 'a> { let size_fn_ptr_ptr = cache_builder .build_bitcast( size_fn_ptr_ptr, - size_fn_ty.ptr_type(AddressSpace::Generic), + size_fn_ty.ptr_type(AddressSpace::default()), "", ) .into_pointer_value(); @@ -1966,13 +1970,6 @@ pub fn tbaa_label<'ctx>( let context = module.get_context(); - // TODO: StoreRef can't return us the lifetime from module through Deref. - // This could be fixed once generic_associated_types is stable. - let context = { - let context2 = &*context; - unsafe { std::mem::transmute::<&Context, &'ctx Context>(context2) } - }; - // `!wasmer_tbaa_root = {}`, the TBAA root node for wasmer. let tbaa_root = module .get_global_metadata("wasmer_tbaa_root") From c12a60f9a78b65c2d8f075d21cff3cd7ac4a5428 Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Wed, 15 Feb 2023 16:13:48 +1100 Subject: [PATCH 3/8] Many bug fixes and performance optimizations - Removed generation_arena which was causing some serious leakages of files and sockets - Added OsError for NetworkErrors so that "Too Many Open Files" is properly passed - Local networking will now cap at 10 sockets in the backlog - Added the missing shutdown error code - Removed the inodes lock around most of the WASI syscalls - Fixed some race conditions in the event notifications for WASI - The polling loop will now only notify a closed socket once - Event notifications now uses Wakers rather than MPSC - Some socket errors now return the right codes which prevents panics in WASM - Fixed a bug where the file read and write guards might release the file before the lock - The inode seed is now much safer preventing overlaps - The fd seed is now much safer preventing overlaps - Closing files is now implicit rather than explicit reducing possibliities for error - Forking of file descriptors is now much simplier - Polling events will now be returned in random order to prevent some race conditions - Removed a number of memory allocations which were wasting memory and performance - Sockets now only copy the send and recv data once rather than multiple times --- Cargo.lock | 61 +- lib/vnet/Cargo.toml | 1 + lib/vnet/src/lib.rs | 13 +- lib/wasi-experimental-io-devices/src/lib.rs | 2 +- .../src/link-ext.rs | 2 +- lib/wasi-local-networking/src/lib.rs | 6 + lib/wasi-types/schema/wasi/typenames.wit | 2 + lib/wasi-types/src/wasi/bindings.rs | 4 + lib/wasi/Cargo.toml | 3 +- lib/wasi/src/fs/fd.rs | 48 +- lib/wasi/src/fs/inode_guard.rs | 499 +++++++-------- lib/wasi/src/fs/mod.rs | 590 ++++++++---------- lib/wasi/src/macros.rs | 34 + lib/wasi/src/net/mod.rs | 36 ++ lib/wasi/src/net/socket.rs | 154 ++--- .../src/os/command/builtins/cmd_wasmer.rs | 2 +- lib/wasi/src/state/builder.rs | 35 +- lib/wasi/src/state/env.rs | 30 +- lib/wasi/src/state/mod.rs | 8 +- lib/wasi/src/syscalls/mod.rs | 68 +- lib/wasi/src/syscalls/wasi/fd_allocate.rs | 6 +- lib/wasi/src/syscalls/wasi/fd_close.rs | 33 +- lib/wasi/src/syscalls/wasi/fd_datasync.rs | 8 +- lib/wasi/src/syscalls/wasi/fd_event.rs | 14 +- lib/wasi/src/syscalls/wasi/fd_fdstat_get.rs | 2 +- .../src/syscalls/wasi/fd_fdstat_set_flags.rs | 2 +- lib/wasi/src/syscalls/wasi/fd_filestat_get.rs | 2 +- .../src/syscalls/wasi/fd_filestat_set_size.rs | 6 +- .../syscalls/wasi/fd_filestat_set_times.rs | 5 +- .../src/syscalls/wasi/fd_prestat_dir_name.rs | 17 +- lib/wasi/src/syscalls/wasi/fd_prestat_get.rs | 12 +- lib/wasi/src/syscalls/wasi/fd_read.rs | 318 ++++++---- lib/wasi/src/syscalls/wasi/fd_readdir.rs | 31 +- lib/wasi/src/syscalls/wasi/fd_renumber.rs | 11 +- lib/wasi/src/syscalls/wasi/fd_seek.rs | 6 +- lib/wasi/src/syscalls/wasi/fd_sync.rs | 10 +- lib/wasi/src/syscalls/wasi/fd_write.rs | 26 +- .../syscalls/wasi/path_create_directory.rs | 30 +- .../src/syscalls/wasi/path_filestat_get.rs | 14 +- .../syscalls/wasi/path_filestat_set_times.rs | 14 +- lib/wasi/src/syscalls/wasi/path_link.rs | 22 +- lib/wasi/src/syscalls/wasi/path_open.rs | 25 +- lib/wasi/src/syscalls/wasi/path_readlink.rs | 8 +- .../syscalls/wasi/path_remove_directory.rs | 16 +- lib/wasi/src/syscalls/wasi/path_rename.rs | 32 +- lib/wasi/src/syscalls/wasi/path_symlink.rs | 24 +- .../src/syscalls/wasi/path_unlink_file.rs | 39 +- lib/wasi/src/syscalls/wasi/poll_oneoff.rs | 60 +- lib/wasi/src/syscalls/wasix/bus_poll.rs | 4 +- lib/wasi/src/syscalls/wasix/fd_pipe.rs | 6 +- lib/wasi/src/syscalls/wasix/getcwd.rs | 6 +- lib/wasi/src/syscalls/wasix/proc_exec.rs | 8 +- lib/wasi/src/syscalls/wasix/proc_spawn.rs | 12 +- lib/wasi/src/syscalls/wasix/sock_accept.rs | 9 +- lib/wasi/src/syscalls/wasix/sock_open.rs | 12 +- lib/wasi/src/syscalls/wasix/sock_recv.rs | 113 +++- lib/wasi/src/syscalls/wasix/sock_send.rs | 60 +- lib/wasi/src/syscalls/wasix/sock_send_file.rs | 16 +- lib/wasi/src/syscalls/wasix/thread_spawn.rs | 36 +- lib/wasi/src/syscalls/wasix/ws_connect.rs | 9 +- 60 files changed, 1349 insertions(+), 1333 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 67cbc7510b5..d0ff7be7c42 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -125,6 +125,15 @@ dependencies = [ "syn", ] +[[package]] +name = "atomic-polyfill" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3ff7eb3f316534d83a8a2c3d1674ace8a5a71198eba31e2e2b597833f699b28" +dependencies = [ + "critical-section", +] + [[package]] name = "atty" version = "0.2.14" @@ -732,6 +741,12 @@ dependencies = [ "itertools", ] +[[package]] +name = "critical-section" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6548a0ad5d2549e111e1f6a11a6c2e2d00ce6a3dafe22948d67c2b443f775e52" + [[package]] name = "crossbeam-channel" version = "0.5.6" @@ -1332,16 +1347,6 @@ dependencies = [ "byteorder", ] -[[package]] -name = "generational-arena" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e1d3b771574f62d0548cee0ad9057857e9fc25d7a3335f140c84f6acd0bf601" -dependencies = [ - "cfg-if 0.1.10", - "serde", -] - [[package]] name = "generic-array" version = "0.14.6" @@ -1511,6 +1516,15 @@ version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" +[[package]] +name = "hash32" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67" +dependencies = [ + "byteorder", +] + [[package]] name = "hashbrown" version = "0.11.2" @@ -1538,6 +1552,19 @@ dependencies = [ "hashbrown 0.12.3", ] +[[package]] +name = "heapless" +version = "0.7.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db04bc24a18b9ea980628ecf00e6c0264f3c1426dac36c00cb49b6fbad8b0743" +dependencies = [ + "atomic-polyfill", + "hash32", + "rustc_version 0.4.0", + "spin 0.9.5", + "stable_deref_trait", +] + [[package]] name = "heck" version = "0.3.3" @@ -2985,7 +3012,7 @@ dependencies = [ "cc", "libc", "once_cell", - "spin", + "spin 0.5.2", "untrusted", "web-sys", "winapi", @@ -3506,6 +3533,15 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" +[[package]] +name = "spin" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dccf47db1b41fa1573ed27ccf5e08e3ca771cb994f776668c5ebda893b248fc" +dependencies = [ + "lock_api", +] + [[package]] name = "spinoff" version = "0.5.4" @@ -5135,6 +5171,7 @@ dependencies = [ "async-trait", "bytes", "thiserror", + "tracing", "wasmer-vfs", ] @@ -5151,8 +5188,8 @@ dependencies = [ "cooked-waker", "derivative", "futures", - "generational-arena", "getrandom", + "heapless", "hex", "http", "lazy_static", diff --git a/lib/vnet/Cargo.toml b/lib/vnet/Cargo.toml index 77c18313ca9..1694217da2f 100644 --- a/lib/vnet/Cargo.toml +++ b/lib/vnet/Cargo.toml @@ -11,6 +11,7 @@ thiserror = "1" wasmer-vfs = { path = "../vfs", version = "=3.2.0-alpha.1", default-features = false } bytes = "1" async-trait = { version = "^0.1" } +tracing = "0.1" [features] default = [] diff --git a/lib/vnet/src/lib.rs b/lib/vnet/src/lib.rs index 1ffc70c6df6..3cd22ec377c 100644 --- a/lib/vnet/src/lib.rs +++ b/lib/vnet/src/lib.rs @@ -554,6 +554,9 @@ pub enum NetworkError { /// A call to write returned 0 #[error("write returned 0")] WriteZero, + /// OS error + #[error("operating system error({0})")] + OsError(i32), /// The operation is not supported. #[error("unsupported")] Unsupported, @@ -563,6 +566,7 @@ pub enum NetworkError { } pub fn net_error_into_io_err(net_error: NetworkError) -> std::io::Error { + use std::io::Error; use std::io::ErrorKind; match net_error { NetworkError::InvalidFd => ErrorKind::BrokenPipe.into(), @@ -585,6 +589,7 @@ pub fn net_error_into_io_err(net_error: NetworkError) -> std::io::Error { NetworkError::UnexpectedEof => ErrorKind::UnexpectedEof.into(), NetworkError::WouldBlock => ErrorKind::WouldBlock.into(), NetworkError::WriteZero => ErrorKind::WriteZero.into(), + NetworkError::OsError(code) => Error::from_raw_os_error(code), NetworkError::Unsupported => ErrorKind::Unsupported.into(), NetworkError::UnknownError => ErrorKind::BrokenPipe.into(), } @@ -610,6 +615,12 @@ pub fn io_err_into_net_error(net_error: std::io::Error) -> NetworkError { ErrorKind::WouldBlock => NetworkError::WouldBlock, ErrorKind::WriteZero => NetworkError::WriteZero, ErrorKind::Unsupported => NetworkError::Unsupported, - _ => NetworkError::UnknownError, + _ => { + if let Some(code) = net_error.raw_os_error() { + NetworkError::OsError(code) + } else { + NetworkError::UnknownError + } + } } } diff --git a/lib/wasi-experimental-io-devices/src/lib.rs b/lib/wasi-experimental-io-devices/src/lib.rs index 95708ab69ee..139fdbba618 100644 --- a/lib/wasi-experimental-io-devices/src/lib.rs +++ b/lib/wasi-experimental-io-devices/src/lib.rs @@ -11,6 +11,6 @@ use wasmer_wasi::os::fs::WasiFs; use wasmer_wasi::os::fs::WasiInodes; #[cfg(not(feature = "link_external_libs"))] -pub fn initialize(_: &mut WasiInodes, _: &mut WasiFs) -> Result<(), String> { +pub fn initialize(_: &WasiInodes, _: &mut WasiFs) -> Result<(), String> { Err("wasi-experimental-io-devices has to be compiled with --features=\"link_external_libs\" (not enabled by default) for graphics I/O to work".to_string()) } diff --git a/lib/wasi-experimental-io-devices/src/link-ext.rs b/lib/wasi-experimental-io-devices/src/link-ext.rs index e123e9d0091..dec7d5959a4 100644 --- a/lib/wasi-experimental-io-devices/src/link-ext.rs +++ b/lib/wasi-experimental-io-devices/src/link-ext.rs @@ -430,7 +430,7 @@ impl VirtualFile for FrameBuffer { } } -pub fn initialize(inodes: &mut WasiInodes, fs: &mut WasiFs) -> Result<(), String> { +pub fn initialize(inodes: &WasiInodes, fs: &mut WasiFs) -> Result<(), String> { let frame_buffer_file = Box::new(FrameBuffer { fb_type: FrameBufferFileType::Buffer, cursor: 0, diff --git a/lib/wasi-local-networking/src/lib.rs b/lib/wasi-local-networking/src/lib.rs index 86f7284fe57..5d6eb42bff8 100644 --- a/lib/wasi-local-networking/src/lib.rs +++ b/lib/wasi-local-networking/src/lib.rs @@ -140,6 +140,12 @@ impl VirtualTcpListener for LocalTcpListener { &mut self, cx: &mut std::task::Context<'_>, ) -> std::task::Poll> { + { + let backlog = self.backlog.lock().unwrap(); + if backlog.len() > 10 { + return Poll::Ready(Ok(backlog.len())); + } + } self.stream .poll_accept(cx) .map_err(io_err_into_net_error) diff --git a/lib/wasi-types/schema/wasi/typenames.wit b/lib/wasi-types/schema/wasi/typenames.wit index b0af708415d..e85354b73fe 100644 --- a/lib/wasi-types/schema/wasi/typenames.wit +++ b/lib/wasi-types/schema/wasi/typenames.wit @@ -245,6 +245,8 @@ enum errno { xdev, /// Extension: Capabilities insufficient. notcapable, + /// Cannot send after socket shutdown. + shutdown, } enum bus-errno { diff --git a/lib/wasi-types/src/wasi/bindings.rs b/lib/wasi-types/src/wasi/bindings.rs index 56b738ff949..9dc9f012bd4 100644 --- a/lib/wasi-types/src/wasi/bindings.rs +++ b/lib/wasi-types/src/wasi/bindings.rs @@ -246,6 +246,8 @@ pub enum Errno { Xdev, #[doc = " Extension: Capabilities insufficient."] Notcapable, + #[doc = " Cannot send after socket shutdown."] + Shutdown, } impl Errno { pub fn name(&self) -> &'static str { @@ -327,6 +329,7 @@ impl Errno { Errno::Txtbsy => "txtbsy", Errno::Xdev => "xdev", Errno::Notcapable => "notcapable", + Errno::Shutdown => "shutdown", } } pub fn message(&self) -> &'static str { @@ -408,6 +411,7 @@ impl Errno { Errno::Txtbsy => "Text file busy.", Errno::Xdev => "Cross-device link.", Errno::Notcapable => "Extension: Capabilities insufficient.", + Errno::Shutdown => "Cannot send after socket shutdown.", } } } diff --git a/lib/wasi/Cargo.toml b/lib/wasi/Cargo.toml index cf77e83a502..927e349d495 100644 --- a/lib/wasi/Cargo.toml +++ b/lib/wasi/Cargo.toml @@ -13,7 +13,6 @@ edition = "2018" [dependencies] cfg-if = "1.0" thiserror = "1" -generational-arena = { version = "0.2" } tracing = "0.1" getrandom = "0.2" wasmer-wasi-types = { path = "../wasi-types", version = "=3.2.0-alpha.1" } @@ -61,6 +60,7 @@ wasmer-compiler-singlepass = { version = "=3.2.0-alpha.1", path = "../compiler-s wasmer-compiler = { version = "=3.2.0-alpha.1", path = "../compiler", features = [ "translator" ], optional = true } http = "0.2.8" wai-bindgen-wasmer = { path = "../wai-bindgen-wasmer", version = "0.2.3", features = ["tracing"] } +heapless = "0.7.16" [dependencies.reqwest] version = "0.11" @@ -125,6 +125,5 @@ disable-all-logging = [ enable-serde = [ "typetag", "wasmer-vfs/enable-serde", - "generational-arena/serde", "wasmer-wasi-types/enable-serde", ] diff --git a/lib/wasi/src/fs/fd.rs b/lib/wasi/src/fs/fd.rs index 018767a64e7..1376bd4f312 100644 --- a/lib/wasi/src/fs/fd.rs +++ b/lib/wasi/src/fs/fd.rs @@ -2,13 +2,10 @@ use std::{ borrow::Cow, collections::{HashMap, VecDeque}, path::PathBuf, - sync::{ - atomic::{AtomicBool, AtomicU32, AtomicU64}, - Arc, Mutex, RwLock, RwLockReadGuard, RwLockWriteGuard, - }, + sync::{atomic::AtomicU64, Arc, Mutex, RwLock, RwLockReadGuard, RwLockWriteGuard}, + task::Waker, }; -use generational_arena::Index as Inode; #[cfg(feature = "enable-serde")] use serde_derive::{Deserialize, Serialize}; use wasmer_vfs::{VirtualFile, WasiPipe}; @@ -16,12 +13,11 @@ use wasmer_wasi_types::wasi::{Fd as WasiFd, Fdflags, Filestat, Rights}; use crate::net::socket::InodeSocket; +use super::{InodeGuard, InodeWeakGuard}; + #[derive(Debug, Clone)] #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] pub struct Fd { - /// The reference count is only increased when the FD is - /// duplicates - fd_close will not kill the inode until this reaches zero - pub ref_cnt: Arc, pub rights: Rights, pub rights_inheriting: Rights, pub flags: Fdflags, @@ -30,7 +26,7 @@ pub struct Fd { /// /// Used when reopening a [`VirtualFile`] during [`WasiState`] deserialization. pub open_flags: u16, - pub inode: Inode, + pub inode: InodeGuard, pub is_stdio: bool, } @@ -74,6 +70,21 @@ impl InodeVal { } } +#[derive(Debug)] +#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] +pub struct NotificationInner { + /// Used for event notifications by the user application or operating system + /// (positive number means there are events waiting to be processed) + pub counter: AtomicU64, + /// Counter used to prevent duplicate notification events + pub last_poll: AtomicU64, + /// Flag that indicates if this is operating + pub is_semaphore: bool, + /// Receiver that wakes sleeping threads + #[cfg_attr(feature = "enable-serde", serde(skip))] + pub wakers: Mutex>, +} + /// The core of the filesystem abstraction. Includes directories, /// files, and symlinks. #[derive(Debug)] @@ -104,19 +115,19 @@ pub enum Kind { }, Dir { /// Parent directory - parent: Option, + parent: InodeWeakGuard, /// The path on the host system where the directory is located // TODO: wrap it like VirtualFile path: PathBuf, /// The entries of a directory are lazily filled. - entries: HashMap, + entries: HashMap, }, /// The same as Dir but without the irrelevant bits /// The root is immutable after creation; generally the Kind::Root /// branch of whatever code you're writing will be a simpler version of /// your Kind::Dir logic Root { - entries: HashMap, + entries: HashMap, }, /// The first two fields are data _about_ the symlink /// the last field is the data _inside_ the symlink @@ -135,16 +146,5 @@ pub enum Kind { Buffer { buffer: Vec, }, - EventNotifications { - /// Used for event notifications by the user application or operating system - /// (positive number means there are events waiting to be processed) - counter: Arc, - /// Flag that indicates if this is operating - is_semaphore: bool, - /// Receiver that wakes sleeping threads - #[cfg_attr(feature = "enable-serde", serde(skip))] - wakers: Arc>>>, - /// Immediate waker - immediate: Arc, - }, + EventNotifications(Arc), } diff --git a/lib/wasi/src/fs/inode_guard.rs b/lib/wasi/src/fs/inode_guard.rs index 8f1668f315e..84019a90362 100644 --- a/lib/wasi/src/fs/inode_guard.rs +++ b/lib/wasi/src/fs/inode_guard.rs @@ -3,17 +3,11 @@ use std::{ io::{IoSlice, SeekFrom}, ops::{Deref, DerefMut}, pin::Pin, - sync::{ - atomic::{AtomicU64, Ordering}, - Arc, Mutex, RwLock, RwLockReadGuard, RwLockWriteGuard, - }, + sync::{atomic::Ordering, Arc, RwLock, RwLockReadGuard, RwLockWriteGuard}, task::{Context, Poll}, }; -use tokio::{ - io::{AsyncRead, AsyncSeek, AsyncWrite}, - sync::mpsc, -}; +use tokio::io::{AsyncRead, AsyncSeek, AsyncWrite}; use wasmer_vfs::{FsError, VirtualFile}; use wasmer_vnet::{net_error_into_io_err, NetworkError}; use wasmer_wasi_types::{ @@ -22,24 +16,18 @@ use wasmer_wasi_types::{ wasi::{Errno, Event, EventFdReadwrite, EventUnion, Eventrwflags, Subscription}, }; -use super::Kind; +use super::{fd::NotificationInner, InodeGuard, Kind}; use crate::{ net::socket::{InodeSocketInner, InodeSocketKind}, state::{iterate_poll_events, PollEvent, PollEventSet}, syscalls::map_io_err, - WasiInodes, WasiState, + WasiState, }; pub(crate) enum InodeValFilePollGuardMode { File(Arc>>), - EventNotifications { - immediate: bool, - waker: Mutex>, - counter: Arc, - }, - Socket { - inner: Arc>, - }, + EventNotifications(Arc), + Socket { inner: Arc }, } pub(crate) struct InodeValFilePollGuard { @@ -57,25 +45,8 @@ impl InodeValFilePollGuard { guard: &Kind, ) -> Option { let mode = match guard.deref() { - Kind::EventNotifications { - counter, - wakers, - immediate, - .. - } => { - let (tx, rx) = tokio::sync::mpsc::unbounded_channel(); - let immediate = { - let mut wakers = wakers.lock().unwrap(); - wakers.push_back(tx); - immediate - .compare_exchange(true, false, Ordering::AcqRel, Ordering::Relaxed) - .is_ok() - }; - InodeValFilePollGuardMode::EventNotifications { - immediate, - waker: Mutex::new(rx), - counter: counter.clone(), - } + Kind::EventNotifications(inner) => { + InodeValFilePollGuardMode::EventNotifications(inner.clone()) } Kind::Socket { socket } => InodeValFilePollGuardMode::Socket { inner: socket.inner.clone(), @@ -105,7 +76,7 @@ impl std::fmt::Debug for InodeValFilePollGuard { write!(f, "guard-notifications") } InodeValFilePollGuardMode::Socket { inner } => { - let inner = inner.read().unwrap(); + let inner = inner.protected.read().unwrap(); match inner.kind { InodeSocketKind::TcpListener { .. } => write!(f, "guard-tcp-listener"), InodeSocketKind::TcpStream { ref socket, .. } => { @@ -125,20 +96,6 @@ impl std::fmt::Debug for InodeValFilePollGuard { } } -impl InodeValFilePollGuard { - #[allow(dead_code)] - pub fn is_open(&self) -> bool { - match &self.mode { - InodeValFilePollGuardMode::File(file) => { - let guard = file.read().unwrap(); - guard.is_open() - } - InodeValFilePollGuardMode::EventNotifications { .. } - | InodeValFilePollGuardMode::Socket { .. } => true, - } - } -} - pub(crate) struct InodeValFilePollGuardJoin<'a> { mode: &'a mut InodeValFilePollGuardMode, fd: u32, @@ -161,14 +118,17 @@ impl<'a> InodeValFilePollGuardJoin<'a> { } impl<'a> Future for InodeValFilePollGuardJoin<'a> { - type Output = Event; + type Output = heapless::Vec; fn poll(mut self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll { + let fd = self.fd(); + let waker = cx.waker(); let mut has_read = false; let mut has_write = false; let mut has_close = false; let mut has_hangup = false; + let mut ret = heapless::Vec::new(); for in_event in iterate_poll_events(self.peb) { match in_event { PollEvent::PollIn => { @@ -197,10 +157,8 @@ impl<'a> Future for InodeValFilePollGuardJoin<'a> { } InodeValFilePollGuardMode::EventNotifications { .. } => false, InodeValFilePollGuardMode::Socket { ref inner } => { - let mut guard = inner.write().unwrap(); - let is_closed = if let InodeSocketKind::Closed = guard.kind { - true - } else if has_read || has_write { + let mut guard = inner.protected.write().unwrap(); + let is_closed = if has_read || has_write { // this will be handled in the read/write poll instead false } else { @@ -217,11 +175,15 @@ impl<'a> Future for InodeValFilePollGuardJoin<'a> { _ => false, } }; - is_closed + if is_closed { + std::mem::replace(&mut guard.notifications.closed, true) == false + } else { + false + } } }; if is_closed { - return Poll::Ready(Event { + ret.push(Event { userdata: self.subscription.userdata, error: Errno::Success, type_: self.subscription.type_, @@ -238,192 +200,211 @@ impl<'a> Future for InodeValFilePollGuardJoin<'a> { }, Eventtype::Clock => EventUnion { clock: 0 }, }, - }); + }) + .ok(); } } if has_read { - let mut poll_result = match &mut self.mode { + let poll_result = match &mut self.mode { InodeValFilePollGuardMode::File(file) => { let mut guard = file.write().unwrap(); let file = Pin::new(guard.as_mut()); file.poll_read_ready(cx) } - InodeValFilePollGuardMode::EventNotifications { - waker, - counter, - immediate, - .. - } => { - if *immediate { - let cnt = counter.load(Ordering::Acquire); - Poll::Ready(Ok(cnt as usize)) + InodeValFilePollGuardMode::EventNotifications(inner) => { + { + let mut guard = inner.wakers.lock().unwrap(); + if guard.iter().any(|a| a.will_wake(waker)) == false { + guard.push_front(waker.clone()); + } + } + let val = inner.counter.load(Ordering::Acquire); + if inner.last_poll.swap(val, Ordering::AcqRel) != val { + Poll::Ready(Ok(val as usize)) } else { - let counter = counter.clone(); - let mut waker = waker.lock().unwrap(); - let mut notifications = Pin::new(waker.deref_mut()); - notifications.poll_recv(cx).map(|_| { - let cnt = counter.load(Ordering::Acquire); - Ok(cnt as usize) - }) + Poll::Pending } } InodeValFilePollGuardMode::Socket { ref inner } => { - let mut guard = inner.write().unwrap(); - guard.poll_read_ready(cx).map_err(net_error_into_io_err) + let mut guard = inner.protected.write().unwrap(); + let res = guard.poll_read_ready(cx).map_err(net_error_into_io_err); + match res { + Poll::Ready(Err(err)) if is_err_closed(&err) => { + tracing::trace!("socket read ready error (fd={}) - {}", fd, err); + if std::mem::replace(&mut guard.notifications.closed, true) == false { + Poll::Ready(Ok(0)) + } else { + Poll::Pending + } + } + Poll::Ready(Err(err)) => { + tracing::debug!("poll socket error - {}", err); + if std::mem::replace(&mut guard.notifications.failed, true) == false { + Poll::Ready(Ok(0)) + } else { + Poll::Pending + } + } + res => res, + } } }; - if has_close { - poll_result = match poll_result { - Poll::Ready(Err(err)) - if err.kind() == std::io::ErrorKind::ConnectionAborted - || err.kind() == std::io::ErrorKind::ConnectionRefused - || err.kind() == std::io::ErrorKind::ConnectionReset - || err.kind() == std::io::ErrorKind::BrokenPipe - || err.kind() == std::io::ErrorKind::NotConnected - || err.kind() == std::io::ErrorKind::UnexpectedEof => - { - return Poll::Ready(Event { - userdata: self.subscription.userdata, - error: Errno::Success, - type_: self.subscription.type_, - u: match self.subscription.type_ { - Eventtype::FdRead | Eventtype::FdWrite => EventUnion { - fd_readwrite: EventFdReadwrite { - nbytes: 0, - flags: if has_hangup { - Eventrwflags::FD_READWRITE_HANGUP - } else { - Eventrwflags::empty() - }, + match poll_result { + Poll::Ready(Err(err)) if has_close && is_err_closed(&err) => { + ret.push(Event { + userdata: self.subscription.userdata, + error: Errno::Success, + type_: self.subscription.type_, + u: match self.subscription.type_ { + Eventtype::FdRead | Eventtype::FdWrite => EventUnion { + fd_readwrite: EventFdReadwrite { + nbytes: 0, + flags: if has_hangup { + Eventrwflags::FD_READWRITE_HANGUP + } else { + Eventrwflags::empty() }, }, - Eventtype::Clock => EventUnion { clock: 0 }, }, - }); - } - a => a, - }; - } - if let Poll::Ready(bytes_available) = poll_result { - let mut error = Errno::Success; - let bytes_available = match bytes_available { - Ok(a) => a, - Err(e) => { - error = map_io_err(e); - 0 - } - }; - return Poll::Ready(Event { - userdata: self.subscription.userdata, - error, - type_: self.subscription.type_, - u: match self.subscription.type_ { - Eventtype::FdRead | Eventtype::FdWrite => EventUnion { - fd_readwrite: EventFdReadwrite { - nbytes: bytes_available as u64, - flags: if bytes_available == 0 { - Eventrwflags::FD_READWRITE_HANGUP - } else { - Eventrwflags::empty() + Eventtype::Clock => EventUnion { clock: 0 }, + }, + }) + .ok(); + } + Poll::Ready(bytes_available) => { + let mut error = Errno::Success; + let bytes_available = match bytes_available { + Ok(a) => a, + Err(e) => { + error = map_io_err(e); + 0 + } + }; + ret.push(Event { + userdata: self.subscription.userdata, + error, + type_: self.subscription.type_, + u: match self.subscription.type_ { + Eventtype::FdRead | Eventtype::FdWrite => EventUnion { + fd_readwrite: EventFdReadwrite { + nbytes: bytes_available as u64, + flags: if bytes_available == 0 { + Eventrwflags::FD_READWRITE_HANGUP + } else { + Eventrwflags::empty() + }, }, }, + Eventtype::Clock => EventUnion { clock: 0 }, }, - Eventtype::Clock => EventUnion { clock: 0 }, - }, - }); - } + }) + .ok(); + } + Poll::Pending => {} + }; } if has_write { - let mut poll_result = match &mut self.mode { + let poll_result = match &mut self.mode { InodeValFilePollGuardMode::File(file) => { let mut guard = file.write().unwrap(); let file = Pin::new(guard.as_mut()); file.poll_write_ready(cx) } - InodeValFilePollGuardMode::EventNotifications { - waker, - counter, - immediate, - .. - } => { - if *immediate { - let cnt = counter.load(Ordering::Acquire); - Poll::Ready(Ok(cnt as usize)) + InodeValFilePollGuardMode::EventNotifications(inner) => { + { + let mut guard = inner.wakers.lock().unwrap(); + if guard.iter().any(|a| a.will_wake(waker)) == false { + guard.push_front(waker.clone()); + } + } + let val = inner.counter.load(Ordering::Acquire); + if inner.last_poll.swap(val, Ordering::AcqRel) != val { + Poll::Ready(Ok(val as usize)) } else { - let counter = counter.clone(); - let mut waker = waker.lock().unwrap(); - let mut notifications = Pin::new(waker.deref_mut()); - notifications.poll_recv(cx).map(|_| { - let cnt = counter.load(Ordering::Acquire); - Ok(cnt as usize) - }) + Poll::Pending } } InodeValFilePollGuardMode::Socket { ref inner } => { - let mut guard = inner.write().unwrap(); - guard.poll_write_ready(cx).map_err(net_error_into_io_err) + let mut guard = inner.protected.write().unwrap(); + 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); + if std::mem::replace(&mut guard.notifications.closed, true) == false { + Poll::Ready(Ok(0)) + } else { + Poll::Pending + } + } + Poll::Ready(Err(err)) => { + tracing::debug!("poll socket error - {}", err); + if std::mem::replace(&mut guard.notifications.failed, true) == false { + Poll::Ready(Ok(0)) + } else { + Poll::Pending + } + } + res => res, + } } }; - if has_close { - poll_result = match poll_result { - Poll::Ready(Err(err)) - if err.kind() == std::io::ErrorKind::ConnectionAborted - || err.kind() == std::io::ErrorKind::ConnectionRefused - || err.kind() == std::io::ErrorKind::ConnectionReset - || err.kind() == std::io::ErrorKind::BrokenPipe - || err.kind() == std::io::ErrorKind::NotConnected - || err.kind() == std::io::ErrorKind::UnexpectedEof => - { - return Poll::Ready(Event { - userdata: self.subscription.userdata, - error: Errno::Success, - type_: self.subscription.type_, - u: match self.subscription.type_ { - Eventtype::FdRead | Eventtype::FdWrite => EventUnion { - fd_readwrite: EventFdReadwrite { - nbytes: 0, - flags: if has_hangup { - Eventrwflags::FD_READWRITE_HANGUP - } else { - Eventrwflags::empty() - }, + match poll_result { + Poll::Ready(Err(err)) if has_close && is_err_closed(&err) => { + ret.push(Event { + userdata: self.subscription.userdata, + error: Errno::Success, + type_: self.subscription.type_, + u: match self.subscription.type_ { + Eventtype::FdRead | Eventtype::FdWrite => EventUnion { + fd_readwrite: EventFdReadwrite { + nbytes: 0, + flags: if has_hangup { + Eventrwflags::FD_READWRITE_HANGUP + } else { + Eventrwflags::empty() }, }, - Eventtype::Clock => EventUnion { clock: 0 }, }, - }); - } - a => a, - }; - } - if let Poll::Ready(bytes_available) = poll_result { - let mut error = Errno::Success; - let bytes_available = match bytes_available { - Ok(a) => a, - Err(e) => { - error = map_io_err(e); - 0 - } - }; - return Poll::Ready(Event { - userdata: self.subscription.userdata, - error, - type_: self.subscription.type_, - u: match self.subscription.type_ { - Eventtype::FdRead | Eventtype::FdWrite => EventUnion { - fd_readwrite: EventFdReadwrite { - nbytes: bytes_available as u64, - flags: if bytes_available == 0 { - Eventrwflags::FD_READWRITE_HANGUP - } else { - Eventrwflags::empty() + Eventtype::Clock => EventUnion { clock: 0 }, + }, + }) + .ok(); + } + Poll::Ready(bytes_available) => { + let mut error = Errno::Success; + let bytes_available = match bytes_available { + Ok(a) => a, + Err(e) => { + error = map_io_err(e); + 0 + } + }; + ret.push(Event { + userdata: self.subscription.userdata, + error, + type_: self.subscription.type_, + u: match self.subscription.type_ { + Eventtype::FdRead | Eventtype::FdWrite => EventUnion { + fd_readwrite: EventFdReadwrite { + nbytes: bytes_available as u64, + flags: if bytes_available == 0 { + Eventrwflags::FD_READWRITE_HANGUP + } else { + Eventrwflags::empty() + }, }, }, + Eventtype::Clock => EventUnion { clock: 0 }, }, - Eventtype::Clock => EventUnion { clock: 0 }, - }, - }); - } + }) + .ok(); + } + Poll::Pending => {} + }; + } + + if ret.len() > 0 { + return Poll::Ready(ret); } Poll::Pending } @@ -431,16 +412,18 @@ impl<'a> Future for InodeValFilePollGuardJoin<'a> { #[derive(Debug)] pub(crate) struct InodeValFileReadGuard { - #[allow(dead_code)] - file: Arc>>, guard: RwLockReadGuard<'static, Box>, + // we must keep a reference as the lifetime of the guard becomes owned using unsafe code + // (warning!! The fields of a struct are dropped in declaration order thus this must be at the end) + // (https://doc.rust-lang.org/reference/destructors.html#:~:text=The%20fields%20of%20a%20struct,first%20element%20to%20the%20last.) + _file: Arc>>, } impl InodeValFileReadGuard { pub(crate) fn new(file: &Arc>>) -> Self { let guard = file.read().unwrap(); Self { - file: file.clone(), + _file: file.clone(), guard: unsafe { std::mem::transmute(guard) }, } } @@ -457,7 +440,7 @@ impl InodeValFileReadGuard { fd, peb, subscription, - mode: InodeValFilePollGuardMode::File(self.file), + mode: InodeValFilePollGuardMode::File(self._file), } } } @@ -471,16 +454,18 @@ impl Deref for InodeValFileReadGuard { #[derive(Debug)] pub struct InodeValFileWriteGuard { - #[allow(dead_code)] - file: Arc>>, guard: RwLockWriteGuard<'static, Box>, + // we must keep a reference as the lifetime of the guard becomes owned using unsafe code + // (warning!! The fields of a struct are dropped in declaration order thus this must be at the end) + // (https://doc.rust-lang.org/reference/destructors.html#:~:text=The%20fields%20of%20a%20struct,first%20element%20to%20the%20last.) + _file: Arc>>, } impl InodeValFileWriteGuard { pub(crate) fn new(file: &Arc>>) -> Self { let guard = file.write().unwrap(); Self { - file: file.clone(), + _file: file.clone(), guard: unsafe { std::mem::transmute(guard) }, } } @@ -507,32 +492,23 @@ impl DerefMut for InodeValFileWriteGuard { #[derive(Debug)] pub(crate) struct WasiStateFileGuard { - inodes: Arc>, - inode: generational_arena::Index, + inode: InodeGuard, } impl WasiStateFileGuard { pub fn new(state: &WasiState, fd: wasi::Fd) -> Result, FsError> { - let inodes = state.inodes.read().unwrap(); let fd_map = state.fs.fd_map.read().unwrap(); if let Some(fd) = fd_map.get(&fd) { - let guard = inodes.arena[fd.inode].read(); - if let Kind::File { .. } = guard.deref() { - Ok(Some(Self { - inodes: state.inodes.clone(), - inode: fd.inode, - })) - } else { - // Our public API should ensure that this is not possible - Err(FsError::NotAFile) - } + Ok(Some(Self { + inode: fd.inode.clone(), + })) } else { Ok(None) } } - pub fn lock_read(&self, inodes: &RwLockReadGuard) -> Option { - let guard = inodes.arena[self.inode].read(); + pub fn lock_read(&self) -> Option { + let guard = self.inode.read(); if let Kind::File { handle, .. } = guard.deref() { handle.as_ref().map(InodeValFileReadGuard::new) } else { @@ -541,11 +517,8 @@ impl WasiStateFileGuard { } } - pub fn lock_write( - &self, - inodes: &RwLockReadGuard, - ) -> Option { - let guard = inodes.arena[self.inode].read(); + pub fn lock_write(&self) -> Option { + let guard = self.inode.read(); if let Kind::File { handle, .. } = guard.deref() { handle.as_ref().map(InodeValFileWriteGuard::new) } else { @@ -557,8 +530,7 @@ impl WasiStateFileGuard { impl VirtualFile for WasiStateFileGuard { fn last_accessed(&self) -> u64 { - let inodes = self.inodes.read().unwrap(); - let guard = self.lock_read(&inodes); + let guard = self.lock_read(); if let Some(file) = guard.as_ref() { file.last_accessed() } else { @@ -567,8 +539,7 @@ impl VirtualFile for WasiStateFileGuard { } fn last_modified(&self) -> u64 { - let inodes = self.inodes.read().unwrap(); - let guard = self.lock_read(&inodes); + let guard = self.lock_read(); if let Some(file) = guard.as_ref() { file.last_modified() } else { @@ -577,8 +548,7 @@ impl VirtualFile for WasiStateFileGuard { } fn created_time(&self) -> u64 { - let inodes = self.inodes.read().unwrap(); - let guard = self.lock_read(&inodes); + let guard = self.lock_read(); if let Some(file) = guard.as_ref() { file.created_time() } else { @@ -587,8 +557,7 @@ impl VirtualFile for WasiStateFileGuard { } fn size(&self) -> u64 { - let inodes = self.inodes.read().unwrap(); - let guard = self.lock_read(&inodes); + let guard = self.lock_read(); if let Some(file) = guard.as_ref() { file.size() } else { @@ -597,8 +566,7 @@ impl VirtualFile for WasiStateFileGuard { } fn set_len(&mut self, new_size: u64) -> Result<(), FsError> { - let inodes = self.inodes.read().unwrap(); - let mut guard = self.lock_write(&inodes); + let mut guard = self.lock_write(); if let Some(file) = guard.as_mut() { file.set_len(new_size) } else { @@ -607,8 +575,7 @@ impl VirtualFile for WasiStateFileGuard { } fn unlink(&mut self) -> Result<(), FsError> { - let inodes = self.inodes.read().unwrap(); - let mut guard = self.lock_write(&inodes); + let mut guard = self.lock_write(); if let Some(file) = guard.as_mut() { file.unlink() } else { @@ -617,8 +584,7 @@ impl VirtualFile for WasiStateFileGuard { } fn is_open(&self) -> bool { - let inodes = self.inodes.read().unwrap(); - let guard = self.lock_read(&inodes); + let guard = self.lock_read(); if let Some(file) = guard.as_ref() { file.is_open() } else { @@ -627,8 +593,7 @@ impl VirtualFile for WasiStateFileGuard { } fn poll_read_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - let inodes = self.inodes.read().unwrap(); - let mut guard = self.lock_write(&inodes); + let mut guard = self.lock_write(); if let Some(file) = guard.as_mut() { let file = Pin::new(file.deref_mut()); file.poll_read_ready(cx) @@ -641,8 +606,7 @@ impl VirtualFile for WasiStateFileGuard { self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { - let inodes = self.inodes.read().unwrap(); - let mut guard = self.lock_write(&inodes); + let mut guard = self.lock_write(); if let Some(file) = guard.as_mut() { let file = Pin::new(file.deref_mut()); file.poll_write_ready(cx) @@ -654,8 +618,7 @@ impl VirtualFile for WasiStateFileGuard { impl AsyncSeek for WasiStateFileGuard { fn start_seek(self: Pin<&mut Self>, position: SeekFrom) -> std::io::Result<()> { - let inodes = self.inodes.read().unwrap(); - let mut guard = self.lock_write(&inodes); + let mut guard = self.lock_write(); if let Some(guard) = guard.as_mut() { let file = Pin::new(guard.deref_mut()); file.start_seek(position) @@ -664,8 +627,7 @@ impl AsyncSeek for WasiStateFileGuard { } } fn poll_complete(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - let inodes = self.inodes.read().unwrap(); - let mut guard = self.lock_write(&inodes); + let mut guard = self.lock_write(); if let Some(guard) = guard.as_mut() { let file = Pin::new(guard.deref_mut()); file.poll_complete(cx) @@ -681,8 +643,7 @@ impl AsyncWrite for WasiStateFileGuard { cx: &mut Context<'_>, buf: &[u8], ) -> Poll> { - let inodes = self.inodes.read().unwrap(); - let mut guard = self.lock_write(&inodes); + let mut guard = self.lock_write(); if let Some(guard) = guard.as_mut() { let file = Pin::new(guard.deref_mut()); file.poll_write(cx, buf) @@ -691,8 +652,7 @@ impl AsyncWrite for WasiStateFileGuard { } } fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - let inodes = self.inodes.read().unwrap(); - let mut guard = self.lock_write(&inodes); + let mut guard = self.lock_write(); if let Some(guard) = guard.as_mut() { let file = Pin::new(guard.deref_mut()); file.poll_flush(cx) @@ -701,8 +661,7 @@ impl AsyncWrite for WasiStateFileGuard { } } fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - let inodes = self.inodes.read().unwrap(); - let mut guard = self.lock_write(&inodes); + let mut guard = self.lock_write(); if let Some(guard) = guard.as_mut() { let file = Pin::new(guard.deref_mut()); file.poll_shutdown(cx) @@ -715,8 +674,7 @@ impl AsyncWrite for WasiStateFileGuard { cx: &mut Context<'_>, bufs: &[IoSlice<'_>], ) -> Poll> { - let inodes = self.inodes.read().unwrap(); - let mut guard = self.lock_write(&inodes); + let mut guard = self.lock_write(); if let Some(guard) = guard.as_mut() { let file = Pin::new(guard.deref_mut()); file.poll_write_vectored(cx, bufs) @@ -725,8 +683,7 @@ impl AsyncWrite for WasiStateFileGuard { } } fn is_write_vectored(&self) -> bool { - let inodes = self.inodes.read().unwrap(); - let mut guard = self.lock_write(&inodes); + let mut guard = self.lock_write(); if let Some(guard) = guard.as_mut() { let file = Pin::new(guard.deref_mut()); file.is_write_vectored() @@ -742,8 +699,7 @@ impl AsyncRead for WasiStateFileGuard { cx: &mut Context<'_>, buf: &mut tokio::io::ReadBuf<'_>, ) -> Poll> { - let inodes = self.inodes.read().unwrap(); - let mut guard = self.lock_write(&inodes); + let mut guard = self.lock_write(); if let Some(guard) = guard.as_mut() { let file = Pin::new(guard.deref_mut()); file.poll_read(cx, buf) @@ -752,3 +708,12 @@ impl AsyncRead for WasiStateFileGuard { } } } + +fn is_err_closed(err: &std::io::Error) -> bool { + err.kind() == std::io::ErrorKind::ConnectionAborted + || err.kind() == std::io::ErrorKind::ConnectionRefused + || err.kind() == std::io::ErrorKind::ConnectionReset + || err.kind() == std::io::ErrorKind::BrokenPipe + || err.kind() == std::io::ErrorKind::NotConnected + || err.kind() == std::io::ErrorKind::UnexpectedEof +} diff --git a/lib/wasi/src/fs/mod.rs b/lib/wasi/src/fs/mod.rs index c8ef24fde15..96fd0514d55 100644 --- a/lib/wasi/src/fs/mod.rs +++ b/lib/wasi/src/fs/mod.rs @@ -8,12 +8,11 @@ use std::{ path::{Path, PathBuf}, sync::{ atomic::{AtomicBool, AtomicU32, AtomicU64, Ordering}, - Arc, Mutex, RwLock, RwLockWriteGuard, + Arc, Mutex, RwLock, Weak, }, }; use crate::state::{Stderr, Stdin, Stdout}; -use generational_arena::{Arena, Index as Inode}; #[cfg(feature = "enable-serde")] use serde_derive::{Deserialize, Serialize}; use tokio::io::AsyncWriteExt; @@ -27,18 +26,13 @@ use wasmer_wasi_types::{ }, }; -pub use self::fd::{Fd, InodeVal, Kind}; +pub use self::fd::{Fd, InodeVal, Kind, NotificationInner}; pub(crate) use self::inode_guard::{ InodeValFilePollGuard, InodeValFilePollGuardJoin, InodeValFileReadGuard, InodeValFileWriteGuard, WasiStateFileGuard, }; use crate::syscalls::map_io_err; -use crate::{ - bin_factory::BinaryPackage, - net::socket::{InodeSocket, InodeSocketKind}, - state::PreopenedDir, - ALL_RIGHTS, -}; +use crate::{bin_factory::BinaryPackage, state::PreopenedDir, ALL_RIGHTS}; /// the fd value of the virtual root pub const VIRTUAL_ROOT_FD: WasiFd = 3; @@ -73,89 +67,162 @@ const STDERR_DEFAULT_RIGHTS: Rights = STDOUT_DEFAULT_RIGHTS; /// the number of symlinks that can be traversed when resolving a path pub const MAX_SYMLINKS: u32 = 128; +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct Inode(u64); + +impl Inode { + fn as_u64(&self) -> u64 { + self.0 + } +} + +#[derive(Debug, Clone)] +pub struct InodeGuard { + ino: Inode, + inner: Arc, +} +impl InodeGuard { + pub fn ino(&self) -> Inode { + self.ino + } + pub fn downgrade(&self) -> InodeWeakGuard { + InodeWeakGuard { + ino: self.ino, + inner: Arc::downgrade(&self.inner), + } + } +} +impl std::ops::Deref for InodeGuard { + type Target = InodeVal; + fn deref(&self) -> &Self::Target { + self.inner.deref() + } +} + +#[derive(Debug, Clone)] +pub struct InodeWeakGuard { + ino: Inode, + inner: Weak, +} +impl InodeWeakGuard { + pub fn ino(&self) -> Inode { + self.ino + } + pub fn upgrade(&self) -> Option { + if let Some(inner) = Weak::upgrade(&self.inner) { + Some(InodeGuard { + ino: self.ino, + inner, + }) + } else { + None + } + } +} + #[derive(Debug)] #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] +struct WasiInodesProtected { + seed: u64, + lookup: HashMap>, +} + +#[derive(Clone, Debug)] +#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] pub struct WasiInodes { - pub arena: Arena, - pub orphan_fds: HashMap, + protected: Arc>, } impl WasiInodes { - /// gets either a normal inode or an orphaned inode - pub fn get_inodeval(&self, inode: generational_arena::Index) -> Result<&InodeVal, Errno> { - if let Some(iv) = self.arena.get(inode) { - Ok(iv) - } else { - self.orphan_fds.get(&inode).ok_or(Errno::Badf) + pub fn new() -> Self { + Self { + protected: Arc::new(RwLock::new(WasiInodesProtected { + seed: 1, + lookup: Default::default(), + })), } } /// gets either a normal inode or an orphaned inode - pub fn get_inodeval_mut( - &mut self, - inode: generational_arena::Index, - ) -> Result<&mut InodeVal, Errno> { - if let Some(iv) = self.arena.get_mut(inode) { - Ok(iv) + pub fn get_inodeval(&self, ino: Inode) -> Result { + let guard = self.protected.read().unwrap(); + if let Some(inner) = guard.lookup.get(&ino).and_then(Weak::upgrade) { + Ok(InodeGuard { ino, inner }) } else { - self.orphan_fds.get_mut(&inode).ok_or(Errno::Badf) + Err(Errno::Badf) } } + /// aads another value to the inodes + pub fn add_inode_val(&self, val: InodeVal) -> InodeGuard { + let val = Arc::new(val); + + let mut guard = self.protected.write().unwrap(); + let ino = Inode(guard.seed); + guard.seed += 1; + guard.lookup.insert(ino, Arc::downgrade(&val)); + + // Set the inode value + { + let mut guard = val.stat.write().unwrap(); + guard.st_ino = ino.0; + } + + // every 100 calls we clear out dead weaks + if guard.seed % 100 == 1 { + guard.lookup.retain(|_, v| Weak::strong_count(v) > 0); + } + + InodeGuard { ino, inner: val } + } + /// Get the `VirtualFile` object at stdout pub(crate) fn stdout( - &self, fd_map: &RwLock>, ) -> Result { - self.std_dev_get(fd_map, __WASI_STDOUT_FILENO) + Self::std_dev_get(fd_map, __WASI_STDOUT_FILENO) } /// Get the `VirtualFile` object at stdout mutably pub(crate) fn stdout_mut( - &self, fd_map: &RwLock>, ) -> Result { - self.std_dev_get_mut(fd_map, __WASI_STDOUT_FILENO) + Self::std_dev_get_mut(fd_map, __WASI_STDOUT_FILENO) } /// Get the `VirtualFile` object at stderr pub(crate) fn stderr( - &self, fd_map: &RwLock>, ) -> Result { - self.std_dev_get(fd_map, __WASI_STDERR_FILENO) + Self::std_dev_get(fd_map, __WASI_STDERR_FILENO) } /// Get the `VirtualFile` object at stderr mutably pub(crate) fn stderr_mut( - &self, fd_map: &RwLock>, ) -> Result { - self.std_dev_get_mut(fd_map, __WASI_STDERR_FILENO) + Self::std_dev_get_mut(fd_map, __WASI_STDERR_FILENO) } /// Get the `VirtualFile` object at stdin pub(crate) fn stdin( - &self, fd_map: &RwLock>, ) -> Result { - self.std_dev_get(fd_map, __WASI_STDIN_FILENO) + Self::std_dev_get(fd_map, __WASI_STDIN_FILENO) } /// Get the `VirtualFile` object at stdin mutably pub(crate) fn stdin_mut( - &self, fd_map: &RwLock>, ) -> Result { - self.std_dev_get_mut(fd_map, __WASI_STDIN_FILENO) + Self::std_dev_get_mut(fd_map, __WASI_STDIN_FILENO) } /// Internal helper function to get a standard device handle. /// Expects one of `__WASI_STDIN_FILENO`, `__WASI_STDOUT_FILENO`, `__WASI_STDERR_FILENO`. fn std_dev_get( - &self, fd_map: &RwLock>, fd: WasiFd, ) -> Result { if let Some(fd) = fd_map.read().unwrap().get(&fd) { - let guard = self.arena[fd.inode].read(); + let guard = fd.inode.read(); if let Kind::File { handle: Some(handle), .. @@ -174,12 +241,11 @@ impl WasiInodes { /// Internal helper function to mutably get a standard device handle. /// Expects one of `__WASI_STDIN_FILENO`, `__WASI_STDOUT_FILENO`, `__WASI_STDERR_FILENO`. fn std_dev_get_mut( - &self, fd_map: &RwLock>, fd: WasiFd, ) -> Result { if let Some(fd) = fd_map.read().unwrap().get(&fd) { - let guard = self.arena[fd.inode].read(); + let guard = fd.inode.read(); if let Kind::File { handle: Some(handle), .. @@ -264,49 +330,35 @@ pub struct WasiFs { pub name_map: HashMap, pub fd_map: Arc>>, pub next_fd: AtomicU32, - inode_counter: AtomicU64, pub current_dir: Mutex, pub is_wasix: AtomicBool, #[cfg_attr(feature = "enable-serde", serde(skip, default))] pub root_fs: WasiFsRoot, + pub root_inode: InodeGuard, pub has_unioned: Arc>>, } impl WasiFs { /// Forking the WasiState is used when either fork or vfork is called - pub fn fork(&self, inc_refs: bool) -> Self { + pub fn fork(&self) -> Self { let fd_map = self.fd_map.read().unwrap().clone(); - if inc_refs { - for (id, fd) in fd_map.iter() { - tracing::trace!( - "fd_fork(fd={}) ref-cnt-inc({}+1)", - *id, - fd.ref_cnt.load(Ordering::Relaxed) - ); - fd.ref_cnt.fetch_add(1, Ordering::Relaxed); - } - } Self { preopen_fds: RwLock::new(self.preopen_fds.read().unwrap().clone()), name_map: self.name_map.clone(), fd_map: Arc::new(RwLock::new(fd_map)), - next_fd: AtomicU32::new(self.next_fd.load(Ordering::Acquire)), - inode_counter: AtomicU64::new(self.inode_counter.load(Ordering::Acquire)), + next_fd: AtomicU32::new(self.next_fd.load(Ordering::SeqCst)), current_dir: Mutex::new(self.current_dir.lock().unwrap().clone()), is_wasix: AtomicBool::new(self.is_wasix.load(Ordering::Acquire)), root_fs: self.root_fs.clone(), + root_inode: self.root_inode.clone(), has_unioned: Arc::new(Mutex::new(HashSet::new())), } } /// Closes all the file handles - pub fn close_all(&self, inodes: &WasiInodes) { + pub fn close_all(&self) { let mut guard = self.fd_map.write().unwrap(); - let fds = { guard.iter().map(|a| *a.0).collect::>() }; - - for fd in fds { - _ = self.close_fd_ext(inodes, &mut guard, fd); - } + guard.clear(); } /// Will conditionally union the binary file system with this one @@ -333,7 +385,7 @@ impl WasiFs { /// Created for the builder API. like `new` but with more information pub(crate) fn new_with_preopen( - inodes: &mut WasiInodes, + inodes: &WasiInodes, preopens: &[PreopenedDir], vfs_preopens: &[String], fs_backing: WasiFsRoot, @@ -342,7 +394,7 @@ impl WasiFs { for preopen_name in vfs_preopens { let kind = Kind::Dir { - parent: Some(root_inode), + parent: root_inode.downgrade(), path: PathBuf::from(preopen_name), entries: Default::default(), }; @@ -369,10 +421,10 @@ impl WasiFs { })?; let fd_flags = Fd::READ; let fd = wasi_fs - .create_fd(rights, rights, Fdflags::empty(), fd_flags, inode) + .create_fd(rights, rights, Fdflags::empty(), fd_flags, inode.clone()) .map_err(|e| format!("Could not open fd for file {:?}: {}", preopen_name, e))?; { - let mut guard = inodes.arena[root_inode].write(); + let mut guard = root_inode.write(); if let Kind::Root { entries } = guard.deref_mut() { let existing_entry = entries.insert(preopen_name.clone(), inode); if existing_entry.is_some() { @@ -407,7 +459,7 @@ impl WasiFs { let kind = if cur_dir_metadata.is_dir() { Kind::Dir { - parent: Some(root_inode), + parent: root_inode.downgrade(), path: path.clone(), entries: Default::default(), } @@ -487,10 +539,10 @@ impl WasiFs { fd_flags }; let fd = wasi_fs - .create_fd(rights, rights, Fdflags::empty(), fd_flags, inode) + .create_fd(rights, rights, Fdflags::empty(), fd_flags, inode.clone()) .map_err(|e| format!("Could not open fd for file {:?}: {}", path, e))?; { - let mut guard = inodes.arena[root_inode].write(); + let mut guard = root_inode.write(); if let Kind::Root { entries } = guard.deref_mut() { let key = if let Some(alias) = &alias { alias.clone() @@ -524,18 +576,32 @@ impl WasiFs { /// Private helper function to init the filesystem, called in `new` and /// `new_with_preopen` - fn new_init(fs_backing: WasiFsRoot, inodes: &mut WasiInodes) -> Result<(Self, Inode), String> { + fn new_init(fs_backing: WasiFsRoot, inodes: &WasiInodes) -> Result<(Self, InodeGuard), String> { debug!("Initializing WASI filesystem"); + let stat = Filestat { + st_filetype: Filetype::Directory, + ..Filestat::default() + }; + let root_kind = Kind::Root { + entries: HashMap::new(), + }; + let root_inode = inodes.add_inode_val(InodeVal { + stat: RwLock::new(stat), + is_preopened: true, + name: "/".into(), + kind: RwLock::new(root_kind), + }); + let wasi_fs = Self { preopen_fds: RwLock::new(vec![]), name_map: HashMap::new(), fd_map: Arc::new(RwLock::new(HashMap::new())), next_fd: AtomicU32::new(3), - inode_counter: AtomicU64::new(1024), current_dir: Mutex::new("/".to_string()), is_wasix: AtomicBool::new(false), root_fs: fs_backing, + root_inode: root_inode.clone(), has_unioned: Arc::new(Mutex::new(HashSet::new())), }; wasi_fs.create_stdin(inodes); @@ -543,43 +609,40 @@ impl WasiFs { wasi_fs.create_stderr(inodes); // create virtual root - let root_inode = { - let all_rights = ALL_RIGHTS; - // TODO: make this a list of positive rigths instead of negative ones - // root gets all right for now - let root_rights = all_rights - /* - & (!Rights::FD_WRITE) - & (!Rights::FD_ALLOCATE) - & (!Rights::PATH_CREATE_DIRECTORY) - & (!Rights::PATH_CREATE_FILE) - & (!Rights::PATH_LINK_SOURCE) - & (!Rights::PATH_RENAME_SOURCE) - & (!Rights::PATH_RENAME_TARGET) - & (!Rights::PATH_FILESTAT_SET_SIZE) - & (!Rights::PATH_FILESTAT_SET_TIMES) - & (!Rights::FD_FILESTAT_SET_SIZE) - & (!Rights::FD_FILESTAT_SET_TIMES) - & (!Rights::PATH_SYMLINK) - & (!Rights::PATH_UNLINK_FILE) - & (!Rights::PATH_REMOVE_DIRECTORY) - */; - let inode = wasi_fs.create_virtual_root(inodes); - let fd = wasi_fs - .create_fd(root_rights, root_rights, Fdflags::empty(), Fd::READ, inode) - .map_err(|e| format!("Could not create root fd: {}", e))?; - wasi_fs.preopen_fds.write().unwrap().push(fd); - inode - }; + let all_rights = ALL_RIGHTS; + // TODO: make this a list of positive rigths instead of negative ones + // root gets all right for now + let root_rights = all_rights + /* + & (!Rights::FD_WRITE) + & (!Rights::FD_ALLOCATE) + & (!Rights::PATH_CREATE_DIRECTORY) + & (!Rights::PATH_CREATE_FILE) + & (!Rights::PATH_LINK_SOURCE) + & (!Rights::PATH_RENAME_SOURCE) + & (!Rights::PATH_RENAME_TARGET) + & (!Rights::PATH_FILESTAT_SET_SIZE) + & (!Rights::PATH_FILESTAT_SET_TIMES) + & (!Rights::FD_FILESTAT_SET_SIZE) + & (!Rights::FD_FILESTAT_SET_TIMES) + & (!Rights::PATH_SYMLINK) + & (!Rights::PATH_UNLINK_FILE) + & (!Rights::PATH_REMOVE_DIRECTORY) + */; + let fd = wasi_fs + .create_fd( + root_rights, + root_rights, + Fdflags::empty(), + Fd::READ, + root_inode.clone(), + ) + .map_err(|e| format!("Could not create root fd: {}", e))?; + wasi_fs.preopen_fds.write().unwrap().push(fd); Ok((wasi_fs, root_inode)) } - /// Returns the next available inode index for creating a new inode. - fn get_next_inode_index(&self) -> u64 { - self.inode_counter.fetch_add(1, Ordering::AcqRel) - } - /// This function is like create dir all, but it also opens it. /// Function is unsafe because it may break invariants and hasn't been tested. /// This is an experimental function and may be removed @@ -592,7 +655,7 @@ impl WasiFs { #[allow(dead_code)] pub unsafe fn open_dir_all( &mut self, - inodes: &mut WasiInodes, + inodes: &WasiInodes, base: WasiFd, name: String, rights: Rights, @@ -607,7 +670,7 @@ impl WasiFs { //let n_components = path.components().count(); for c in path.components() { let segment_name = c.as_os_str().to_string_lossy().to_string(); - let guard = inodes.arena[cur_inode].read(); + let guard = cur_inode.read(); match guard.deref() { Kind::Dir { ref entries, .. } | Kind::Root { ref entries } => { if let Some(_entry) = entries.get(&segment_name) { @@ -616,7 +679,7 @@ impl WasiFs { } let kind = Kind::Dir { - parent: Some(cur_inode), + parent: cur_inode.downgrade(), path: PathBuf::from(""), entries: HashMap::new(), }; @@ -631,13 +694,13 @@ impl WasiFs { // reborrow to insert { - let mut guard = inodes.arena[cur_inode].write(); + let mut guard = cur_inode.write(); match guard.deref_mut() { Kind::Dir { ref mut entries, .. } | Kind::Root { ref mut entries } => { - entries.insert(segment_name, inode); + entries.insert(segment_name, inode.clone()); } _ => unreachable!("Dir or Root became not Dir or Root"), } @@ -665,7 +728,7 @@ impl WasiFs { #[allow(dead_code, clippy::too_many_arguments)] pub fn open_file_at( &mut self, - inodes: &mut WasiInodes, + inodes: &WasiInodes, base: WasiFd, file: Box, open_flags: u16, @@ -678,7 +741,7 @@ impl WasiFs { // an explicit choice, so justify it in a comment when we remove this one let base_inode = self.get_fd_inode(base).map_err(fs_error_from_wasi_err)?; - let guard = inodes.arena[base_inode].read(); + let guard = base_inode.read(); match guard.deref() { Kind::Dir { ref entries, .. } | Kind::Root { ref entries } => { if let Some(_entry) = entries.get(&name) { @@ -689,7 +752,7 @@ impl WasiFs { let kind = Kind::File { handle: Some(Arc::new(RwLock::new(file))), path: PathBuf::from(""), - fd: Some(self.next_fd.load(Ordering::Acquire)), + fd: Some(self.next_fd.fetch_add(1, Ordering::SeqCst)), }; drop(guard); @@ -698,13 +761,13 @@ impl WasiFs { .map_err(|_| FsError::IOError)?; { - let mut guard = inodes.arena[base_inode].write(); + let mut guard = base_inode.write(); match guard.deref_mut() { Kind::Dir { ref mut entries, .. } | Kind::Root { ref mut entries } => { - entries.insert(name, inode); + entries.insert(name, inode.clone()); } _ => unreachable!("Dir or Root became not Dir or Root"), } @@ -723,28 +786,27 @@ impl WasiFs { #[allow(dead_code)] pub fn swap_file( &self, - inodes: &WasiInodes, fd: WasiFd, mut file: Box, ) -> Result>, FsError> { match fd { __WASI_STDIN_FILENO => { - let mut target = inodes.stdin_mut(&self.fd_map)?; + let mut target = WasiInodes::stdin_mut(&self.fd_map)?; Ok(Some(target.swap(file))) } __WASI_STDOUT_FILENO => { - let mut target = inodes.stdout_mut(&self.fd_map)?; + let mut target = WasiInodes::stdout_mut(&self.fd_map)?; Ok(Some(target.swap(file))) } __WASI_STDERR_FILENO => { - let mut target = inodes.stderr_mut(&self.fd_map)?; + let mut target = WasiInodes::stderr_mut(&self.fd_map)?; Ok(Some(target.swap(file))) } _ => { let base_inode = self.get_fd_inode(fd).map_err(fs_error_from_wasi_err)?; { // happy path - let guard = inodes.arena[base_inode].read(); + let guard = base_inode.read(); match guard.deref() { Kind::File { ref handle, .. } => { if let Some(handle) = handle { @@ -757,7 +819,7 @@ impl WasiFs { } } // slow path - let mut guard = inodes.arena[base_inode].write(); + let mut guard = base_inode.write(); match guard.deref_mut() { Kind::File { ref mut handle, .. } => { if let Some(handle) = handle { @@ -776,9 +838,9 @@ impl WasiFs { } /// refresh size from filesystem - pub fn filestat_resync_size(&self, inodes: &WasiInodes, fd: WasiFd) -> Result { + pub fn filestat_resync_size(&self, fd: WasiFd) -> Result { let inode = self.get_fd_inode(fd)?; - let mut guard = inodes.arena[inode].write(); + let mut guard = inode.write(); match guard.deref_mut() { Kind::File { handle, .. } => { if let Some(h) = handle { @@ -787,7 +849,7 @@ impl WasiFs { drop(h); drop(guard); - inodes.arena[inode].stat.write().unwrap().st_size = new_size; + inode.stat.write().unwrap().st_size = new_size; Ok(new_size as Filesize) } else { Err(Errno::Badf) @@ -807,18 +869,18 @@ impl WasiFs { /// Gets the current directory pub fn get_current_dir( &self, - inodes: &mut WasiInodes, + inodes: &WasiInodes, base: WasiFd, - ) -> Result<(Inode, String), Errno> { + ) -> Result<(InodeGuard, String), Errno> { self.get_current_dir_inner(inodes, base, 0) } pub(crate) fn get_current_dir_inner( &self, - inodes: &mut WasiInodes, + inodes: &WasiInodes, base: WasiFd, symlink_count: u32, - ) -> Result<(Inode, String), Errno> { + ) -> Result<(InodeGuard, String), Errno> { let current_dir = { let guard = self.current_dir.lock().unwrap(); guard.clone() @@ -849,12 +911,12 @@ impl WasiFs { /// TODO: write more tests for this code fn get_inode_at_path_inner( &self, - inodes: &mut WasiInodes, - mut cur_inode: generational_arena::Index, + inodes: &WasiInodes, + mut cur_inode: InodeGuard, path: &str, mut symlink_count: u32, follow_symlinks: bool, - ) -> Result { + ) -> Result { if symlink_count > MAX_SYMLINKS { return Err(Errno::Mlink); } @@ -869,7 +931,8 @@ impl WasiFs { // for each component traverse file structure // loading inodes as necessary 'symlink_resolution: while symlink_count < MAX_SYMLINKS { - let mut guard = inodes.arena[cur_inode].write(); + let processing_cur_inode = cur_inode.clone(); + let mut guard = processing_cur_inode.write(); match guard.deref_mut() { Kind::Buffer { .. } => unimplemented!("state::get_inode_at_path for buffers"), Kind::Dir { @@ -880,8 +943,8 @@ impl WasiFs { } => { match component.as_os_str().to_string_lossy().borrow() { ".." => { - if let Some(p) = parent { - cur_inode = *p; + if let Some(p) = parent.upgrade() { + cur_inode = p; continue 'path_iter; } else { return Err(Errno::Access); @@ -895,7 +958,7 @@ impl WasiFs { if let Some(entry) = entries.get(component.as_os_str().to_string_lossy().as_ref()) { - cur_inode = *entry; + cur_inode = entry.clone(); } else { let file = { let mut cd = path.clone(); @@ -916,7 +979,7 @@ impl WasiFs { should_insert = true; // load DIR Kind::Dir { - parent: Some(cur_inode), + parent: cur_inode.downgrade(), path: file.clone(), entries: Default::default(), } @@ -934,7 +997,7 @@ impl WasiFs { debug!("attempting to decompose path {:?}", link_value); let (pre_open_dir_fd, relative_path) = if link_value.is_relative() { - self.path_into_pre_open_and_relative_path(inodes, &file)? + self.path_into_pre_open_and_relative_path(&file)? } else { unimplemented!("Absolute symlinks are not yet supported"); }; @@ -981,14 +1044,14 @@ impl WasiFs { }, ); - let mut guard = inodes.arena[cur_inode].write(); + let mut guard = cur_inode.write(); if let Kind::Dir { ref mut entries, .. } = guard.deref_mut() { entries.insert( component.as_os_str().to_string_lossy().to_string(), - new_inode, + new_inode.clone(), ); } else { unreachable!( @@ -1001,8 +1064,8 @@ impl WasiFs { #[cfg(not(unix))] unimplemented!("state::get_inode_at_path unknown file type: not file, directory, or symlink"); }; - drop(guard); + let new_inode = self.create_inode( inodes, kind, @@ -1010,14 +1073,14 @@ impl WasiFs { file.to_string_lossy().to_string(), )?; if should_insert { - let mut guard = inodes.arena[cur_inode].write(); + let mut guard = processing_cur_inode.write(); if let Kind::Dir { ref mut entries, .. } = guard.deref_mut() { entries.insert( component.as_os_str().to_string_lossy().to_string(), - new_inode, + new_inode.clone(), ); } } @@ -1041,7 +1104,7 @@ impl WasiFs { if let Some(entry) = entries.get(component.as_os_str().to_string_lossy().as_ref()) { - cur_inode = *entry; + cur_inode = entry.clone(); } else { // Root is not capable of having something other then preopenned folders return Err(Errno::Notcapable); @@ -1085,7 +1148,7 @@ impl WasiFs { cur_inode = symlink_inode; // if we're at the very end and we found a file, then we're done // TODO: figure out if this should also happen for directories? - let guard = inodes.arena[cur_inode].read(); + let guard = cur_inode.read(); if let Kind::File { .. } = guard.deref() { // check if on last step if last_component { @@ -1113,7 +1176,6 @@ impl WasiFs { /// In the case of a tie, the later preopened fd is preferred. fn path_into_pre_open_and_relative_path<'path>( &self, - inodes: &WasiInodes, path: &'path Path, ) -> Result<(WasiFd, &'path Path), Errno> { enum BaseFdAndRelPath<'a> { @@ -1137,8 +1199,8 @@ impl WasiFs { // for each preopened directory let preopen_fds = self.preopen_fds.read().unwrap(); for po_fd in preopen_fds.deref() { - let po_inode = self.fd_map.read().unwrap()[po_fd].inode; - let guard = inodes.arena[po_inode].read(); + let po_inode = self.fd_map.read().unwrap()[po_fd].inode.clone(); + let guard = po_inode.read(); let po_path = match guard.deref() { Kind::Dir { path, .. } => &**path, Kind::Root { .. } => Path::new("/"), @@ -1168,23 +1230,21 @@ impl WasiFs { /// finds the number of directories between the fd and the inode if they're connected /// expects inode to point to a directory - pub(crate) fn path_depth_from_fd( - &self, - inodes: &WasiInodes, - fd: WasiFd, - inode: Inode, - ) -> Result { + pub(crate) fn path_depth_from_fd(&self, fd: WasiFd, inode: InodeGuard) -> Result { let mut counter = 0; let base_inode = self.get_fd_inode(fd)?; let mut cur_inode = inode; - while cur_inode != base_inode { + while cur_inode.ino() != base_inode.ino() { counter += 1; - let guard = inodes.arena[cur_inode].read(); + + let processing_cur_inode = cur_inode.clone(); + let guard = processing_cur_inode.read(); + match guard.deref() { Kind::Dir { parent, .. } => { - if let Some(p) = parent { - cur_inode = *p; + if let Some(p) = parent.upgrade() { + cur_inode = p; } } _ => return Err(Errno::Inval), @@ -1202,11 +1262,11 @@ impl WasiFs { // This will be resolved when we have tests asserting the correct behavior pub(crate) fn get_inode_at_path( &self, - inodes: &mut WasiInodes, + inodes: &WasiInodes, base: WasiFd, path: &str, follow_symlinks: bool, - ) -> Result { + ) -> Result { let start_inode = if !path.starts_with('/') && self.is_wasix.load(Ordering::Acquire) { let (cur_inode, _) = self.get_current_dir(inodes, base)?; cur_inode @@ -1220,11 +1280,11 @@ impl WasiFs { /// stripped off pub(crate) fn get_parent_inode_at_path( &self, - inodes: &mut WasiInodes, + inodes: &WasiInodes, base: WasiFd, path: &Path, follow_symlinks: bool, - ) -> Result<(Inode, String), Errno> { + ) -> Result<(InodeGuard, String), Errno> { let mut parent_dir = std::path::PathBuf::new(); let mut components = path.components().rev(); let new_entity_name = components @@ -1249,21 +1309,22 @@ impl WasiFs { .map(|a| a.clone()) } - pub fn get_fd_inode(&self, fd: WasiFd) -> Result { + pub fn get_fd_inode(&self, fd: WasiFd) -> Result { self.fd_map .read() .unwrap() .get(&fd) .ok_or(Errno::Badf) - .map(|a| a.inode) + .map(|a| a.inode.clone()) } - pub fn filestat_fd(&self, inodes: &WasiInodes, fd: WasiFd) -> Result { + pub fn filestat_fd(&self, fd: WasiFd) -> Result { let inode = self.get_fd_inode(fd)?; - Ok(*inodes.arena[inode].stat.read().unwrap().deref()) + let guard = inode.stat.read().unwrap(); + Ok(guard.deref().clone()) } - pub fn fdstat(&self, inodes: &WasiInodes, fd: WasiFd) -> Result { + pub fn fdstat(&self, fd: WasiFd) -> Result { match fd { __WASI_STDIN_FILENO => { return Ok(Fdstat { @@ -1303,7 +1364,7 @@ impl WasiFs { let fd = self.get_fd(fd)?; debug!("fdstat: {:?}", fd); - let guard = inodes.arena[fd.inode].read(); + let guard = fd.inode.read(); let deref = guard.deref(); Ok(Fdstat { fs_filetype: match deref { @@ -1318,14 +1379,12 @@ impl WasiFs { }) } - pub fn prestat_fd(&self, inodes: &WasiInodes, fd: WasiFd) -> Result { + pub fn prestat_fd(&self, fd: WasiFd) -> Result { let inode = self.get_fd_inode(fd)?; //trace!("in prestat_fd {:?}", self.get_fd(fd)?); - let inode_val = &inodes.arena[inode]; - - if inode_val.is_preopened { - Ok(self.prestat_fd_inner(inode_val)) + if inode.is_preopened { + Ok(self.prestat_fd_inner(inode.deref())) } else { Err(Errno::Badf) } @@ -1344,17 +1403,15 @@ impl WasiFs { } } - pub async fn flush(&self, inodes: &WasiInodes, fd: WasiFd) -> Result<(), Errno> { + pub async fn flush(&self, fd: WasiFd) -> Result<(), Errno> { match fd { __WASI_STDIN_FILENO => (), - __WASI_STDOUT_FILENO => inodes - .stdout_mut(&self.fd_map) + __WASI_STDOUT_FILENO => WasiInodes::stdout_mut(&self.fd_map) .map_err(fs_error_into_wasi_err)? .flush() .await .map_err(map_io_err)?, - __WASI_STDERR_FILENO => inodes - .stderr_mut(&self.fd_map) + __WASI_STDERR_FILENO => WasiInodes::stderr_mut(&self.fd_map) .map_err(fs_error_into_wasi_err)? .flush() .await @@ -1365,7 +1422,7 @@ impl WasiFs { return Err(Errno::Access); } - let guard = inodes.arena[fd.inode].read(); + let guard = fd.inode.read(); match guard.deref() { Kind::File { handle: Some(file), .. @@ -1387,23 +1444,23 @@ impl WasiFs { /// Creates an inode and inserts it given a Kind and some extra data pub(crate) fn create_inode( &self, - inodes: &mut WasiInodes, + inodes: &WasiInodes, kind: Kind, is_preopened: bool, name: String, - ) -> Result { - let stat = self.get_stat_for_kind(inodes, &kind)?; + ) -> Result { + let stat = self.get_stat_for_kind(&kind)?; Ok(self.create_inode_with_stat(inodes, kind, is_preopened, name.into(), stat)) } /// Creates an inode and inserts it given a Kind, does not assume the file exists. pub(crate) fn create_inode_with_default_stat( &self, - inodes: &mut WasiInodes, + inodes: &WasiInodes, kind: Kind, is_preopened: bool, name: Cow<'static, str>, - ) -> Inode { + ) -> InodeGuard { let stat = Filestat::default(); self.create_inode_with_stat(inodes, kind, is_preopened, name, stat) } @@ -1411,13 +1468,12 @@ impl WasiFs { /// Creates an inode with the given filestat and inserts it. pub(crate) fn create_inode_with_stat( &self, - inodes: &mut WasiInodes, + inodes: &WasiInodes, kind: Kind, is_preopened: bool, name: Cow<'static, str>, mut stat: Filestat, - ) -> Inode { - stat.st_ino = self.get_next_inode_index(); + ) -> InodeGuard { match &kind { Kind::File { handle: Some(handle), @@ -1431,12 +1487,15 @@ impl WasiFs { } _ => {} } - inodes.arena.insert(InodeVal { + + let ret = inodes.add_inode_val(InodeVal { stat: RwLock::new(stat), is_preopened, name, kind: RwLock::new(kind), - }) + }); + stat.st_ino = ret.ino().as_u64(); + ret } pub fn create_fd( @@ -1445,9 +1504,9 @@ impl WasiFs { rights_inheriting: Rights, flags: Fdflags, open_flags: u16, - inode: Inode, + inode: InodeGuard, ) -> Result { - let idx = self.next_fd.fetch_add(1, Ordering::AcqRel); + let idx = self.next_fd.fetch_add(1, Ordering::SeqCst); self.create_fd_ext(rights, rights_inheriting, flags, open_flags, inode, idx)?; Ok(idx) } @@ -1458,7 +1517,7 @@ impl WasiFs { rights_inheriting: Rights, flags: Fdflags, open_flags: u16, - inode: Inode, + inode: InodeGuard, idx: WasiFd, ) -> Result<(), Errno> { let is_stdio = matches!( @@ -1468,7 +1527,6 @@ impl WasiFs { self.fd_map.write().unwrap().insert( idx, Fd { - ref_cnt: Arc::new(AtomicU32::new(1)), rights, rights_inheriting, flags, @@ -1483,12 +1541,10 @@ impl WasiFs { pub fn clone_fd(&self, fd: WasiFd) -> Result { let fd = self.get_fd(fd)?; - let idx = self.next_fd.fetch_add(1, Ordering::AcqRel); - fd.ref_cnt.fetch_add(1, Ordering::Acquire); + let idx = self.next_fd.fetch_add(1, Ordering::SeqCst); self.fd_map.write().unwrap().insert( idx, Fd { - ref_cnt: fd.ref_cnt.clone(), rights: fd.rights, rights_inheriting: fd.rights_inheriting, flags: fd.flags, @@ -1509,29 +1565,12 @@ impl WasiFs { /// # Safety /// - The caller must ensure that all references to the specified inode have /// been removed from the filesystem. - pub unsafe fn remove_inode(&self, inodes: &mut WasiInodes, inode: Inode) -> Option { - inodes.arena.remove(inode) - } - - fn create_virtual_root(&self, inodes: &mut WasiInodes) -> Inode { - let stat = Filestat { - st_filetype: Filetype::Directory, - st_ino: self.get_next_inode_index(), - ..Filestat::default() - }; - let root_kind = Kind::Root { - entries: HashMap::new(), - }; - - inodes.arena.insert(InodeVal { - stat: RwLock::new(stat), - is_preopened: true, - name: "/".into(), - kind: RwLock::new(root_kind), - }) + pub unsafe fn remove_inode(&self, inodes: &WasiInodes, ino: Inode) -> Option> { + let mut guard = inodes.protected.write().unwrap(); + guard.lookup.remove(&ino).and_then(|a| Weak::upgrade(&a)) } - fn create_stdout(&self, inodes: &mut WasiInodes) { + fn create_stdout(&self, inodes: &WasiInodes) { self.create_std_dev_inner( inodes, Box::new(Stdout::default()), @@ -1541,7 +1580,7 @@ impl WasiFs { Fdflags::APPEND, ); } - fn create_stdin(&self, inodes: &mut WasiInodes) { + fn create_stdin(&self, inodes: &WasiInodes) { self.create_std_dev_inner( inodes, Box::new(Stdin::default()), @@ -1551,7 +1590,7 @@ impl WasiFs { Fdflags::empty(), ); } - fn create_stderr(&self, inodes: &mut WasiInodes) { + fn create_stderr(&self, inodes: &WasiInodes) { self.create_std_dev_inner( inodes, Box::new(Stderr::default()), @@ -1564,25 +1603,24 @@ impl WasiFs { fn create_std_dev_inner( &self, - inodes: &mut WasiInodes, + inodes: &WasiInodes, handle: Box, name: &'static str, raw_fd: WasiFd, rights: Rights, fd_flags: Fdflags, ) { - let stat = Filestat { - st_filetype: Filetype::CharacterDevice, - st_ino: self.get_next_inode_index(), - ..Filestat::default() - }; - let kind = Kind::File { - fd: Some(raw_fd), - handle: Some(Arc::new(RwLock::new(handle))), - path: "".into(), - }; let inode = { - inodes.arena.insert(InodeVal { + let stat = Filestat { + st_filetype: Filetype::CharacterDevice, + ..Filestat::default() + }; + let kind = Kind::File { + fd: Some(raw_fd), + handle: Some(Arc::new(RwLock::new(handle))), + path: "".into(), + }; + inodes.add_inode_val(InodeVal { stat: RwLock::new(stat), is_preopened: true, name: name.to_string().into(), @@ -1592,7 +1630,6 @@ impl WasiFs { self.fd_map.write().unwrap().insert( raw_fd, Fd { - ref_cnt: Arc::new(AtomicU32::new(1)), rights, rights_inheriting: Rights::empty(), flags: fd_flags, @@ -1605,7 +1642,7 @@ impl WasiFs { ); } - pub fn get_stat_for_kind(&self, inodes: &WasiInodes, kind: &Kind) -> Result { + pub fn get_stat_for_kind(&self, kind: &Kind) -> Result { let md = match kind { Kind::File { handle, path, .. } => match handle { Some(wf) => { @@ -1635,8 +1672,7 @@ impl WasiFs { .. } => { let base_po_inode = &self.fd_map.read().unwrap()[base_po_dir].inode; - let base_po_inode_v = &inodes.arena[*base_po_inode]; - let guard = base_po_inode_v.read(); + let guard = base_po_inode.read(); match guard.deref() { Kind::Root { .. } => { self.root_fs.symlink_metadata(path_to_symlink).map_err(fs_error_into_wasi_err)? @@ -1669,95 +1705,19 @@ impl WasiFs { } /// Closes an open FD, handling all details such as FD being preopen - pub(crate) fn close_fd(&self, inodes: &WasiInodes, fd: WasiFd) -> Result<(), Errno> { + pub(crate) fn close_fd(&self, fd: WasiFd) -> Result<(), Errno> { let mut fd_map = self.fd_map.write().unwrap(); - self.close_fd_ext(inodes, &mut fd_map, fd) - } - - /// Closes an open FD, handling all details such as FD being preopen - pub(crate) fn close_fd_ext( - &self, - inodes: &WasiInodes, - fd_map: &mut RwLockWriteGuard>, - fd: WasiFd, - ) -> Result<(), Errno> { - let pfd = fd_map.remove(&fd).ok_or(Errno::Badf)?; - let ref_cnt = pfd.ref_cnt.fetch_sub(1, Ordering::AcqRel); - if ref_cnt > 1 { - trace!( - "closing file descriptor({}) - weak - ref-cnt={}", - fd, - ref_cnt - ); - return Ok(()); - } - trace!("closing file descriptor({}) - inode", fd); - - let inode = pfd.inode; - let inodeval = inodes.get_inodeval(inode)?; - let is_preopened = inodeval.is_preopened; - let mut guard = inodeval.write(); - match guard.deref_mut() { - Kind::File { ref mut handle, .. } => { - let mut empty_handle = None; - std::mem::swap(handle, &mut empty_handle); + let pfd = fd_map.remove(&fd).ok_or(Errno::Badf); + match pfd { + Ok(fd_ref) => { + let inode = fd_ref.inode.ino().as_u64(); + trace!(%fd, %inode, "closing file descriptor"); } - Kind::Socket { ref mut socket, .. } => { - let mut closed_socket = InodeSocket::new(InodeSocketKind::Closed); - std::mem::swap(socket, &mut closed_socket); + Err(err) => { + trace!(%fd, "closing file descriptor failed - {}", err); } - Kind::Pipe { ref mut pipe } => { - pipe.close(); - } - Kind::Dir { parent, path, .. } => { - debug!("Closing dir {:?}", &path); - let key = path - .file_name() - .ok_or(Errno::Inval)? - .to_string_lossy() - .to_string(); - if let Some(p) = *parent { - drop(guard); - let mut guard = inodes.arena[p].write(); - match guard.deref_mut() { - Kind::Dir { entries, .. } | Kind::Root { entries } => { - fd_map.remove(&fd); - if is_preopened { - let mut idx = None; - { - let preopen_fds = self.preopen_fds.read().unwrap(); - for (i, po_fd) in preopen_fds.iter().enumerate() { - if *po_fd == fd { - idx = Some(i); - break; - } - } - } - if let Some(i) = idx { - // only remove entry properly if this is the original preopen FD - // calling `path_open` can give you an fd to the same inode as a preopen fd - entries.remove(&key); - self.preopen_fds.write().unwrap().remove(i); - // Maybe recursively closes fds if original preopen? - } - } - } - _ => unreachable!( - "Fatal internal logic error, directory's parent is not a directory" - ), - } - } else { - // this shouldn't be possible anymore due to Root - debug!("HIT UNREACHABLE CODE! Non-root directory does not have a parent"); - return Err(Errno::Inval); - } - } - Kind::EventNotifications { .. } => {} - Kind::Root { .. } => return Err(Errno::Access), - Kind::Symlink { .. } | Kind::Buffer { .. } => return Err(Errno::Inval), } - Ok(()) } } diff --git a/lib/wasi/src/macros.rs b/lib/wasi/src/macros.rs index a6f5ad542ae..cff8d64a00e 100644 --- a/lib/wasi/src/macros.rs +++ b/lib/wasi/src/macros.rs @@ -86,6 +86,21 @@ macro_rules! wasi_try_bus_ok { }}; } +/// Like the `try!` macro or `?` syntax: returns the value if the computation +/// succeeded or returns the error value. +#[allow(unused_macros)] +macro_rules! wasi_try_bus_ok_ok { + ($expr:expr) => {{ + let res: Result<_, crate::BusErrno> = $expr; + match res { + Ok(val) => val + Err(err) => { + return Ok(Err(err)); + } + } + }}; +} + /// Like `wasi_try` but converts a `MemoryAccessError` to a `wasi::Errno`. macro_rules! wasi_try_mem { ($expr:expr) => {{ @@ -109,6 +124,14 @@ macro_rules! wasi_try_mem_bus_ok { }}; } +/// Like `wasi_try` but converts a `MemoryAccessError` to a __bus_errno_t`. +#[allow(unused_macros)] +macro_rules! wasi_try_mem_bus_ok_ok { + ($expr:expr) => {{ + wasi_try_bus_ok_ok!($expr.map_err($crate::mem_error_to_bus)) + }}; +} + /// Like `wasi_try` but converts a `MemoryAccessError` to a `wasi::Errno`. macro_rules! wasi_try_mem_ok { ($expr:expr) => {{ @@ -120,6 +143,17 @@ macro_rules! wasi_try_mem_ok { }}; } +/// Like `wasi_try` but converts a `MemoryAccessError` to a `wasi::Errno`. +macro_rules! wasi_try_mem_ok_ok { + ($expr:expr) => {{ + wasi_try_ok_ok!($expr.map_err($crate::mem_error_to_wasi)) + }}; + + ($expr:expr, $thread:expr) => {{ + wasi_try_ok_ok!($expr.map_err($crate::mem_error_to_wasi), $thread) + }}; +} + /// Reads a string from Wasm memory. macro_rules! get_input_str { ($memory:expr, $data:expr, $len:expr) => {{ diff --git a/lib/wasi/src/net/mod.rs b/lib/wasi/src/net/mod.rs index 45f21662001..5d84b77d7c8 100644 --- a/lib/wasi/src/net/mod.rs +++ b/lib/wasi/src/net/mod.rs @@ -386,6 +386,42 @@ pub fn net_error_into_wasi_err(net_error: NetworkError) -> Errno { NetworkError::UnexpectedEof => Errno::Proto, NetworkError::WouldBlock => Errno::Again, NetworkError::WriteZero => Errno::Nospc, + NetworkError::OsError(code) => match code { + libc::EPERM => Errno::Perm, + libc::ENOENT => Errno::Noent, + libc::ESRCH => Errno::Srch, + libc::EINTR => Errno::Intr, + libc::EIO => Errno::Io, + libc::ENXIO => Errno::Nxio, + libc::E2BIG => Errno::Toobig, + libc::ENOEXEC => Errno::Noexec, + libc::EBADF => Errno::Badf, + libc::ECHILD => Errno::Badf, + libc::EAGAIN => Errno::Again, + libc::ENOMEM => Errno::Badf, + libc::EACCES => Errno::Access, + libc::EFAULT => Errno::Fault, + libc::EBUSY => Errno::Busy, + libc::EEXIST => Errno::Exist, + libc::EXDEV => Errno::Xdev, + libc::ENODEV => Errno::Nodev, + libc::ENOTDIR => Errno::Notdir, + libc::EISDIR => Errno::Isdir, + libc::EINVAL => Errno::Inval, + libc::ENFILE => Errno::Nfile, + libc::EMFILE => Errno::Mfile, + libc::ENOTTY => Errno::Notty, + libc::ETXTBSY => Errno::Txtbsy, + libc::EFBIG => Errno::Fbig, + libc::ENOSPC => Errno::Nospc, + libc::ESPIPE => Errno::Spipe, + libc::EROFS => Errno::Rofs, + libc::EMLINK => Errno::Mlink, + libc::EPIPE => Errno::Pipe, + libc::EDOM => Errno::Dom, + libc::ERANGE => Errno::Range, + _ => Errno::Io, + }, NetworkError::Unsupported => Errno::Notsup, NetworkError::UnknownError => Errno::Io, } diff --git a/lib/wasi/src/net/socket.rs b/lib/wasi/src/net/socket.rs index 1e6add59d67..197c85cd092 100644 --- a/lib/wasi/src/net/socket.rs +++ b/lib/wasi/src/net/socket.rs @@ -66,7 +66,6 @@ pub enum InodeSocketKind { socket: Box, peer: Option, }, - Closed, } pub enum WasiSocketOption { @@ -154,20 +153,39 @@ pub enum TimeType { #[derive(Debug)] //#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] -pub(crate) struct InodeSocketInner { +pub(crate) struct InodeSocketProtected { pub kind: InodeSocketKind, + pub notifications: InodeSocketNotifications, +} + +#[derive(Debug, Default)] +//#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] +pub(crate) struct InodeSocketNotifications { + pub closed: bool, + pub failed: bool, +} + +#[derive(Debug)] +//#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] +pub(crate) struct InodeSocketInner { + pub protected: RwLock, } #[derive(Debug, Clone)] //#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] pub struct InodeSocket { - pub(crate) inner: Arc>, + pub(crate) inner: Arc, } impl InodeSocket { pub fn new(kind: InodeSocketKind) -> Self { Self { - inner: Arc::new(RwLock::new(InodeSocketInner { kind })), + inner: Arc::new(InodeSocketInner { + protected: RwLock::new(InodeSocketProtected { + kind, + notifications: Default::default(), + }), + }), } } @@ -184,7 +202,7 @@ impl InodeSocket { .unwrap_or(Duration::from_secs(30)); let socket = { - let mut inner = self.inner.write().unwrap(); + let mut inner = self.inner.protected.write().unwrap(); match &mut inner.kind { InodeSocketKind::PreSocket { family, @@ -255,7 +273,7 @@ impl InodeSocket { .unwrap_or(Duration::from_secs(30)); let socket = { - let inner = self.inner.read().unwrap(); + let inner = self.inner.protected.read().unwrap(); match &inner.kind { InodeSocketKind::PreSocket { ty, @@ -283,10 +301,6 @@ impl InodeSocket { return Err(Errno::Notsup); } }, - InodeSocketKind::Closed => { - tracing::warn!("wasi[?]::sock_listen - failed - socket closed"); - return Err(Errno::Io); - } _ => { tracing::warn!("wasi[?]::sock_listen - failed - not supported(2)"); return Err(Errno::Notsup); @@ -328,7 +342,7 @@ impl InodeSocket { self: Pin<&mut Self>, cx: &mut std::task::Context<'_>, ) -> std::task::Poll { - let mut inner = self.sock.inner.write().unwrap(); + let mut inner = self.sock.inner.protected.write().unwrap(); match &mut inner.kind { InodeSocketKind::TcpListener { socket, .. } => { if self.nonblocking { @@ -342,7 +356,6 @@ impl InodeSocket { } } InodeSocketKind::PreSocket { .. } => Poll::Ready(Err(Errno::Notconn)), - InodeSocketKind::Closed => Poll::Ready(Err(Errno::Io)), _ => Poll::Ready(Err(Errno::Notsup)), } } @@ -355,7 +368,7 @@ impl InodeSocket { } pub fn close(&self) -> Result<(), Errno> { - let mut inner = self.inner.write().unwrap(); + let mut inner = self.inner.protected.write().unwrap(); match &mut inner.kind { InodeSocketKind::TcpListener { .. } => {} InodeSocketKind::TcpStream { socket, .. } => { @@ -366,7 +379,6 @@ impl InodeSocket { InodeSocketKind::WebSocket(_) => {} InodeSocketKind::Raw(_) => {} InodeSocketKind::PreSocket { .. } => return Err(Errno::Notconn), - InodeSocketKind::Closed => return Err(Errno::Notconn), }; Ok(()) } @@ -380,12 +392,12 @@ impl InodeSocket { #[derive(Debug)] struct SocketFlusher<'a> { - sock: &'a RwLock, + inner: &'a InodeSocketInner, } impl<'a> Future for SocketFlusher<'a> { type Output = Result<(), Errno>; fn poll(self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll { - let mut inner = self.sock.write().unwrap(); + let mut inner = self.inner.protected.write().unwrap(); match &mut inner.kind { InodeSocketKind::TcpListener { .. } => Poll::Ready(Ok(())), InodeSocketKind::TcpStream { socket, .. } => { @@ -396,13 +408,12 @@ impl InodeSocket { InodeSocketKind::WebSocket(_) => Poll::Ready(Ok(())), InodeSocketKind::Raw(_) => Poll::Ready(Ok(())), InodeSocketKind::PreSocket { .. } => Poll::Ready(Err(Errno::Notconn)), - InodeSocketKind::Closed => Poll::Ready(Err(Errno::Notconn)), } } } tokio::select! { - res = SocketFlusher { sock: &self.inner } => res, + res = SocketFlusher { inner: &self.inner } => res, _ = tasks.sleep_now(timeout) => Err(Errno::Timedout) } } @@ -420,7 +431,7 @@ impl InodeSocket { let timeout = timeout.unwrap_or(Duration::from_secs(30)); let connect = { - let mut inner = self.inner.write().unwrap(); + let mut inner = self.inner.protected.write().unwrap(); match &mut inner.kind { InodeSocketKind::PreSocket { ty, @@ -455,7 +466,6 @@ impl InodeSocket { target_peer.replace(peer); return Ok(None); } - InodeSocketKind::Closed => return Err(Errno::Io), _ => return Err(Errno::Notsup), } }; @@ -472,20 +482,19 @@ impl InodeSocket { } pub fn status(&self) -> Result { - let inner = self.inner.read().unwrap(); + let inner = self.inner.protected.read().unwrap(); Ok(match &inner.kind { InodeSocketKind::PreSocket { .. } => WasiSocketStatus::Opening, InodeSocketKind::WebSocket(_) => WasiSocketStatus::Opened, InodeSocketKind::TcpListener { .. } => WasiSocketStatus::Opened, InodeSocketKind::TcpStream { .. } => WasiSocketStatus::Opened, InodeSocketKind::UdpSocket { .. } => WasiSocketStatus::Opened, - InodeSocketKind::Closed => WasiSocketStatus::Closed, _ => WasiSocketStatus::Failed, }) } pub fn addr_local(&self) -> Result { - let inner = self.inner.read().unwrap(); + let inner = self.inner.protected.read().unwrap(); Ok(match &inner.kind { InodeSocketKind::PreSocket { family, addr, .. } => { if let Some(addr) = addr { @@ -511,13 +520,12 @@ impl InodeSocket { InodeSocketKind::UdpSocket { socket, .. } => { socket.addr_local().map_err(net_error_into_wasi_err)? } - InodeSocketKind::Closed => return Err(Errno::Io), _ => return Err(Errno::Notsup), }) } pub fn addr_peer(&self) -> Result { - let inner = self.inner.read().unwrap(); + let inner = self.inner.protected.read().unwrap(); Ok(match &inner.kind { InodeSocketKind::PreSocket { family, .. } => SocketAddr::new( match *family { @@ -548,13 +556,12 @@ impl InodeSocket { ) }) })?, - InodeSocketKind::Closed => return Err(Errno::Io), _ => return Err(Errno::Notsup), }) } pub fn set_opt_flag(&mut self, option: WasiSocketOption, val: bool) -> Result<(), Errno> { - let mut inner = self.inner.write().unwrap(); + let mut inner = self.inner.protected.write().unwrap(); match &mut inner.kind { InodeSocketKind::PreSocket { only_v6, @@ -593,14 +600,13 @@ impl InodeSocket { .map_err(net_error_into_wasi_err)?, _ => return Err(Errno::Inval), }, - InodeSocketKind::Closed => return Err(Errno::Io), _ => return Err(Errno::Notsup), } Ok(()) } pub fn get_opt_flag(&self, option: WasiSocketOption) -> Result { - let mut inner = self.inner.write().unwrap(); + let mut inner = self.inner.protected.write().unwrap(); Ok(match &mut inner.kind { InodeSocketKind::PreSocket { only_v6, @@ -635,13 +641,12 @@ impl InodeSocket { .map_err(net_error_into_wasi_err)?, _ => return Err(Errno::Inval), }, - InodeSocketKind::Closed => return Err(Errno::Io), _ => return Err(Errno::Notsup), }) } pub fn set_send_buf_size(&mut self, size: usize) -> Result<(), Errno> { - let mut inner = self.inner.write().unwrap(); + let mut inner = self.inner.protected.write().unwrap(); match &mut inner.kind { InodeSocketKind::PreSocket { send_buf_size, .. } => { *send_buf_size = Some(size); @@ -651,14 +656,13 @@ impl InodeSocket { .set_send_buf_size(size) .map_err(net_error_into_wasi_err)?; } - InodeSocketKind::Closed => return Err(Errno::Io), _ => return Err(Errno::Notsup), } Ok(()) } pub fn send_buf_size(&self) -> Result { - let inner = self.inner.read().unwrap(); + let inner = self.inner.protected.read().unwrap(); match &inner.kind { InodeSocketKind::PreSocket { send_buf_size, .. } => { Ok((*send_buf_size).unwrap_or_default()) @@ -666,13 +670,12 @@ impl InodeSocket { InodeSocketKind::TcpStream { socket, .. } => { socket.send_buf_size().map_err(net_error_into_wasi_err) } - InodeSocketKind::Closed => Err(Errno::Io), _ => Err(Errno::Notsup), } } pub fn set_recv_buf_size(&mut self, size: usize) -> Result<(), Errno> { - let mut inner = self.inner.write().unwrap(); + let mut inner = self.inner.protected.write().unwrap(); match &mut inner.kind { InodeSocketKind::PreSocket { recv_buf_size, .. } => { *recv_buf_size = Some(size); @@ -682,14 +685,13 @@ impl InodeSocket { .set_recv_buf_size(size) .map_err(net_error_into_wasi_err)?; } - InodeSocketKind::Closed => return Err(Errno::Io), _ => return Err(Errno::Notsup), } Ok(()) } pub fn recv_buf_size(&self) -> Result { - let inner = self.inner.read().unwrap(); + let inner = self.inner.protected.read().unwrap(); match &inner.kind { InodeSocketKind::PreSocket { recv_buf_size, .. } => { Ok((*recv_buf_size).unwrap_or_default()) @@ -697,31 +699,28 @@ impl InodeSocket { InodeSocketKind::TcpStream { socket, .. } => { socket.recv_buf_size().map_err(net_error_into_wasi_err) } - InodeSocketKind::Closed => Err(Errno::Io), _ => Err(Errno::Notsup), } } pub fn set_linger(&mut self, linger: Option) -> Result<(), Errno> { - let mut inner = self.inner.write().unwrap(); + let mut inner = self.inner.protected.write().unwrap(); match &mut inner.kind { InodeSocketKind::TcpStream { socket, .. } => { socket.set_linger(linger).map_err(net_error_into_wasi_err) } InodeSocketKind::PreSocket { .. } => Err(Errno::Io), - InodeSocketKind::Closed => Err(Errno::Io), _ => Err(Errno::Notsup), } } pub fn linger(&self) -> Result, Errno> { - let inner = self.inner.read().unwrap(); + let inner = self.inner.protected.read().unwrap(); match &inner.kind { InodeSocketKind::TcpStream { socket, .. } => { socket.linger().map_err(net_error_into_wasi_err) } InodeSocketKind::PreSocket { .. } => Err(Errno::Io), - InodeSocketKind::Closed => Err(Errno::Io), _ => Err(Errno::Notsup), } } @@ -731,7 +730,7 @@ impl InodeSocket { ty: TimeType, timeout: Option, ) -> Result<(), Errno> { - let mut inner = self.inner.write().unwrap(); + let mut inner = self.inner.protected.write().unwrap(); match &mut inner.kind { InodeSocketKind::TcpStream { write_timeout, @@ -768,13 +767,12 @@ impl InodeSocket { } Ok(()) } - InodeSocketKind::Closed => Err(Errno::Io), _ => Err(Errno::Notsup), } } pub fn opt_time(&self, ty: TimeType) -> Result, Errno> { - let inner = self.inner.read().unwrap(); + let inner = self.inner.protected.read().unwrap(); match &inner.kind { InodeSocketKind::TcpStream { read_timeout, @@ -802,13 +800,12 @@ impl InodeSocket { TimeType::WriteTimeout => Ok(*write_timeout), _ => Err(Errno::Inval), }, - InodeSocketKind::Closed => Err(Errno::Io), _ => Err(Errno::Notsup), } } pub fn set_ttl(&self, ttl: u32) -> Result<(), Errno> { - let mut inner = self.inner.write().unwrap(); + let mut inner = self.inner.protected.write().unwrap(); match &mut inner.kind { InodeSocketKind::TcpStream { socket, .. } => { socket.set_ttl(ttl).map_err(net_error_into_wasi_err) @@ -817,13 +814,12 @@ impl InodeSocket { socket.set_ttl(ttl).map_err(net_error_into_wasi_err) } InodeSocketKind::PreSocket { .. } => Err(Errno::Io), - InodeSocketKind::Closed => Err(Errno::Io), _ => Err(Errno::Notsup), } } pub fn ttl(&self) -> Result { - let inner = self.inner.read().unwrap(); + let inner = self.inner.protected.read().unwrap(); match &inner.kind { InodeSocketKind::TcpStream { socket, .. } => { socket.ttl().map_err(net_error_into_wasi_err) @@ -832,79 +828,72 @@ impl InodeSocket { socket.ttl().map_err(net_error_into_wasi_err) } InodeSocketKind::PreSocket { .. } => Err(Errno::Io), - InodeSocketKind::Closed => Err(Errno::Io), _ => Err(Errno::Notsup), } } pub fn set_multicast_ttl_v4(&self, ttl: u32) -> Result<(), Errno> { - let mut inner = self.inner.write().unwrap(); + let mut inner = self.inner.protected.write().unwrap(); match &mut inner.kind { InodeSocketKind::UdpSocket { socket, .. } => socket .set_multicast_ttl_v4(ttl) .map_err(net_error_into_wasi_err), InodeSocketKind::PreSocket { .. } => Err(Errno::Io), - InodeSocketKind::Closed => Err(Errno::Io), _ => Err(Errno::Notsup), } } pub fn multicast_ttl_v4(&self) -> Result { - let inner = self.inner.read().unwrap(); + let inner = self.inner.protected.read().unwrap(); match &inner.kind { InodeSocketKind::UdpSocket { socket, .. } => { socket.multicast_ttl_v4().map_err(net_error_into_wasi_err) } InodeSocketKind::PreSocket { .. } => Err(Errno::Io), - InodeSocketKind::Closed => Err(Errno::Io), _ => Err(Errno::Notsup), } } pub fn join_multicast_v4(&self, multiaddr: Ipv4Addr, iface: Ipv4Addr) -> Result<(), Errno> { - let mut inner = self.inner.write().unwrap(); + let mut inner = self.inner.protected.write().unwrap(); match &mut inner.kind { InodeSocketKind::UdpSocket { socket, .. } => socket .join_multicast_v4(multiaddr, iface) .map_err(net_error_into_wasi_err), InodeSocketKind::PreSocket { .. } => Err(Errno::Io), - InodeSocketKind::Closed => Err(Errno::Io), _ => Err(Errno::Notsup), } } pub fn leave_multicast_v4(&self, multiaddr: Ipv4Addr, iface: Ipv4Addr) -> Result<(), Errno> { - let mut inner = self.inner.write().unwrap(); + let mut inner = self.inner.protected.write().unwrap(); match &mut inner.kind { InodeSocketKind::UdpSocket { socket, .. } => socket .leave_multicast_v4(multiaddr, iface) .map_err(net_error_into_wasi_err), InodeSocketKind::PreSocket { .. } => Err(Errno::Io), - InodeSocketKind::Closed => Err(Errno::Io), _ => Err(Errno::Notsup), } } pub fn join_multicast_v6(&self, multiaddr: Ipv6Addr, iface: u32) -> Result<(), Errno> { - let mut inner = self.inner.write().unwrap(); + let mut inner = self.inner.protected.write().unwrap(); match &mut inner.kind { InodeSocketKind::UdpSocket { socket, .. } => socket .join_multicast_v6(multiaddr, iface) .map_err(net_error_into_wasi_err), InodeSocketKind::PreSocket { .. } => Err(Errno::Io), - InodeSocketKind::Closed => Err(Errno::Io), _ => Err(Errno::Notsup), } } pub fn leave_multicast_v6(&mut self, multiaddr: Ipv6Addr, iface: u32) -> Result<(), Errno> { - let mut inner = self.inner.write().unwrap(); + let mut inner = self.inner.protected.write().unwrap(); match &mut inner.kind { InodeSocketKind::UdpSocket { socket, .. } => socket .leave_multicast_v6(multiaddr, iface) .map_err(net_error_into_wasi_err), InodeSocketKind::PreSocket { .. } => Err(Errno::Io), - InodeSocketKind::Closed => Err(Errno::Io), _ => Err(Errno::Notsup), } } @@ -924,14 +913,14 @@ impl InodeSocket { #[derive(Debug)] struct SocketSender<'a, 'b> { - sock: &'a RwLock, + inner: &'a InodeSocketInner, data: &'b [u8], nonblocking: bool, } impl<'a, 'b> Future for SocketSender<'a, 'b> { type Output = Result; fn poll(self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll { - let mut inner = self.sock.write().unwrap(); + let mut inner = self.inner.protected.write().unwrap(); match &mut inner.kind { InodeSocketKind::WebSocket(sock) => sock .poll_send(cx, self.data) @@ -976,14 +965,13 @@ impl InodeSocket { } } InodeSocketKind::PreSocket { .. } => Poll::Ready(Err(Errno::Notconn)), - InodeSocketKind::Closed => Poll::Ready(Err(Errno::Io)), _ => Poll::Ready(Err(Errno::Notsup)), } } } tokio::select! { - res = SocketSender { sock: &self.inner, data: buf, nonblocking } => res, + res = SocketSender { inner: &self.inner, data: buf, nonblocking } => res, _ = tasks.sleep_now(timeout) => Err(Errno::Timedout) } } @@ -1004,7 +992,7 @@ impl InodeSocket { #[derive(Debug)] struct SocketSender<'a, 'b> { - sock: &'a RwLock, + inner: &'a InodeSocketInner, data: &'b [u8], addr: SocketAddr, nonblocking: bool, @@ -1012,7 +1000,7 @@ impl InodeSocket { impl<'a, 'b> Future for SocketSender<'a, 'b> { type Output = Result; fn poll(self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll { - let mut inner = self.sock.write().unwrap(); + let mut inner = self.inner.protected.write().unwrap(); match &mut inner.kind { InodeSocketKind::Icmp(sock) => { if self.nonblocking { @@ -1038,14 +1026,13 @@ impl InodeSocket { } } InodeSocketKind::PreSocket { .. } => Poll::Ready(Err(Errno::Notconn)), - InodeSocketKind::Closed => Poll::Ready(Err(Errno::Io)), _ => Poll::Ready(Err(Errno::Notsup)), } } } tokio::select! { - res = SocketSender { sock: &self.inner, data: buf, addr, nonblocking } => res, + res = SocketSender { inner: &self.inner, data: buf, addr, nonblocking } => res, _ = tasks.sleep_now(timeout) => Err(Errno::Timedout) } } @@ -1065,7 +1052,7 @@ impl InodeSocket { #[derive(Debug)] struct SocketReceiver<'a, 'b> { - sock: &'a RwLock, + inner: &'a InodeSocketInner, data: &'b mut [MaybeUninit], nonblocking: bool, } @@ -1075,7 +1062,7 @@ impl InodeSocket { mut self: Pin<&mut Self>, cx: &mut std::task::Context<'_>, ) -> Poll { - let mut inner = self.sock.write().unwrap(); + let mut inner = self.inner.protected.write().unwrap(); match &mut inner.kind { InodeSocketKind::WebSocket(sock) => sock .poll_recv(cx, self.data) @@ -1132,14 +1119,13 @@ impl InodeSocket { } } InodeSocketKind::PreSocket { .. } => Poll::Ready(Err(Errno::Notconn)), - InodeSocketKind::Closed => Poll::Ready(Err(Errno::Io)), _ => Poll::Ready(Err(Errno::Notsup)), } } } tokio::select! { - res = SocketReceiver { sock: &self.inner, data: buf, nonblocking } => res, + res = SocketReceiver { inner: &self.inner, data: buf, nonblocking } => res, _ = tasks.sleep_now(timeout) => Err(Errno::Timedout) } } @@ -1159,7 +1145,7 @@ impl InodeSocket { #[derive(Debug)] struct SocketReceiver<'a, 'b> { - sock: &'a RwLock, + inner: &'a InodeSocketInner, data: &'b mut [MaybeUninit], nonblocking: bool, } @@ -1169,7 +1155,7 @@ impl InodeSocket { mut self: Pin<&mut Self>, cx: &mut std::task::Context<'_>, ) -> Poll { - let mut inner = self.sock.write().unwrap(); + let mut inner = self.inner.protected.write().unwrap(); match &mut inner.kind { InodeSocketKind::Icmp(sock) => { if self.nonblocking { @@ -1195,33 +1181,31 @@ impl InodeSocket { } } InodeSocketKind::PreSocket { .. } => Poll::Ready(Err(Errno::Notconn)), - InodeSocketKind::Closed => Poll::Ready(Err(Errno::Io)), _ => Poll::Ready(Err(Errno::Notsup)), } } } tokio::select! { - res = SocketReceiver { sock: &self.inner, data: buf, nonblocking } => res, + res = SocketReceiver { inner: &self.inner, data: buf, nonblocking } => res, _ = tasks.sleep_now(timeout) => Err(Errno::Timedout) } } pub fn shutdown(&mut self, how: std::net::Shutdown) -> Result<(), Errno> { - let mut inner = self.inner.write().unwrap(); + let mut inner = self.inner.protected.write().unwrap(); match &mut inner.kind { InodeSocketKind::TcpStream { socket, .. } => { socket.shutdown(how).map_err(net_error_into_wasi_err)?; } InodeSocketKind::PreSocket { .. } => return Err(Errno::Notconn), - InodeSocketKind::Closed => return Err(Errno::Io), _ => return Err(Errno::Notsup), } Ok(()) } pub async fn can_write(&self) -> bool { - if let Ok(mut guard) = self.inner.try_write() { + if let Ok(mut guard) = self.inner.protected.try_write() { #[allow(clippy::match_like_matches_macro)] match &mut guard.kind { InodeSocketKind::TcpStream { .. } @@ -1236,7 +1220,7 @@ impl InodeSocket { } } -impl InodeSocketInner { +impl InodeSocketProtected { pub fn poll_read_ready( &mut self, cx: &mut std::task::Context<'_>, @@ -1251,9 +1235,6 @@ impl InodeSocketInner { InodeSocketKind::PreSocket { .. } => { std::task::Poll::Ready(Err(wasmer_vnet::NetworkError::IOError)) } - InodeSocketKind::Closed => { - std::task::Poll::Ready(Err(wasmer_vnet::NetworkError::ConnectionAborted)) - } } } @@ -1271,9 +1252,6 @@ impl InodeSocketInner { InodeSocketKind::PreSocket { .. } => { std::task::Poll::Ready(Err(wasmer_vnet::NetworkError::IOError)) } - InodeSocketKind::Closed => { - std::task::Poll::Ready(Err(wasmer_vnet::NetworkError::ConnectionAborted)) - } } } } diff --git a/lib/wasi/src/os/command/builtins/cmd_wasmer.rs b/lib/wasi/src/os/command/builtins/cmd_wasmer.rs index 5eb492a92de..16fdfad6f2e 100644 --- a/lib/wasi/src/os/command/builtins/cmd_wasmer.rs +++ b/lib/wasi/src/os/command/builtins/cmd_wasmer.rs @@ -65,7 +65,7 @@ impl CmdWasmer { let mut config = config.take().ok_or(VirtualBusError::UnknownError)?.conf(); // Set the arguments of the environment by replacing the state - let mut state = config.env().state.fork(true); + let mut state = config.env().state.fork(); args.insert(0, what.clone()); state.args = args; config.env_mut().state = Arc::new(state); diff --git a/lib/wasi/src/state/builder.rs b/lib/wasi/src/state/builder.rs index a10f04270d7..13da8b2592e 100644 --- a/lib/wasi/src/state/builder.rs +++ b/lib/wasi/src/state/builder.rs @@ -2,12 +2,10 @@ use std::{ collections::HashMap, - ops::{Deref, DerefMut}, path::{Path, PathBuf}, - sync::{Arc, RwLock}, + sync::Arc, }; -use generational_arena::Arena; use rand::Rng; use thiserror::Error; use wasmer::AsStoreMut; @@ -49,7 +47,7 @@ pub struct WasiStateBuilder { vfs_preopens: Vec, compiled_modules: Arc, #[allow(clippy::type_complexity)] - setup_fs_fn: Option Result<(), String> + Send>>, + setup_fs_fn: Option Result<(), String> + Send>>, stdout_override: Option>, stderr_override: Option>, stdin_override: Option>, @@ -109,7 +107,7 @@ fn validate_mapped_dir_alias(alias: &str) -> Result<(), WasiStateCreationError> Ok(()) } -pub type SetupFsFn = Box Result<(), String> + Send>; +pub type SetupFsFn = Box Result<(), String> + Send>; // TODO add other WasiFS APIs here like swapping out stdout, for example (though we need to // return stdout somehow, it's unclear what that API should look like) @@ -510,46 +508,35 @@ impl WasiStateBuilder { .unwrap_or_else(|| WasiFsRoot::Sandbox(Arc::new(TmpFileSystem::new()))); // self.preopens are checked in [`PreopenDirBuilder::build`] - let inodes = RwLock::new(crate::state::WasiInodes { - arena: Arena::new(), - orphan_fds: HashMap::new(), - }); + let inodes = crate::state::WasiInodes::new(); let wasi_fs = { - let mut inodes = inodes.write().unwrap(); - // self.preopens are checked in [`PreopenDirBuilder::build`] - let mut wasi_fs = WasiFs::new_with_preopen( - inodes.deref_mut(), - &self.preopens, - &self.vfs_preopens, - fs_backing, - ) - .map_err(WasiStateCreationError::WasiFsCreationError)?; + let mut wasi_fs = + WasiFs::new_with_preopen(&inodes, &self.preopens, &self.vfs_preopens, fs_backing) + .map_err(WasiStateCreationError::WasiFsCreationError)?; // set up the file system, overriding base files and calling the setup function wasi_fs - .swap_file(inodes.deref(), __WASI_STDIN_FILENO, stdin) + .swap_file(__WASI_STDIN_FILENO, stdin) .map_err(WasiStateCreationError::FileSystemError)?; if let Some(stdout_override) = self.stdout_override.take() { wasi_fs - .swap_file(inodes.deref(), __WASI_STDOUT_FILENO, stdout_override) + .swap_file(__WASI_STDOUT_FILENO, stdout_override) .map_err(WasiStateCreationError::FileSystemError)?; } if let Some(stderr_override) = self.stderr_override.take() { wasi_fs - .swap_file(inodes.deref(), __WASI_STDERR_FILENO, stderr_override) + .swap_file(__WASI_STDERR_FILENO, stderr_override) .map_err(WasiStateCreationError::FileSystemError)?; } if let Some(f) = &self.setup_fs_fn { - f(inodes.deref_mut(), &mut wasi_fs) - .map_err(WasiStateCreationError::WasiFsSetupError)?; + f(&inodes, &mut wasi_fs).map_err(WasiStateCreationError::WasiFsSetupError)?; } wasi_fs }; - let inodes = Arc::new(inodes); Ok(WasiState { fs: wasi_fs, diff --git a/lib/wasi/src/state/env.rs b/lib/wasi/src/state/env.rs index e7af10fd8ed..38671781bfa 100644 --- a/lib/wasi/src/state/env.rs +++ b/lib/wasi/src/state/env.rs @@ -1,7 +1,4 @@ -use std::{ - ops::Deref, - sync::{Arc, RwLockReadGuard, RwLockWriteGuard}, -}; +use std::{ops::Deref, sync::Arc}; use derivative::Derivative; use tracing::{trace, warn}; @@ -200,6 +197,8 @@ pub struct WasiEnv { pub stack_base: 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 /// executing WASI program can see. pub state: Arc, @@ -234,7 +233,7 @@ impl WasiEnv { let thread = handle.as_thread(); thread.copy_stack_from(&self.thread); - let state = Arc::new(self.state.fork(true)); + let state = Arc::new(self.state.fork()); let bin_factory = { let mut bin_factory = self.bin_factory.clone(); @@ -246,6 +245,7 @@ impl WasiEnv { process, thread, vfork: None, + poll_seed: 0, stack_base: self.stack_base, stack_start: self.stack_start, bin_factory, @@ -293,6 +293,7 @@ impl WasiEnv { process, thread: thread.as_thread(), vfork: None, + poll_seed: 0, stack_base: DEFAULT_STACK_SIZE, stack_start: 0, state, @@ -529,21 +530,10 @@ impl WasiEnv { &'a self, store: &'a impl AsStoreRef, _mem_index: u32, - ) -> (MemoryView<'a>, &WasiState, RwLockReadGuard) { - let memory = self.memory_view(store); - let state = self.state.deref(); - let inodes = state.inodes.read().unwrap(); - (memory, state, inodes) - } - - pub(crate) fn get_memory_and_wasi_state_and_inodes_mut<'a>( - &'a self, - store: &'a impl AsStoreRef, - _mem_index: u32, - ) -> (MemoryView<'a>, &WasiState, RwLockWriteGuard) { + ) -> (MemoryView<'a>, &WasiState, &WasiInodes) { let memory = self.memory_view(store); let state = self.state.deref(); - let inodes = state.inodes.write().unwrap(); + let inodes = &state.inodes; (memory, state, inodes) } @@ -693,9 +683,7 @@ impl WasiEnv { // If this is the main thread then also close all the files if self.thread.is_main() { trace!("wasi[{}]:: cleaning up open file handles", self.pid()); - - let inodes = self.state.inodes.read().unwrap(); - self.state.fs.close_all(inodes.deref()); + self.state.fs.close_all(); // Now send a signal that the thread is terminated self.process.signal_process(Signal::Sigquit); diff --git a/lib/wasi/src/state/mod.rs b/lib/wasi/src/state/mod.rs index 3a246ec19e8..dbc1407fda0 100644 --- a/lib/wasi/src/state/mod.rs +++ b/lib/wasi/src/state/mod.rs @@ -31,7 +31,6 @@ use std::{ }; use derivative::Derivative; -pub use generational_arena::Index as Inode; #[cfg(feature = "enable-serde")] use serde::{Deserialize, Serialize}; use wasmer::Store; @@ -46,6 +45,7 @@ pub use self::{ func_env::WasiFunctionEnv, types::*, }; +pub use crate::fs::{InodeGuard, InodeWeakGuard}; use crate::{ fs::{fs_error_into_wasi_err, WasiFs, WasiFsRoot, WasiInodes, WasiStateFileGuard}, os::task::process::WasiProcessId, @@ -243,7 +243,7 @@ impl WasiBusState { pub struct WasiState { pub fs: WasiFs, pub secret: [u8; 32], - pub inodes: Arc>, + pub inodes: WasiInodes, // TODO: review allow... #[allow(dead_code)] pub(crate) threading: RwLock, @@ -343,9 +343,9 @@ impl WasiState { } /// Forking the WasiState is used when either fork or vfork is called - pub fn fork(&self, inc_refs: bool) -> Self { + pub fn fork(&self) -> Self { WasiState { - fs: self.fs.fork(inc_refs), + fs: self.fs.fork(), secret: self.secret, inodes: self.inodes.clone(), threading: Default::default(), diff --git a/lib/wasi/src/syscalls/mod.rs b/lib/wasi/src/syscalls/mod.rs index e0341009184..6a1c460cded 100644 --- a/lib/wasi/src/syscalls/mod.rs +++ b/lib/wasi/src/syscalls/mod.rs @@ -105,8 +105,9 @@ pub(crate) use crate::{ }, runtime::{task_manager::VirtualTaskManagerExt, SpawnType}, state::{ - self, bus_errno_into_vbus_error, iterate_poll_events, vbus_error_into_bus_errno, Inode, - PollEvent, PollEventBuilder, WasiBusCall, WasiFutex, WasiState, WasiThreadContext, + self, bus_errno_into_vbus_error, iterate_poll_events, vbus_error_into_bus_errno, + InodeGuard, InodeWeakGuard, PollEvent, PollEventBuilder, WasiBusCall, WasiFutex, WasiState, + WasiThreadContext, }, utils::{self, map_io_err}, VirtualTaskManager, WasiEnv, WasiEnvInner, WasiError, WasiFunctionEnv, @@ -118,6 +119,7 @@ use crate::{ MAX_SYMLINKS, }, utils::store::InstanceSnapshot, + WasiInodes, }; pub(crate) use crate::{net::net_error_into_wasi_err, utils::WasiParkingLot}; @@ -248,11 +250,9 @@ pub(crate) fn read_bytes( pub async fn stderr_write(ctx: &FunctionEnvMut<'_, WasiEnv>, buf: &[u8]) -> Result<(), Errno> { let env = ctx.data(); - let (memory, state, inodes) = env.get_memory_and_wasi_state_and_inodes_mut(ctx, 0); + let (memory, state, inodes) = env.get_memory_and_wasi_state_and_inodes(ctx, 0); - let mut stderr = inodes - .stderr_mut(&state.fs.fd_map) - .map_err(fs_error_into_wasi_err)?; + let mut stderr = WasiInodes::stderr_mut(&state.fs.fd_map).map_err(fs_error_into_wasi_err)?; stderr.write_all(buf).await.map_err(map_io_err) } @@ -465,19 +465,13 @@ where F: FnOnce(crate::net::socket::InodeSocket, Fd) -> Fut, Fut: std::future::Future>, { - let state = env.state.clone(); - let inodes = state.inodes.clone(); - - let fd_entry = state.fs.get_fd(sock)?; + let fd_entry = env.state.fs.get_fd(sock)?; if !rights.is_empty() && !fd_entry.rights.contains(rights) { return Err(Errno::Access); } let work = { - let inodes_guard = inodes.read().unwrap(); - let inode_idx = fd_entry.inode; - let inode = &inodes_guard.arena[inode_idx]; - + let inode = fd_entry.inode.clone(); let tasks = env.tasks.clone(); let mut guard = inode.read(); match guard.deref() { @@ -514,24 +508,18 @@ where let env = ctx.data(); let tasks = env.tasks.clone(); - let state = env.state.clone(); - let inodes = state.inodes.clone(); - - let fd_entry = state.fs.get_fd(sock)?; + let fd_entry = env.state.fs.get_fd(sock)?; if !rights.is_empty() && !fd_entry.rights.contains(rights) { return Err(Errno::Access); } - let inode_idx = fd_entry.inode; - let inodes_guard = inodes.read().unwrap(); - let inode = &inodes_guard.arena[inode_idx]; + let inode = fd_entry.inode.clone(); let mut guard = inode.write(); match guard.deref_mut() { Kind::Socket { socket } => { // Clone the socket and release the lock let socket = socket.clone(); drop(guard); - drop(inodes_guard); // Start the work using the socket let work = actor(socket, fd_entry); @@ -558,17 +546,12 @@ where let env = ctx.data(); let tasks = env.tasks.clone(); - let state = env.state.clone(); - let inodes = state.inodes.clone(); - - let fd_entry = state.fs.get_fd(sock)?; + let fd_entry = env.state.fs.get_fd(sock)?; if !rights.is_empty() && !fd_entry.rights.contains(rights) { return Err(Errno::Access); } - let inodes_guard = inodes.read().unwrap(); - let inode_idx = fd_entry.inode; - let inode = &inodes_guard.arena[inode_idx]; + let inode = fd_entry.inode.clone(); let tasks = env.tasks.clone(); let mut guard = inode.read(); @@ -602,24 +585,18 @@ where let env = ctx.data(); let tasks = env.tasks.clone(); - let state = env.state.clone(); - let inodes = state.inodes.clone(); - - let fd_entry = state.fs.get_fd(sock)?; + let fd_entry = env.state.fs.get_fd(sock)?; if !rights.is_empty() && !fd_entry.rights.contains(rights) { return Err(Errno::Access); } - let inode_idx = fd_entry.inode; - let inodes_guard = inodes.read().unwrap(); - let inode = &inodes_guard.arena[inode_idx]; + let inode = fd_entry.inode.clone(); let mut guard = inode.write(); match guard.deref_mut() { Kind::Socket { socket } => { // Clone the socket and release the lock let socket = socket.clone(); drop(guard); - drop(inodes_guard); // Start the work using the socket actor(socket, fd_entry) @@ -642,10 +619,7 @@ where Fut: std::future::Future, Errno>> + 'a, { let env = ctx.data(); - let state = env.state.clone(); - let inodes = state.inodes.clone(); - - let fd_entry = state.fs.get_fd(sock)?; + let fd_entry = env.state.fs.get_fd(sock)?; if !rights.is_empty() && !fd_entry.rights.contains(rights) { tracing::warn!( "wasi[{}:{}]::sock_upgrade(fd={}, rights={:?}) - failed - no access rights to upgrade", @@ -659,15 +633,12 @@ where let tasks = env.tasks.clone(); { - let inode_idx = fd_entry.inode; - let inodes_guard = inodes.read().unwrap(); - let inode = &inodes_guard.arena[inode_idx]; + let inode = fd_entry.inode.clone(); let mut guard = inode.write(); match guard.deref_mut() { Kind::Socket { socket } => { let socket = socket.clone(); drop(guard); - drop(inodes_guard); // Start the work using the socket let work = actor(socket); @@ -681,8 +652,6 @@ where let new_socket = rx.recv().unwrap()?; if let Some(mut new_socket) = new_socket { - let inodes_guard = inodes.read().unwrap(); - let inode = &inodes_guard.arena[inode_idx]; let mut guard = inode.write(); match guard.deref_mut() { Kind::Socket { socket } => { @@ -1096,7 +1065,7 @@ pub(crate) fn handle_rewind(ctx: &mut FunctionEnvMut<'_, WasiEnv> pub(crate) fn _prepare_wasi(wasi_env: &mut WasiEnv, args: Option>) { // Swap out the arguments with the new ones if let Some(args) = args { - let mut wasi_state = wasi_env.state.fork(false); + let mut wasi_state = wasi_env.state.fork(); wasi_state.args = args; wasi_env.state = Arc::new(wasi_state); } @@ -1120,8 +1089,7 @@ pub(crate) fn _prepare_wasi(wasi_env: &mut WasiEnv, args: Option>) { // Now close all these files for fd in close_fds { - let inodes = wasi_env.state.inodes.read().unwrap(); - let _ = wasi_env.state.fs.close_fd(inodes.deref(), fd); + let _ = wasi_env.state.fs.close_fd(fd); } } diff --git a/lib/wasi/src/syscalls/wasi/fd_allocate.rs b/lib/wasi/src/syscalls/wasi/fd_allocate.rs index a4a61241a64..8056d127483 100644 --- a/lib/wasi/src/syscalls/wasi/fd_allocate.rs +++ b/lib/wasi/src/syscalls/wasi/fd_allocate.rs @@ -22,7 +22,7 @@ pub fn fd_allocate( ctx.data().tid() ); let env = ctx.data(); - let (_, mut state, inodes) = env.get_memory_and_wasi_state_and_inodes(&ctx, 0); + let (_, mut state) = env.get_memory_and_wasi_state(&ctx, 0); let fd_entry = wasi_try!(state.fs.get_fd(fd)); let inode = fd_entry.inode; @@ -31,7 +31,7 @@ pub fn fd_allocate( } let new_size = wasi_try!(offset.checked_add(len).ok_or(Errno::Inval)); { - let mut guard = inodes.arena[inode].write(); + let mut guard = inode.write(); match guard.deref_mut() { Kind::File { handle, .. } => { if let Some(handle) = handle { @@ -51,7 +51,7 @@ pub fn fd_allocate( Kind::Dir { .. } | Kind::Root { .. } => return Errno::Isdir, } } - inodes.arena[inode].stat.write().unwrap().st_size = new_size; + inode.stat.write().unwrap().st_size = new_size; debug!("New file size: {}", new_size); Errno::Success diff --git a/lib/wasi/src/syscalls/wasi/fd_close.rs b/lib/wasi/src/syscalls/wasi/fd_close.rs index 86998d4c556..531e41c13c6 100644 --- a/lib/wasi/src/syscalls/wasi/fd_close.rs +++ b/lib/wasi/src/syscalls/wasi/fd_close.rs @@ -19,37 +19,10 @@ pub fn fd_close(mut ctx: FunctionEnvMut<'_, WasiEnv>, fd: WasiFd) -> Result { - let socket = socket.clone(); - drop(guard); - drop(inodes); - - __asyncify(&mut ctx, None, async move { - socket.close().map(|()| Errno::Success) - })? - .unwrap_or_else(|a| a) - } - _ => Errno::Success, - } - } else { - return Ok(Errno::Success); - } - }; let env = ctx.data(); - let (_, mut state, inodes) = env.get_memory_and_wasi_state_and_inodes(&ctx, 0); - wasi_try_ok!(state.fs.close_fd(inodes.deref(), fd)); + let (_, mut state) = env.get_memory_and_wasi_state(&ctx, 0); + wasi_try_ok!(state.fs.close_fd(fd)); - Ok(ret) + Ok(Errno::Success) } diff --git a/lib/wasi/src/syscalls/wasi/fd_datasync.rs b/lib/wasi/src/syscalls/wasi/fd_datasync.rs index a3a6585200b..04bfd4b8613 100644 --- a/lib/wasi/src/syscalls/wasi/fd_datasync.rs +++ b/lib/wasi/src/syscalls/wasi/fd_datasync.rs @@ -14,18 +14,12 @@ pub fn fd_datasync(mut ctx: FunctionEnvMut<'_, WasiEnv>, fd: WasiFd) -> Result( debug!("wasi[{}:{}]::fd_event", ctx.data().pid(), ctx.data().tid()); let env = ctx.data(); - let (memory, state, mut inodes) = env.get_memory_and_wasi_state_and_inodes_mut(&ctx, 0); + let (memory, state, mut inodes) = env.get_memory_and_wasi_state_and_inodes(&ctx, 0); - let kind = Kind::EventNotifications { - counter: Arc::new(AtomicU64::new(initial_val)), + let kind = Kind::EventNotifications(Arc::new(NotificationInner { + counter: AtomicU64::new(initial_val), + last_poll: AtomicU64::new(0), is_semaphore: flags & EVENT_FD_FLAGS_SEMAPHORE != 0, wakers: Default::default(), - immediate: Arc::new(AtomicBool::new(false)), - }; + })); let inode = state.fs.create_inode_with_default_stat( - inodes.deref_mut(), + inodes.deref(), kind, false, "event".to_string().into(), diff --git a/lib/wasi/src/syscalls/wasi/fd_fdstat_get.rs b/lib/wasi/src/syscalls/wasi/fd_fdstat_get.rs index d159460b037..cb1b9070fa2 100644 --- a/lib/wasi/src/syscalls/wasi/fd_fdstat_get.rs +++ b/lib/wasi/src/syscalls/wasi/fd_fdstat_get.rs @@ -23,7 +23,7 @@ pub fn fd_fdstat_get( ); let env = ctx.data(); let (memory, mut state, inodes) = env.get_memory_and_wasi_state_and_inodes(&ctx, 0); - let stat = wasi_try!(state.fs.fdstat(inodes.deref(), fd)); + let stat = wasi_try!(state.fs.fdstat(fd)); let buf = buf_ptr.deref(&memory); diff --git a/lib/wasi/src/syscalls/wasi/fd_fdstat_set_flags.rs b/lib/wasi/src/syscalls/wasi/fd_fdstat_set_flags.rs index 4d28449e777..63d82d8aeed 100644 --- a/lib/wasi/src/syscalls/wasi/fd_fdstat_set_flags.rs +++ b/lib/wasi/src/syscalls/wasi/fd_fdstat_set_flags.rs @@ -26,7 +26,7 @@ pub fn fd_fdstat_set_flags( let (_, mut state, inodes) = env.get_memory_and_wasi_state_and_inodes(&ctx, 0); let mut fd_map = state.fs.fd_map.write().unwrap(); let fd_entry = wasi_try_ok!(fd_map.get_mut(&fd).ok_or(Errno::Badf)); - let inode = fd_entry.inode; + let inode = fd_entry.inode.clone(); if !fd_entry.rights.contains(Rights::FD_FDSTAT_SET_FLAGS) { debug!( diff --git a/lib/wasi/src/syscalls/wasi/fd_filestat_get.rs b/lib/wasi/src/syscalls/wasi/fd_filestat_get.rs index 3a9b0f53592..d55c5b8ac48 100644 --- a/lib/wasi/src/syscalls/wasi/fd_filestat_get.rs +++ b/lib/wasi/src/syscalls/wasi/fd_filestat_get.rs @@ -42,7 +42,7 @@ pub(crate) fn fd_filestat_get_internal( return Errno::Access; } - let stat = wasi_try!(state.fs.filestat_fd(inodes.deref(), fd)); + let stat = wasi_try!(state.fs.filestat_fd(fd)); let buf = buf.deref(&memory); wasi_try_mem!(buf.write(stat)); diff --git a/lib/wasi/src/syscalls/wasi/fd_filestat_set_size.rs b/lib/wasi/src/syscalls/wasi/fd_filestat_set_size.rs index d7883317358..01024228bad 100644 --- a/lib/wasi/src/syscalls/wasi/fd_filestat_set_size.rs +++ b/lib/wasi/src/syscalls/wasi/fd_filestat_set_size.rs @@ -19,7 +19,7 @@ pub fn fd_filestat_set_size( ctx.data().tid() ); let env = ctx.data(); - let (_, mut state, inodes) = env.get_memory_and_wasi_state_and_inodes(&ctx, 0); + let (_, mut state) = env.get_memory_and_wasi_state(&ctx, 0); let fd_entry = wasi_try!(state.fs.get_fd(fd)); let inode = fd_entry.inode; @@ -28,7 +28,7 @@ pub fn fd_filestat_set_size( } { - let mut guard = inodes.arena[inode].write(); + let mut guard = inode.write(); match guard.deref_mut() { Kind::File { handle, .. } => { if let Some(handle) = handle { @@ -48,7 +48,7 @@ pub fn fd_filestat_set_size( Kind::Dir { .. } | Kind::Root { .. } => return Errno::Isdir, } } - inodes.arena[inode].stat.write().unwrap().st_size = st_size; + inode.stat.write().unwrap().st_size = st_size; Errno::Success } diff --git a/lib/wasi/src/syscalls/wasi/fd_filestat_set_times.rs b/lib/wasi/src/syscalls/wasi/fd_filestat_set_times.rs index 183ea8e0691..2bb32fa28a4 100644 --- a/lib/wasi/src/syscalls/wasi/fd_filestat_set_times.rs +++ b/lib/wasi/src/syscalls/wasi/fd_filestat_set_times.rs @@ -23,7 +23,7 @@ pub fn fd_filestat_set_times( ctx.data().tid() ); let env = ctx.data(); - let (_, mut state, inodes) = env.get_memory_and_wasi_state_and_inodes(&ctx, 0); + let (_, mut state) = env.get_memory_and_wasi_state(&ctx, 0); let fd_entry = wasi_try!(state.fs.get_fd(fd)); if !fd_entry.rights.contains(Rights::FD_FILESTAT_SET_TIMES) { @@ -36,8 +36,7 @@ pub fn fd_filestat_set_times( return Errno::Inval; } - let inode_idx = fd_entry.inode; - let inode = &inodes.arena[inode_idx]; + let inode = fd_entry.inode; if fst_flags.contains(Fstflags::SET_ATIM) || fst_flags.contains(Fstflags::SET_ATIM_NOW) { let time_to_set = if fst_flags.contains(Fstflags::SET_ATIM) { diff --git a/lib/wasi/src/syscalls/wasi/fd_prestat_dir_name.rs b/lib/wasi/src/syscalls/wasi/fd_prestat_dir_name.rs index e0fc0d7736f..0e0a2a84609 100644 --- a/lib/wasi/src/syscalls/wasi/fd_prestat_dir_name.rs +++ b/lib/wasi/src/syscalls/wasi/fd_prestat_dir_name.rs @@ -15,25 +15,24 @@ pub fn fd_prestat_dir_name( path_len ); let env = ctx.data(); - let (memory, mut state, inodes) = env.get_memory_and_wasi_state_and_inodes(&ctx, 0); + let (memory, mut state) = env.get_memory_and_wasi_state(&ctx, 0); let path_chars = wasi_try_mem!(path.slice(&memory, path_len)); - let real_inode = wasi_try!(state.fs.get_fd_inode(fd)); - let inode_val = &inodes.arena[real_inode]; + let inode = wasi_try!(state.fs.get_fd_inode(fd)); // check inode-val.is_preopened? - trace!("=> inode: {:?}", inode_val); - let guard = inode_val.read(); + trace!("=> inode: {:?}", inode); + let guard = inode.read(); match guard.deref() { Kind::Dir { .. } | Kind::Root { .. } => { // TODO: verify this: null termination, etc let path_len: u64 = path_len.into(); - if (inode_val.name.len() as u64) < path_len { + if (inode.name.len() as u64) < path_len { wasi_try_mem!(path_chars - .subslice(0..inode_val.name.len() as u64) - .write_slice(inode_val.name.as_bytes())); - wasi_try_mem!(path_chars.index(inode_val.name.len() as u64).write(0)); + .subslice(0..inode.name.len() as u64) + .write_slice(inode.name.as_bytes())); + wasi_try_mem!(path_chars.index(inode.name.len() as u64).write(0)); //trace!("=> result: \"{}\"", inode_val.name); diff --git a/lib/wasi/src/syscalls/wasi/fd_prestat_get.rs b/lib/wasi/src/syscalls/wasi/fd_prestat_get.rs index 66e3a2d70a5..918892df78b 100644 --- a/lib/wasi/src/syscalls/wasi/fd_prestat_get.rs +++ b/lib/wasi/src/syscalls/wasi/fd_prestat_get.rs @@ -21,16 +21,14 @@ pub fn fd_prestat_get( fd ); let env = ctx.data(); - let (memory, mut state, inodes) = env.get_memory_and_wasi_state_and_inodes(&ctx, 0); + let (memory, mut state) = env.get_memory_and_wasi_state(&ctx, 0); let prestat_ptr = buf.deref(&memory); wasi_try_mem!( - prestat_ptr.write(wasi_try!(state.fs.prestat_fd(inodes.deref(), fd).map_err( - |code| { - debug!("fd_prestat_get failed (fd={}) - errno={}", fd, code); - code - } - ))) + prestat_ptr.write(wasi_try!(state.fs.prestat_fd(fd).map_err(|code| { + debug!("fd_prestat_get failed (fd={}) - errno={}", fd, code); + code + }))) ); Errno::Success diff --git a/lib/wasi/src/syscalls/wasi/fd_read.rs b/lib/wasi/src/syscalls/wasi/fd_read.rs index 41f7dd7af81..7a3694c8bd9 100644 --- a/lib/wasi/src/syscalls/wasi/fd_read.rs +++ b/lib/wasi/src/syscalls/wasi/fd_read.rs @@ -1,7 +1,9 @@ +use std::{collections::VecDeque, task::Waker}; + use wasmer_vfs::AsyncReadExt; use super::*; -use crate::syscalls::*; +use crate::{fs::NotificationInner, syscalls::*}; /// ### `fd_read()` /// Read data from file descriptor @@ -35,15 +37,45 @@ pub fn fd_read( fd_entry.offset.load(Ordering::Acquire) as usize }; - let ret = fd_read_internal::(ctx, fd, iovs, iovs_len, offset, nread, true); - trace!( - %fd, - "wasi[{}:{}]::fd_read - {:?}", - pid, - tid, - ret - ); - ret + let res = fd_read_internal::(&mut ctx, fd, iovs, iovs_len, offset, nread, true)?; + + let mut ret = Errno::Success; + let bytes_read = match res { + Ok(bytes_read) => { + trace!( + %fd, + %bytes_read, + "wasi[{}:{}]::fd_read", + ctx.data().pid(), + ctx.data().tid(), + ); + bytes_read + } + Err(err) => { + let read_err = err.name(); + trace!( + %fd, + %read_err, + "wasi[{}:{}]::fd_read", + ctx.data().pid(), + ctx.data().tid(), + ); + ret = err; + 0 + } + }; + + let bytes_read: M::Offset = wasi_try_ok!(bytes_read.try_into().map_err(|_| Errno::Overflow)); + + let env = ctx.data(); + let memory = env.memory_view(&ctx); + + let env = ctx.data(); + let memory = env.memory_view(&ctx); + let nread_ref = nread.deref(&memory); + wasi_try_mem_ok!(nread_ref.write(bytes_read)); + + Ok(ret) } /// ### `fd_pread()` @@ -62,7 +94,7 @@ pub fn fd_read( /// - `size_t nread` /// The number of bytes read pub fn fd_pread( - ctx: FunctionEnvMut<'_, WasiEnv>, + mut ctx: FunctionEnvMut<'_, WasiEnv>, fd: WasiFd, iovs: WasmPtr<__wasi_iovec_t, M>, iovs_len: M::Offset, @@ -72,86 +104,100 @@ pub fn fd_pread( let pid = ctx.data().pid(); let tid = ctx.data().tid(); - let ret = fd_read_internal::(ctx, fd, iovs, iovs_len, offset as usize, nread, false); - trace!( - %fd, - %offset, - "wasi[{}:{}]::fd_pread - {:?}", - pid, - tid, - ret - ); - ret + let res = fd_read_internal::(&mut ctx, fd, iovs, iovs_len, offset as usize, nread, false)?; + + let mut ret = Errno::Success; + let bytes_read = match res { + Ok(bytes_read) => { + trace!( + %fd, + %offset, + %bytes_read, + "wasi[{}:{}]::fd_pread - {:?}", + ctx.data().pid(), + ctx.data().tid(), + ret + ); + bytes_read + } + Err(err) => { + let read_err = err.name(); + trace!( + %fd, + %offset, + %read_err, + "wasi[{}:{}]::fd_pread - {:?}", + ctx.data().pid(), + ctx.data().tid(), + ret + ); + ret = err; + 0 + } + }; + + let bytes_read: M::Offset = wasi_try_ok!(bytes_read.try_into().map_err(|_| Errno::Overflow)); + + let env = ctx.data(); + let memory = env.memory_view(&ctx); + + let env = ctx.data(); + let memory = env.memory_view(&ctx); + let nread_ref = nread.deref(&memory); + wasi_try_mem_ok!(nread_ref.write(bytes_read)); + + Ok(ret) } -/// ### `fd_pread()` -/// Read from the file at the given offset without updating the file cursor. -/// This acts like a stateless version of Seek + Read -/// Inputs: -/// - `Fd fd` -/// The file descriptor to read the data with -/// - `const __wasi_iovec_t* iovs' -/// Vectors where the data will be stored -/// - `size_t iovs_len` -/// The number of vectors to store the data into -/// - `Filesize offset` -/// The file cursor to use: the starting position from which data will be read -/// Output: -/// - `size_t nread` -/// The number of bytes read fn fd_read_internal( - mut ctx: FunctionEnvMut<'_, WasiEnv>, + ctx: &mut FunctionEnvMut<'_, WasiEnv>, fd: WasiFd, iovs: WasmPtr<__wasi_iovec_t, M>, iovs_len: M::Offset, offset: usize, nread: WasmPtr, should_update_cursor: bool, -) -> Result { - wasi_try_ok!(WasiEnv::process_signals_and_exit(&mut ctx)?); +) -> Result, WasiError> { + wasi_try_ok_ok!(WasiEnv::process_signals_and_exit(ctx)?); let mut env = ctx.data(); let state = env.state.clone(); - let inodes = state.inodes.clone(); - let fd_entry = wasi_try_ok!(state.fs.get_fd(fd)); + let fd_entry = wasi_try_ok_ok!(state.fs.get_fd(fd)); let is_stdio = fd_entry.is_stdio; let bytes_read = { if !is_stdio && !fd_entry.rights.contains(Rights::FD_READ) { // TODO: figure out the error to return when lacking rights - return Ok(Errno::Access); + return Ok(Err(Errno::Access)); } - let inode_idx = fd_entry.inode; + let inode = fd_entry.inode; let fd_flags = fd_entry.flags; let max_size = { - let memory = env.memory_view(&ctx); - let iovs_arr = wasi_try_mem_ok!(iovs.slice(&memory, iovs_len)); + let memory = env.memory_view(ctx); + let iovs_arr = wasi_try_mem_ok_ok!(iovs.slice(&memory, iovs_len)); let mut max_size = 0usize; for iovs in iovs_arr.iter() { - let iovs = wasi_try_mem_ok!(iovs.read()); + let iovs = wasi_try_mem_ok_ok!(iovs.read()); let buf_len: usize = - wasi_try_ok!(iovs.buf_len.try_into().map_err(|_| Errno::Overflow)); + wasi_try_ok_ok!(iovs.buf_len.try_into().map_err(|_| Errno::Overflow)); max_size += buf_len; } max_size }; let (bytes_read, can_update_cursor) = { - let inodes = inodes.read().unwrap(); - let inode = &inodes.arena[inode_idx]; let mut guard = inode.write(); match guard.deref_mut() { Kind::File { handle, .. } => { if let Some(handle) = handle { let handle = handle.clone(); drop(guard); - drop(inodes); - let data = wasi_try_ok!(__asyncify( - &mut ctx, + let data = wasi_try_ok_ok!(__asyncify( + ctx, if fd_flags.contains(Fdflags::NONBLOCK) { Some(Duration::ZERO) } else { @@ -166,8 +212,7 @@ fn fd_read_internal( .map_err(map_io_err)?; } - // TODO: optimize with MaybeUninit - let mut data = vec![0u8; max_size]; + let mut data = Vec::with_capacity(max_size); unsafe { data.set_len(max_size) }; let amt = handle.read(&mut data[..]).await.map_err(|err| { let err = From::::from(err); @@ -193,22 +238,21 @@ fn fd_read_internal( env = ctx.data(); let memory = env.memory_view(&ctx); - let iovs_arr = wasi_try_mem_ok!(iovs.slice(&memory, iovs_len)); - let read = wasi_try_ok!(read_bytes(&data[..], &memory, iovs_arr)); + let iovs_arr = wasi_try_mem_ok_ok!(iovs.slice(&memory, iovs_len)); + let read = wasi_try_ok_ok!(read_bytes(&data[..], &memory, iovs_arr)); (read, true) } else { - return Ok(Errno::Inval); + return Ok(Err(Errno::Badf)); } } Kind::Socket { socket } => { let socket = socket.clone(); drop(guard); - drop(inodes); let tasks = env.tasks.clone(); let res = __asyncify( - &mut ctx, + ctx, if fd_flags.contains(Fdflags::NONBLOCK) { Some(Duration::ZERO) } else { @@ -238,14 +282,14 @@ fn fd_read_internal( match res { Err(Errno::Connaborted) | Err(Errno::Connreset) => (0, false), res => { - let data = wasi_try_ok!(res); + let data = wasi_try_ok_ok!(res); env = ctx.data(); let data_len = data.len(); let mut reader = &data[..]; let memory = env.memory_view(&ctx); - let iovs_arr = wasi_try_mem_ok!(iovs.slice(&memory, iovs_len)); - let bytes_read = wasi_try_ok!( + let iovs_arr = wasi_try_mem_ok_ok!(iovs.slice(&memory, iovs_len)); + let bytes_read = wasi_try_ok_ok!( read_bytes(reader, &memory, iovs_arr).map(|_| data_len) ); (bytes_read, false) @@ -256,10 +300,9 @@ fn fd_read_internal( let mut pipe = pipe.clone(); drop(guard); - drop(inodes); - let data = wasi_try_ok!(__asyncify( - &mut ctx, + let data = wasi_try_ok_ok!(__asyncify( + ctx, if fd_flags.contains(Fdflags::NONBLOCK) { Some(Duration::ZERO) } else { @@ -284,78 +327,103 @@ fn fd_read_internal( let data_len = data.len(); let mut reader = &data[..]; - let memory = env.memory_view(&ctx); - let iovs_arr = wasi_try_mem_ok!(iovs.slice(&memory, iovs_len)); + let memory = env.memory_view(ctx); + let iovs_arr = wasi_try_mem_ok_ok!(iovs.slice(&memory, iovs_len)); let bytes_read = - wasi_try_ok!(read_bytes(reader, &memory, iovs_arr).map(|_| data_len)); + wasi_try_ok_ok!(read_bytes(reader, &memory, iovs_arr).map(|_| data_len)); (bytes_read, false) } Kind::Dir { .. } | Kind::Root { .. } => { // TODO: verify - return Ok(Errno::Isdir); + return Ok(Err(Errno::Isdir)); } - Kind::EventNotifications { - counter: ref_counter, - is_semaphore: ref_is_semaphore, - wakers: ref_wakers, - .. - } => { - let counter = Arc::clone(ref_counter); - let is_semaphore: bool = *ref_is_semaphore; - let wakers = Arc::clone(ref_wakers); - - let (tx, mut rx) = tokio::sync::mpsc::unbounded_channel(); - { - let mut guard = wakers.lock().unwrap(); - guard.push_front(tx); + Kind::EventNotifications(inner) => { + // Create a poller + struct NotifyPoller { + inner: Arc, + non_blocking: bool, } + let poller = NotifyPoller { + inner: inner.clone(), + non_blocking: fd_flags.contains(Fdflags::NONBLOCK), + }; drop(guard); - drop(inodes); - - let ret; - loop { - let val = counter.load(Ordering::Acquire); - if val > 0 { - let new_val = if is_semaphore { val - 1 } else { 0 }; - if counter - .compare_exchange(val, new_val, Ordering::AcqRel, Ordering::Acquire) - .is_ok() - { - let mut memory = env.memory_view(&ctx); - let reader = val.to_ne_bytes(); - let iovs_arr = wasi_try_mem_ok!(iovs.slice(&memory, iovs_len)); - ret = wasi_try_ok!(read_bytes(&reader[..], &memory, iovs_arr)); - break; - } else { - continue; + + // The poller will register itself for notifications and wait for the + // counter to drop + impl Future for NotifyPoller { + type Output = Result; + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + if !self.non_blocking { + let waker = cx.waker(); + let mut guard = self.inner.wakers.lock().unwrap(); + if guard.iter().any(|w| w.will_wake(waker)) == false { + guard.push_front(waker.clone()); + } } - } - // If its none blocking then exit - if fd_flags.contains(Fdflags::NONBLOCK) { - return Ok(Errno::Again); + let wake_all = || { + self.inner.last_poll.store(u64::MAX, Ordering::Release); + let mut guard = self.inner.wakers.lock().unwrap(); + while let Some(waker) = guard.pop_front() { + waker.wake(); + } + }; + + loop { + let val = self.inner.counter.load(Ordering::Acquire); + if val > 0 { + let new_val = if self.inner.is_semaphore { val - 1 } else { 0 }; + if self + .inner + .counter + .compare_exchange( + val, + new_val, + Ordering::AcqRel, + Ordering::Acquire, + ) + .is_ok() + { + wake_all(); + return Poll::Ready(Ok(val)); + } else { + continue; + } + } + + // If its none blocking then exit + if self.non_blocking { + wake_all(); + return Poll::Ready(Err(Errno::Again)); + } + + return Poll::Pending; + } } + } - // Yield until the notifications are triggered - let tasks_inner = env.tasks.clone(); - rx = wasi_try_ok!(__asyncify(&mut ctx, None, async move { - let _ = rx.recv().await; - Ok(rx) - })? + // Yield until the notifications are triggered + let tasks_inner = env.tasks.clone(); + let val = wasi_try_ok_ok!(__asyncify(ctx, None, async { poller.await })? .map_err(|err| match err { Errno::Timedout => Errno::Again, a => a, })); - env = ctx.data(); - } + env = ctx.data(); + + let mut memory = env.memory_view(ctx); + let reader = val.to_ne_bytes(); + let iovs_arr = wasi_try_mem_ok_ok!(iovs.slice(&memory, iovs_len)); + let ret = wasi_try_ok_ok!(read_bytes(&reader[..], &memory, iovs_arr)); (ret, false) } Kind::Symlink { .. } => unimplemented!("Symlinks in wasi::fd_read"), Kind::Buffer { buffer } => { - let memory = env.memory_view(&ctx); - let iovs_arr = wasi_try_mem_ok!(iovs.slice(&memory, iovs_len)); - let read = wasi_try_ok!(read_bytes(&buffer[offset..], &memory, iovs_arr)); + let memory = env.memory_view(ctx); + let iovs_arr = wasi_try_mem_ok_ok!(iovs.slice(&memory, iovs_len)); + let read = wasi_try_ok_ok!(read_bytes(&buffer[offset..], &memory, iovs_arr)); (read, true) } } @@ -364,7 +432,7 @@ fn fd_read_internal( if !is_stdio && should_update_cursor && can_update_cursor { // reborrow let mut fd_map = state.fs.fd_map.write().unwrap(); - let fd_entry = wasi_try_ok!(fd_map.get_mut(&fd).ok_or(Errno::Badf)); + let fd_entry = wasi_try_ok_ok!(fd_map.get_mut(&fd).ok_or(Errno::Badf)); let old = fd_entry .offset .fetch_add(bytes_read as u64, Ordering::AcqRel); @@ -373,19 +441,5 @@ fn fd_read_internal( bytes_read }; - let bytes_read: M::Offset = wasi_try_ok!(bytes_read.try_into().map_err(|_| Errno::Overflow)); - trace!( - "wasi[{}:{}]::fd_read: fd={},bytes_read={}", - ctx.data().pid(), - ctx.data().tid(), - fd, - bytes_read - ); - - let env = ctx.data(); - let memory = env.memory_view(&ctx); - let nread_ref = nread.deref(&memory); - wasi_try_mem_ok!(nread_ref.write(bytes_read)); - - Ok(Errno::Success) + Ok(Ok(bytes_read)) } diff --git a/lib/wasi/src/syscalls/wasi/fd_readdir.rs b/lib/wasi/src/syscalls/wasi/fd_readdir.rs index a0c6eef74a7..761de329485 100644 --- a/lib/wasi/src/syscalls/wasi/fd_readdir.rs +++ b/lib/wasi/src/syscalls/wasi/fd_readdir.rs @@ -30,7 +30,7 @@ pub fn fd_readdir( ctx.data().tid() ); let env = ctx.data(); - let (memory, mut state, inodes) = env.get_memory_and_wasi_state_and_inodes(&ctx, 0); + let (memory, mut state) = env.get_memory_and_wasi_state(&ctx, 0); // TODO: figure out how this is supposed to work; // is it supposed to pack the buffer full every time until it can't? or do one at a time? @@ -41,7 +41,7 @@ pub fn fd_readdir( let mut buf_idx = 0usize; let entries: Vec<(String, Filetype, u64)> = { - let guard = inodes.arena[working_dir.inode].read(); + let guard = working_dir.inode.read(); match guard.deref() { Kind::Dir { path, entries, .. } => { debug!("Reading dir {:?}", path); @@ -65,16 +65,12 @@ pub fn fd_readdir( )) }) .collect::, _>>()); - entry_vec.extend( - entries - .iter() - .filter(|(_, inode)| inodes.arena[**inode].is_preopened) - .map(|(name, inode)| { - let entry = &inodes.arena[*inode]; - let stat = entry.stat.read().unwrap(); - (entry.name.to_string(), stat.st_filetype, stat.st_ino) - }), - ); + entry_vec.extend(entries.iter().filter(|(_, inode)| inode.is_preopened).map( + |(name, inode)| { + let stat = inode.stat.read().unwrap(); + (inode.name.to_string(), stat.st_filetype, stat.st_ino) + }, + )); // adding . and .. special folders // TODO: inode entry_vec.push((".".to_string(), Filetype::Directory, 0)); @@ -85,17 +81,18 @@ pub fn fd_readdir( Kind::Root { entries } => { debug!("Reading root"); let sorted_entries = { - let mut entry_vec: Vec<(String, Inode)> = - entries.iter().map(|(a, b)| (a.clone(), *b)).collect(); + let mut entry_vec: Vec<(String, InodeGuard)> = entries + .iter() + .map(|(a, b)| (a.clone(), b.clone())) + .collect(); entry_vec.sort_by(|a, b| a.0.cmp(&b.0)); entry_vec }; sorted_entries .into_iter() .map(|(name, inode)| { - let entry = &inodes.arena[inode]; - let stat = entry.stat.read().unwrap(); - (format!("/{}", entry.name), stat.st_filetype, stat.st_ino) + let stat = inode.stat.read().unwrap(); + (format!("/{}", inode.name), stat.st_filetype, stat.st_ino) }) .collect() } diff --git a/lib/wasi/src/syscalls/wasi/fd_renumber.rs b/lib/wasi/src/syscalls/wasi/fd_renumber.rs index 31112b49490..7b4e1ea27ff 100644 --- a/lib/wasi/src/syscalls/wasi/fd_renumber.rs +++ b/lib/wasi/src/syscalls/wasi/fd_renumber.rs @@ -20,25 +20,18 @@ pub fn fd_renumber(ctx: FunctionEnvMut<'_, WasiEnv>, from: WasiFd, to: WasiFd) - return Errno::Success; } let env = ctx.data(); - let (_, mut state, inodes) = env.get_memory_and_wasi_state_and_inodes(&ctx, 0); + let (_, mut state) = env.get_memory_and_wasi_state(&ctx, 0); let mut fd_map = state.fs.fd_map.write().unwrap(); let fd_entry = wasi_try!(fd_map.get_mut(&from).ok_or(Errno::Badf)); - fd_entry.ref_cnt.fetch_add(1, Ordering::Acquire); let new_fd_entry = Fd { // TODO: verify this is correct - ref_cnt: fd_entry.ref_cnt.clone(), offset: fd_entry.offset.clone(), rights: fd_entry.rights_inheriting, + inode: fd_entry.inode.clone(), ..*fd_entry }; - - if let Some(fd_entry) = fd_map.get(&to).cloned() { - if fd_entry.ref_cnt.fetch_sub(1, Ordering::AcqRel) == 1 { - wasi_try!(state.fs.close_fd_ext(inodes.deref(), &mut fd_map, to)); - } - } fd_map.insert(to, new_fd_entry); Errno::Success diff --git a/lib/wasi/src/syscalls/wasi/fd_seek.rs b/lib/wasi/src/syscalls/wasi/fd_seek.rs index c7c425cd48a..09fb5761fab 100644 --- a/lib/wasi/src/syscalls/wasi/fd_seek.rs +++ b/lib/wasi/src/syscalls/wasi/fd_seek.rs @@ -33,7 +33,7 @@ pub fn fd_seek( let env = ctx.data(); let state = env.state.clone(); - let (memory, _, inodes) = env.get_memory_and_wasi_state_and_inodes(&ctx, 0); + let (memory, _) = env.get_memory_and_wasi_state(&ctx, 0); let fd_entry = wasi_try_ok!(state.fs.get_fd(fd)); if !fd_entry.rights.contains(Rights::FD_SEEK) { @@ -60,15 +60,13 @@ pub fn fd_seek( } Whence::End => { use std::io::SeekFrom; - let inode_idx = fd_entry.inode; - let mut guard = inodes.arena[inode_idx].write(); + let mut guard = fd_entry.inode.write(); let deref_mut = guard.deref_mut(); match deref_mut { Kind::File { ref mut handle, .. } => { if let Some(handle) = handle { let handle = handle.clone(); drop(guard); - drop(inodes); wasi_try_ok!(__asyncify(&mut ctx, None, async move { let mut handle = handle.write().unwrap(); diff --git a/lib/wasi/src/syscalls/wasi/fd_sync.rs b/lib/wasi/src/syscalls/wasi/fd_sync.rs index 76acae99f98..4990740638d 100644 --- a/lib/wasi/src/syscalls/wasi/fd_sync.rs +++ b/lib/wasi/src/syscalls/wasi/fd_sync.rs @@ -14,7 +14,7 @@ pub fn fd_sync(mut ctx: FunctionEnvMut<'_, WasiEnv>, fd: WasiFd) -> Result fd={}", fd); let env = ctx.data(); - let (_, mut state, inodes) = env.get_memory_and_wasi_state_and_inodes(&ctx, 0); + let (_, mut state) = env.get_memory_and_wasi_state(&ctx, 0); let fd_entry = wasi_try_ok!(state.fs.get_fd(fd)); if !fd_entry.rights.contains(Rights::FD_SYNC) { return Ok(Errno::Access); @@ -23,14 +23,12 @@ pub fn fd_sync(mut ctx: FunctionEnvMut<'_, WasiEnv>, fd: WasiFd) -> Result { if let Some(handle) = handle { let handle = handle.clone(); - drop(fd_entry); drop(guard); - drop(inodes); let size = { wasi_try_ok!(__asyncify(&mut ctx, None, async move { @@ -45,11 +43,11 @@ pub fn fd_sync(mut ctx: FunctionEnvMut<'_, WasiEnv>, fd: WasiFd) -> Result( } let fd_flags = fd_entry.flags; - let inode_idx = fd_entry.inode; let (bytes_written, can_update_cursor) = { - let (mut memory, _, inodes) = env.get_memory_and_wasi_state_and_inodes(&ctx, 0); - let inode = &inodes.arena[inode_idx]; - let mut guard = inode.write(); + let (mut memory, _) = env.get_memory_and_wasi_state(&ctx, 0); + let mut guard = fd_entry.inode.write(); match guard.deref_mut() { Kind::File { handle, .. } => { if let Some(handle) = handle { let handle = handle.clone(); drop(guard); - drop(inodes); let buf_len: M::Offset = iovs_arr .iter() @@ -168,7 +165,6 @@ fn fd_write_internal( Kind::Socket { socket } => { let socket = socket.clone(); drop(guard); - drop(inodes); let buf_len: M::Offset = iovs_arr .iter() @@ -203,12 +199,7 @@ fn fd_write_internal( // TODO: verify return Ok(Errno::Isdir); } - Kind::EventNotifications { - counter, - wakers, - immediate, - .. - } => { + Kind::EventNotifications(inner) => { let mut val: [MaybeUninit; 8] = unsafe { MaybeUninit::uninit().assume_init() }; let written = wasi_try_ok!(copy_to_slice(&memory, iovs_arr, &mut val[..])); @@ -217,12 +208,12 @@ fn fd_write_internal( } let val = u64::from_ne_bytes(unsafe { std::mem::transmute(val) }); - counter.fetch_add(val, Ordering::AcqRel); + inner.counter.fetch_add(val, Ordering::AcqRel); + inner.last_poll.store(u64::MAX, Ordering::Release); { - let mut guard = wakers.lock().unwrap(); - immediate.store(true, Ordering::Release); + let mut guard = inner.wakers.lock().unwrap(); while let Some(wake) = guard.pop_back() { - let _ = wake.send(()); + wake.wake(); } } @@ -252,9 +243,8 @@ fn fd_write_internal( // we set the size but we don't return any errors if it fails as // pipes and sockets will not do anything with this let (mut memory, _, inodes) = env.get_memory_and_wasi_state_and_inodes(&ctx, 0); - let inode = &inodes.arena[inode_idx]; // Cast is valid because we don't support 128 bit systems... - inode.stat.write().unwrap().st_size += bytes_written as u64; + fd_entry.inode.stat.write().unwrap().st_size += bytes_written as u64; } bytes_written }; diff --git a/lib/wasi/src/syscalls/wasi/path_create_directory.rs b/lib/wasi/src/syscalls/wasi/path_create_directory.rs index 421b5be909b..7d8fb47a3b7 100644 --- a/lib/wasi/src/syscalls/wasi/path_create_directory.rs +++ b/lib/wasi/src/syscalls/wasi/path_create_directory.rs @@ -26,11 +26,11 @@ pub fn path_create_directory( ctx.data().tid() ); let env = ctx.data(); - let (memory, state, mut inodes) = env.get_memory_and_wasi_state_and_inodes_mut(&ctx, 0); + let (memory, state, inodes) = env.get_memory_and_wasi_state_and_inodes(&ctx, 0); let working_dir = wasi_try!(state.fs.get_fd(fd)); { - let guard = inodes.arena[working_dir.inode].read(); + let guard = working_dir.inode.read(); if let Kind::Root { .. } = guard.deref() { return Errno::Access; } @@ -71,7 +71,9 @@ pub fn path_create_directory( let mut cur_dir_inode = working_dir.inode; for comp in &path_vec { debug!("Creating dir {}", comp); - let mut guard = inodes.arena[cur_dir_inode].write(); + + let processing_cur_dir_inode = cur_dir_inode.clone(); + let mut guard = processing_cur_dir_inode.write(); match guard.deref_mut() { Kind::Dir { ref mut entries, @@ -80,8 +82,8 @@ pub fn path_create_directory( } => { match comp.borrow() { ".." => { - if let Some(p) = parent { - cur_dir_inode = *p; + if let Some(p) = parent.upgrade() { + cur_dir_inode = p; continue; } } @@ -89,7 +91,7 @@ pub fn path_create_directory( _ => (), } if let Some(child) = entries.get(comp) { - cur_dir_inode = *child; + cur_dir_inode = child.clone(); } else { let mut adjusted_path = path.clone(); drop(guard); @@ -99,7 +101,7 @@ pub fn path_create_directory( if let Ok(adjusted_path_stat) = path_filestat_get_internal( &memory, state, - inodes.deref_mut(), + inodes, fd, 0, &adjusted_path.to_string_lossy(), @@ -111,25 +113,21 @@ pub fn path_create_directory( wasi_try!(state.fs_create_dir(&adjusted_path)); } let kind = Kind::Dir { - parent: Some(cur_dir_inode), + parent: cur_dir_inode.downgrade(), path: adjusted_path, entries: Default::default(), }; - let new_inode = wasi_try!(state.fs.create_inode( - inodes.deref_mut(), - kind, - false, - comp.to_string() - )); + let new_inode = + wasi_try!(state.fs.create_inode(inodes, kind, false, comp.to_string())); // reborrow to insert { - let mut guard = inodes.arena[cur_dir_inode].write(); + let mut guard = cur_dir_inode.write(); if let Kind::Dir { ref mut entries, .. } = guard.deref_mut() { - entries.insert(comp.to_string(), new_inode); + entries.insert(comp.to_string(), new_inode.clone()); } } cur_dir_inode = new_inode; diff --git a/lib/wasi/src/syscalls/wasi/path_filestat_get.rs b/lib/wasi/src/syscalls/wasi/path_filestat_get.rs index b4cdb7c7170..5b576f4019a 100644 --- a/lib/wasi/src/syscalls/wasi/path_filestat_get.rs +++ b/lib/wasi/src/syscalls/wasi/path_filestat_get.rs @@ -24,7 +24,7 @@ pub fn path_filestat_get( buf: WasmPtr, ) -> Errno { let env = ctx.data(); - let (memory, mut state, mut inodes) = env.get_memory_and_wasi_state_and_inodes_mut(&ctx, 0); + let (memory, mut state, inodes) = env.get_memory_and_wasi_state_and_inodes(&ctx, 0); let mut path_string = unsafe { get_input_str!(&memory, path, path_len) }; debug!( @@ -49,7 +49,7 @@ pub fn path_filestat_get( let stat = wasi_try!(path_filestat_get_internal( &memory, state, - inodes.deref_mut(), + inodes, fd, flags, &path_string @@ -77,7 +77,7 @@ pub fn path_filestat_get( pub fn path_filestat_get_internal( memory: &MemoryView, state: &WasiState, - inodes: &mut crate::WasiInodes, + inodes: &crate::WasiInodes, fd: WasiFd, flags: LookupFlags, path_string: &str, @@ -95,10 +95,10 @@ pub fn path_filestat_get_internal( path_string, flags & __WASI_LOOKUP_SYMLINK_FOLLOW != 0, )?; - if inodes.arena[file_inode].is_preopened { - Ok(*inodes.arena[file_inode].stat.read().unwrap().deref()) + if file_inode.is_preopened { + Ok(file_inode.stat.read().unwrap().deref().clone()) } else { - let guard = inodes.arena[file_inode].read(); - state.fs.get_stat_for_kind(inodes.deref(), guard.deref()) + let guard = file_inode.read(); + state.fs.get_stat_for_kind(guard.deref()) } } diff --git a/lib/wasi/src/syscalls/wasi/path_filestat_set_times.rs b/lib/wasi/src/syscalls/wasi/path_filestat_set_times.rs index a5deff53488..839162c8568 100644 --- a/lib/wasi/src/syscalls/wasi/path_filestat_set_times.rs +++ b/lib/wasi/src/syscalls/wasi/path_filestat_set_times.rs @@ -34,7 +34,7 @@ pub fn path_filestat_set_times( ctx.data().tid() ); let env = ctx.data(); - let (memory, mut state, mut inodes) = env.get_memory_and_wasi_state_and_inodes_mut(&ctx, 0); + let (memory, mut state, inodes) = env.get_memory_and_wasi_state_and_inodes(&ctx, 0); let fd_entry = wasi_try!(state.fs.get_fd(fd)); let fd_inode = fd_entry.inode; if !fd_entry.rights.contains(Rights::PATH_FILESTAT_SET_TIMES) { @@ -61,25 +61,23 @@ pub fn path_filestat_set_times( } let file_inode = wasi_try!(state.fs.get_inode_at_path( - inodes.deref_mut(), + inodes, fd, &path_string, flags & __WASI_LOOKUP_SYMLINK_FOLLOW != 0, )); let stat = { - let guard = inodes.arena[file_inode].read(); - wasi_try!(state.fs.get_stat_for_kind(inodes.deref(), guard.deref())) + let guard = file_inode.read(); + wasi_try!(state.fs.get_stat_for_kind(guard.deref())) }; - let inode = &inodes.arena[fd_inode]; - if fst_flags.contains(Fstflags::SET_ATIM) || fst_flags.contains(Fstflags::SET_ATIM_NOW) { let time_to_set = if fst_flags.contains(Fstflags::SET_ATIM) { st_atim } else { wasi_try!(get_current_time_in_nanos()) }; - inode.stat.write().unwrap().st_atim = time_to_set; + fd_inode.stat.write().unwrap().st_atim = time_to_set; } if fst_flags.contains(Fstflags::SET_MTIM) || fst_flags.contains(Fstflags::SET_MTIM_NOW) { let time_to_set = if fst_flags.contains(Fstflags::SET_MTIM) { @@ -87,7 +85,7 @@ pub fn path_filestat_set_times( } else { wasi_try!(get_current_time_in_nanos()) }; - inode.stat.write().unwrap().st_mtim = time_to_set; + fd_inode.stat.write().unwrap().st_mtim = time_to_set; } Errno::Success diff --git a/lib/wasi/src/syscalls/wasi/path_link.rs b/lib/wasi/src/syscalls/wasi/path_link.rs index 5ca5cb5d9bd..71a4c4846f4 100644 --- a/lib/wasi/src/syscalls/wasi/path_link.rs +++ b/lib/wasi/src/syscalls/wasi/path_link.rs @@ -33,7 +33,7 @@ pub fn path_link( debug!(" - will follow symlinks when opening path"); } let env = ctx.data(); - let (memory, mut state, mut inodes) = env.get_memory_and_wasi_state_and_inodes_mut(&ctx, 0); + let (memory, mut state, inodes) = env.get_memory_and_wasi_state_and_inodes(&ctx, 0); let mut old_path_str = unsafe { get_input_str!(&memory, old_path, old_path_len) }; let mut new_path_str = unsafe { get_input_str!(&memory, new_path, new_path_len) }; let source_fd = wasi_try!(state.fs.get_fd(old_fd)); @@ -54,30 +54,28 @@ pub fn path_link( new_path_str = ctx.data().state.fs.relative_path_to_absolute(new_path_str); let source_inode = wasi_try!(state.fs.get_inode_at_path( - inodes.deref_mut(), + inodes, old_fd, &old_path_str, old_flags & __WASI_LOOKUP_SYMLINK_FOLLOW != 0, )); let target_path_arg = std::path::PathBuf::from(&new_path_str); - let (target_parent_inode, new_entry_name) = wasi_try!(state.fs.get_parent_inode_at_path( - inodes.deref_mut(), - new_fd, - &target_path_arg, - false - )); + let (target_parent_inode, new_entry_name) = + wasi_try!(state + .fs + .get_parent_inode_at_path(inodes, new_fd, &target_path_arg, false)); - if inodes.arena[source_inode].stat.write().unwrap().st_nlink == Linkcount::max_value() { + if source_inode.stat.write().unwrap().st_nlink == Linkcount::max_value() { return Errno::Mlink; } { - let mut guard = inodes.arena[target_parent_inode].write(); + let mut guard = target_parent_inode.write(); match guard.deref_mut() { Kind::Dir { entries, .. } => { if entries.contains_key(&new_entry_name) { return Errno::Exist; } - entries.insert(new_entry_name, source_inode); + entries.insert(new_entry_name, source_inode.clone()); } Kind::Root { .. } => return Errno::Inval, Kind::File { .. } @@ -88,7 +86,7 @@ pub fn path_link( | Kind::EventNotifications { .. } => return Errno::Notdir, } } - inodes.arena[source_inode].stat.write().unwrap().st_nlink += 1; + source_inode.stat.write().unwrap().st_nlink += 1; Errno::Success } diff --git a/lib/wasi/src/syscalls/wasi/path_open.rs b/lib/wasi/src/syscalls/wasi/path_open.rs index 2f087f972c9..1afd852d2dd 100644 --- a/lib/wasi/src/syscalls/wasi/path_open.rs +++ b/lib/wasi/src/syscalls/wasi/path_open.rs @@ -42,7 +42,7 @@ pub fn path_open( debug!(" - will follow symlinks when opening path"); } let env = ctx.data(); - let (memory, mut state, mut inodes) = env.get_memory_and_wasi_state_and_inodes_mut(&ctx, 0); + let (memory, mut state, mut inodes) = env.get_memory_and_wasi_state_and_inodes(&ctx, 0); /* TODO: find actual upper bound on name size (also this is a path, not a name :think-fish:) */ let path_len64: u64 = path_len.into(); if path_len64 > 1024u64 * 1024u64 { @@ -81,7 +81,7 @@ pub fn path_open( let path_arg = std::path::PathBuf::from(&path_string); let maybe_inode = state.fs.get_inode_at_path( - inodes.deref_mut(), + inodes, dirfd, &path_string, dirflags & __WASI_LOOKUP_SYMLINK_FOLLOW != 0, @@ -148,7 +148,9 @@ pub fn path_open( let inode = if let Ok(inode) = maybe_inode { // Happy path, we found the file we're trying to open - let mut guard = inodes.arena[inode].write(); + let processing_inode = inode.clone(); + let mut guard = processing_inode.write(); + let deref_mut = guard.deref_mut(); match deref_mut { Kind::File { @@ -244,13 +246,13 @@ pub fn path_open( // strip end file name let (parent_inode, new_entity_name) = wasi_try!(state.fs.get_parent_inode_at_path( - inodes.deref_mut(), + inodes, dirfd, &path_arg, dirflags & __WASI_LOOKUP_SYMLINK_FOLLOW != 0 )); let new_file_host_path = { - let guard = inodes.arena[parent_inode].read(); + let guard = parent_inode.read(); match guard.deref() { Kind::Dir { path, .. } => { let mut new_path = path.clone(); @@ -301,21 +303,18 @@ pub fn path_open( path: new_file_host_path, fd: None, }; - wasi_try!(state.fs.create_inode( - inodes.deref_mut(), - kind, - false, - new_entity_name.clone() - )) + wasi_try!(state + .fs + .create_inode(inodes, kind, false, new_entity_name.clone())) }; { - let mut guard = inodes.arena[parent_inode].write(); + let mut guard = parent_inode.write(); if let Kind::Dir { ref mut entries, .. } = guard.deref_mut() { - entries.insert(new_entity_name, new_inode); + entries.insert(new_entity_name, new_inode.clone()); } } diff --git a/lib/wasi/src/syscalls/wasi/path_readlink.rs b/lib/wasi/src/syscalls/wasi/path_readlink.rs index 80100714e09..fe5031e609f 100644 --- a/lib/wasi/src/syscalls/wasi/path_readlink.rs +++ b/lib/wasi/src/syscalls/wasi/path_readlink.rs @@ -32,7 +32,7 @@ pub fn path_readlink( ctx.data().tid() ); let env = ctx.data(); - let (memory, mut state, mut inodes) = env.get_memory_and_wasi_state_and_inodes_mut(&ctx, 0); + let (memory, mut state, inodes) = env.get_memory_and_wasi_state_and_inodes(&ctx, 0); let base_dir = wasi_try!(state.fs.get_fd(dir_fd)); if !base_dir.rights.contains(Rights::PATH_READLINK) { @@ -51,12 +51,10 @@ pub fn path_readlink( ); } - let inode = wasi_try!(state - .fs - .get_inode_at_path(inodes.deref_mut(), dir_fd, &path_str, false)); + let inode = wasi_try!(state.fs.get_inode_at_path(inodes, dir_fd, &path_str, false)); { - let guard = inodes.arena[inode].read(); + let guard = inode.read(); if let Kind::Symlink { relative_path, .. } = guard.deref() { let rel_path_str = relative_path.to_string_lossy(); debug!("Result => {:?}", rel_path_str); diff --git a/lib/wasi/src/syscalls/wasi/path_remove_directory.rs b/lib/wasi/src/syscalls/wasi/path_remove_directory.rs index be7d91dea0c..a940c2d1148 100644 --- a/lib/wasi/src/syscalls/wasi/path_remove_directory.rs +++ b/lib/wasi/src/syscalls/wasi/path_remove_directory.rs @@ -15,7 +15,7 @@ pub fn path_remove_directory( ctx.data().tid() ); let env = ctx.data(); - let (memory, mut state, mut inodes) = env.get_memory_and_wasi_state_and_inodes_mut(&ctx, 0); + let (memory, mut state, inodes) = env.get_memory_and_wasi_state_and_inodes(&ctx, 0); let base_dir = wasi_try!(state.fs.get_fd(fd)); let mut path_str = unsafe { get_input_str!(&memory, path, path_len) }; @@ -31,18 +31,16 @@ pub fn path_remove_directory( ); } - let inode = wasi_try!(state - .fs - .get_inode_at_path(inodes.deref_mut(), fd, &path_str, false)); + let inode = wasi_try!(state.fs.get_inode_at_path(inodes, fd, &path_str, false)); let (parent_inode, childs_name) = wasi_try!(state.fs.get_parent_inode_at_path( - inodes.deref_mut(), + inodes, fd, std::path::Path::new(&path_str), false )); let host_path_to_remove = { - let guard = inodes.arena[inode].read(); + let guard = inode.read(); match guard.deref() { Kind::Dir { entries, path, .. } => { if !entries.is_empty() || wasi_try!(state.fs_read_dir(path)).count() != 0 { @@ -56,14 +54,14 @@ pub fn path_remove_directory( }; { - let mut guard = inodes.arena[parent_inode].write(); + let mut guard = parent_inode.write(); match guard.deref_mut() { Kind::Dir { ref mut entries, .. } => { let removed_inode = wasi_try!(entries.remove(&childs_name).ok_or(Errno::Inval)); // TODO: make this a debug assert in the future - assert!(inode == removed_inode); + assert!(inode.ino() == removed_inode.ino()); } Kind::Root { .. } => return Errno::Access, _ => unreachable!( @@ -74,7 +72,7 @@ pub fn path_remove_directory( if let Err(err) = state.fs_remove_dir(host_path_to_remove) { // reinsert to prevent FS from being in bad state - let mut guard = inodes.arena[parent_inode].write(); + let mut guard = parent_inode.write(); if let Kind::Dir { ref mut entries, .. } = guard.deref_mut() diff --git a/lib/wasi/src/syscalls/wasi/path_rename.rs b/lib/wasi/src/syscalls/wasi/path_rename.rs index c458a12fe9d..6225f062ed7 100644 --- a/lib/wasi/src/syscalls/wasi/path_rename.rs +++ b/lib/wasi/src/syscalls/wasi/path_rename.rs @@ -30,7 +30,7 @@ pub fn path_rename( old_fd, new_fd ); let env = ctx.data(); - let (memory, mut state, mut inodes) = env.get_memory_and_wasi_state_and_inodes_mut(&ctx, 0); + let (memory, mut state, inodes) = env.get_memory_and_wasi_state_and_inodes(&ctx, 0); let mut source_str = unsafe { get_input_str!(&memory, old_path, old_path_len) }; source_str = ctx.data().state.fs.relative_path_to_absolute(source_str); let source_path = std::path::Path::new(&source_str); @@ -52,29 +52,27 @@ pub fn path_rename( // this is to be sure the source file is fetch from filesystem if needed wasi_try!(state.fs.get_inode_at_path( - inodes.deref_mut(), + inodes, old_fd, source_path.to_str().as_ref().unwrap(), true )); // Create the destination inode if the file exists. - let _ = state.fs.get_inode_at_path( - inodes.deref_mut(), - new_fd, - target_path.to_str().as_ref().unwrap(), - true, - ); + let _ = + state + .fs + .get_inode_at_path(inodes, new_fd, target_path.to_str().as_ref().unwrap(), true); let (source_parent_inode, source_entry_name) = wasi_try!(state .fs - .get_parent_inode_at_path(inodes.deref_mut(), old_fd, source_path, true)); + .get_parent_inode_at_path(inodes, old_fd, source_path, true)); let (target_parent_inode, target_entry_name) = wasi_try!(state .fs - .get_parent_inode_at_path(inodes.deref_mut(), new_fd, target_path, true)); + .get_parent_inode_at_path(inodes, new_fd, target_path, true)); let mut need_create = true; let host_adjusted_target_path = { - let guard = inodes.arena[target_parent_inode].read(); + let guard = target_parent_inode.read(); match guard.deref() { Kind::Dir { entries, path, .. } => { if entries.contains_key(&target_entry_name) { @@ -96,7 +94,7 @@ pub fn path_rename( }; let source_entry = { - let mut guard = inodes.arena[source_parent_inode].write(); + let mut guard = source_parent_inode.write(); match guard.deref_mut() { Kind::Dir { entries, .. } => { wasi_try!(entries.remove(&source_entry_name).ok_or(Errno::Noent)) @@ -113,7 +111,7 @@ pub fn path_rename( }; { - let mut guard = inodes.arena[source_entry].write(); + let mut guard = source_entry.write(); match guard.deref_mut() { Kind::File { handle, ref path, .. @@ -131,7 +129,7 @@ pub fn path_rename( drop(guard); let out = state.fs_rename(&path_clone, &host_adjusted_target_path); { - let mut guard = inodes.arena[source_entry].write(); + let mut guard = source_entry.write(); if let Kind::File { ref mut path, .. } = guard.deref_mut() { *path = host_adjusted_target_path; } else { @@ -142,7 +140,7 @@ pub fn path_rename( }; // if the above operation failed we have to revert the previous change and then fail if let Err(e) = result { - let mut guard = inodes.arena[source_parent_inode].write(); + let mut guard = source_parent_inode.write(); if let Kind::Dir { entries, .. } = guard.deref_mut() { entries.insert(source_entry_name, source_entry); return e; @@ -156,7 +154,7 @@ pub fn path_rename( } { drop(guard); - let mut guard = inodes.arena[source_entry].write(); + let mut guard = source_entry.write(); if let Kind::Dir { path, .. } = guard.deref_mut() { *path = host_adjusted_target_path; } @@ -172,7 +170,7 @@ pub fn path_rename( } if need_create { - let mut guard = inodes.arena[target_parent_inode].write(); + let mut guard = target_parent_inode.write(); if let Kind::Dir { entries, .. } = guard.deref_mut() { let result = entries.insert(target_entry_name, source_entry); assert!( diff --git a/lib/wasi/src/syscalls/wasi/path_symlink.rs b/lib/wasi/src/syscalls/wasi/path_symlink.rs index f8bbb511f5d..dbd2148fa20 100644 --- a/lib/wasi/src/syscalls/wasi/path_symlink.rs +++ b/lib/wasi/src/syscalls/wasi/path_symlink.rs @@ -28,7 +28,7 @@ pub fn path_symlink( ctx.data().tid() ); let env = ctx.data(); - let (memory, mut state, mut inodes) = env.get_memory_and_wasi_state_and_inodes_mut(&ctx, 0); + let (memory, mut state, inodes) = env.get_memory_and_wasi_state_and_inodes(&ctx, 0); let mut old_path_str = unsafe { get_input_str!(&memory, old_path, old_path_len) }; let mut new_path_str = unsafe { get_input_str!(&memory, new_path, new_path_len) }; old_path_str = ctx.data().state.fs.relative_path_to_absolute(old_path_str); @@ -43,10 +43,8 @@ pub fn path_symlink( let (source_inode, _) = wasi_try!(state .fs - .get_parent_inode_at_path(inodes.deref_mut(), fd, old_path_path, true)); - let depth = state - .fs - .path_depth_from_fd(inodes.deref(), fd, source_inode); + .get_parent_inode_at_path(inodes, fd, old_path_path, true)); + let depth = state.fs.path_depth_from_fd(fd, source_inode); // depth == -1 means folder is not relative. See issue #3233. let depth = match depth { @@ -58,11 +56,11 @@ pub fn path_symlink( let (target_parent_inode, entry_name) = wasi_try!(state .fs - .get_parent_inode_at_path(inodes.deref_mut(), fd, new_path_path, true)); + .get_parent_inode_at_path(inodes, fd, new_path_path, true)); // short circuit if anything is wrong, before we create an inode { - let guard = inodes.arena[target_parent_inode].read(); + let guard = target_parent_inode.read(); match guard.deref() { Kind::Dir { entries, .. } => { if entries.contains_key(&entry_name) { @@ -96,15 +94,13 @@ pub fn path_symlink( path_to_symlink: std::path::PathBuf::from(new_path_str), relative_path, }; - let new_inode = state.fs.create_inode_with_default_stat( - inodes.deref_mut(), - kind, - false, - entry_name.clone().into(), - ); + let new_inode = + state + .fs + .create_inode_with_default_stat(inodes, kind, false, entry_name.clone().into()); { - let mut guard = inodes.arena[target_parent_inode].write(); + let mut guard = target_parent_inode.write(); if let Kind::Dir { ref mut entries, .. } = guard.deref_mut() diff --git a/lib/wasi/src/syscalls/wasi/path_unlink_file.rs b/lib/wasi/src/syscalls/wasi/path_unlink_file.rs index 811745f76af..10d43d2bbc3 100644 --- a/lib/wasi/src/syscalls/wasi/path_unlink_file.rs +++ b/lib/wasi/src/syscalls/wasi/path_unlink_file.rs @@ -22,7 +22,7 @@ pub fn path_unlink_file( ctx.data().tid() ); let env = ctx.data(); - let (memory, mut state, mut inodes) = env.get_memory_and_wasi_state_and_inodes_mut(&ctx, 0); + let (memory, mut state, inodes) = env.get_memory_and_wasi_state_and_inodes(&ctx, 0); let base_dir = wasi_try!(state.fs.get_fd(fd)); if !base_dir.rights.contains(Rights::PATH_UNLINK_FILE) { @@ -42,26 +42,24 @@ pub fn path_unlink_file( ); } - let inode = wasi_try!(state - .fs - .get_inode_at_path(inodes.deref_mut(), fd, &path_str, false)); + let inode = wasi_try!(state.fs.get_inode_at_path(inodes, fd, &path_str, false)); let (parent_inode, childs_name) = wasi_try!(state.fs.get_parent_inode_at_path( - inodes.deref_mut(), + inodes, fd, std::path::Path::new(&path_str), false )); let removed_inode = { - let mut guard = inodes.arena[parent_inode].write(); + let mut guard = parent_inode.write(); match guard.deref_mut() { Kind::Dir { ref mut entries, .. } => { let removed_inode = wasi_try!(entries.remove(&childs_name).ok_or(Errno::Inval)); // TODO: make this a debug assert in the future - assert!(inode == removed_inode); - debug_assert!(inodes.arena[inode].stat.read().unwrap().st_nlink > 0); + assert!(inode.ino() == removed_inode.ino()); + debug_assert!(inode.stat.read().unwrap().st_nlink > 0); removed_inode } Kind::Root { .. } => return Errno::Access, @@ -72,13 +70,13 @@ pub fn path_unlink_file( }; let st_nlink = { - let mut guard = inodes.arena[removed_inode].stat.write().unwrap(); + let mut guard = removed_inode.stat.write().unwrap(); guard.st_nlink -= 1; guard.st_nlink }; if st_nlink == 0 { { - let mut guard = inodes.arena[removed_inode].read(); + let mut guard = removed_inode.read(); match guard.deref() { Kind::File { handle, path, .. } => { if let Some(h) = handle { @@ -100,27 +98,6 @@ pub fn path_unlink_file( _ => unimplemented!("wasi::path_unlink_file for Buffer"), } } - // TODO: test this on Windows and actually make it portable - // make the file an orphan fd if the fd is still open - let fd_is_orphaned = { - let guard = inodes.arena[removed_inode].read(); - if let Kind::File { handle, .. } = guard.deref() { - handle.is_some() - } else { - false - } - }; - let removed_inode_val = unsafe { state.fs.remove_inode(inodes.deref_mut(), removed_inode) }; - assert!( - removed_inode_val.is_some(), - "Inode could not be removed because it doesn't exist" - ); - - if fd_is_orphaned { - inodes - .orphan_fds - .insert(removed_inode, removed_inode_val.unwrap()); - } } Errno::Success diff --git a/lib/wasi/src/syscalls/wasi/poll_oneoff.rs b/lib/wasi/src/syscalls/wasi/poll_oneoff.rs index d7d11c4715d..930db298163 100644 --- a/lib/wasi/src/syscalls/wasi/poll_oneoff.rs +++ b/lib/wasi/src/syscalls/wasi/poll_oneoff.rs @@ -1,3 +1,5 @@ +use std::f32::consts::E; + use wasmer_wasi_types::wasi::SubscriptionClock; use super::*; @@ -5,6 +7,7 @@ use crate::{ fs::{InodeValFilePollGuard, InodeValFilePollGuardJoin}, state::PollEventSet, syscalls::*, + WasiInodes, }; /// ### `poll_oneoff()` @@ -28,12 +31,15 @@ pub fn poll_oneoff( ) -> Result { wasi_try_ok!(WasiEnv::process_signals_and_exit(&mut ctx)?); + ctx.data_mut().poll_seed += 1; let mut env = ctx.data(); let mut memory = env.memory_view(&ctx); let subscription_array = wasi_try_mem_ok!(in_.slice(&memory, nsubscriptions)); let mut subscriptions = Vec::with_capacity(subscription_array.len() as usize); - for sub in subscription_array.iter() { + for n in 0..subscription_array.len() { + let n = (n + env.poll_seed) % subscription_array.len(); + let sub = subscription_array.index(n); let s = wasi_try_mem_ok!(sub.read()); subscriptions.push((None, PollEventSet::default(), s)); } @@ -97,17 +103,18 @@ impl<'a> Future for PollBatch<'a> { let mut guard = Pin::new(join); match guard.poll(cx) { Poll::Pending => {} - Poll::Ready(evt) => { - tracing::trace!( - "wasi[{}:{}]::poll_oneoff triggered_fd (fd={}, userdata={}, type={:?})", - pid, - tid, - fd, - evt.userdata, - evt.type_, - ); - evts.push(evt); - done = true; + Poll::Ready(e) => { + for evt in e { + tracing::trace!( + "wasi[{}:{}]::poll_oneoff triggered_fd (fd={}, userdata={}, type={:?})", + pid, + tid, + fd, + evt.userdata, + evt.type_, + ); + evts.push(evt); + } } } } @@ -247,8 +254,6 @@ pub(crate) fn poll_oneoff_internal( let mut guards = { // We start by building a list of files we are going to poll // and open a read lock on them all - let inodes = state.inodes.clone(); - let inodes = inodes.read().unwrap(); let mut fd_guards = Vec::with_capacity(subs.len()); #[allow(clippy::significant_drop_in_scrutinee)] @@ -256,20 +261,17 @@ pub(crate) fn poll_oneoff_internal( if let Some(fd) = fd { let wasi_file_ref = match fd { __WASI_STDERR_FILENO => { - wasi_try_ok_ok!(inodes - .stderr(&state.fs.fd_map) + wasi_try_ok_ok!(WasiInodes::stderr(&state.fs.fd_map) .map(|g| g.into_poll_guard(fd, peb, s)) .map_err(fs_error_into_wasi_err)) } __WASI_STDIN_FILENO => { - wasi_try_ok_ok!(inodes - .stdin(&state.fs.fd_map) + wasi_try_ok_ok!(WasiInodes::stdin(&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!(inodes - .stdout(&state.fs.fd_map) + wasi_try_ok_ok!(WasiInodes::stdout(&state.fs.fd_map) .map(|g| g.into_poll_guard(fd, peb, s)) .map_err(fs_error_into_wasi_err)) } @@ -281,7 +283,7 @@ pub(crate) fn poll_oneoff_internal( let inode = fd_entry.inode; { - let guard = inodes.arena[inode].read(); + let guard = inode.read(); if let Some(guard) = crate::fs::InodeValFilePollGuard::new(fd, peb, s, guard.deref()) { @@ -307,12 +309,16 @@ pub(crate) fn poll_oneoff_internal( }; if let Some(time_to_sleep) = time_to_sleep.as_ref() { - tracing::trace!( - "wasi[{}:{}]::poll_oneoff wait_for_timeout={}", - pid, - tid, - time_to_sleep.as_millis() - ); + if *time_to_sleep == Duration::ZERO { + tracing::trace!("wasi[{}:{}]::poll_oneoff non_blocking", pid, tid,); + } else { + tracing::trace!( + "wasi[{}:{}]::poll_oneoff wait_for_timeout={}", + pid, + tid, + time_to_sleep.as_millis() + ); + } } else { tracing::trace!("wasi[{}:{}]::poll_oneoff wait_for_infinite", pid, tid,); } diff --git a/lib/wasi/src/syscalls/wasix/bus_poll.rs b/lib/wasi/src/syscalls/wasix/bus_poll.rs index b52b56f1f4a..ca60f441cf1 100644 --- a/lib/wasi/src/syscalls/wasix/bus_poll.rs +++ b/lib/wasi/src/syscalls/wasix/bus_poll.rs @@ -75,11 +75,9 @@ pub fn bus_poll( // Function that turns a buffer into a readable file handle let buf_to_fd = { let state = env.state.clone(); - let inodes = state.inodes.clone(); move |data: Vec| -> Result { - let mut inodes = inodes.write().unwrap(); let inode = state.fs.create_inode_with_default_stat( - inodes.deref_mut(), + &state.inodes, Kind::Buffer { buffer: data }, false, "bus".into(), diff --git a/lib/wasi/src/syscalls/wasix/fd_pipe.rs b/lib/wasi/src/syscalls/wasix/fd_pipe.rs index da4ef487078..5d9e2e429c1 100644 --- a/lib/wasi/src/syscalls/wasix/fd_pipe.rs +++ b/lib/wasi/src/syscalls/wasix/fd_pipe.rs @@ -16,20 +16,20 @@ pub fn fd_pipe( trace!("wasi[{}:{}]::fd_pipe", ctx.data().pid(), ctx.data().tid()); let env = ctx.data(); - let (memory, state, mut inodes) = env.get_memory_and_wasi_state_and_inodes_mut(&ctx, 0); + let (memory, state, inodes) = env.get_memory_and_wasi_state_and_inodes(&ctx, 0); let pipes = WasiBidirectionalPipePair::new(); let pipe1 = pipes.tx; let pipe2 = pipes.rx; let inode1 = state.fs.create_inode_with_default_stat( - inodes.deref_mut(), + inodes, Kind::Pipe { pipe: pipe1 }, false, "pipe".to_string().into(), ); let inode2 = state.fs.create_inode_with_default_stat( - inodes.deref_mut(), + inodes, Kind::Pipe { pipe: pipe2 }, false, "pipe".to_string().into(), diff --git a/lib/wasi/src/syscalls/wasix/getcwd.rs b/lib/wasi/src/syscalls/wasix/getcwd.rs index 616c962666d..85404d7f4fa 100644 --- a/lib/wasi/src/syscalls/wasix/getcwd.rs +++ b/lib/wasi/src/syscalls/wasix/getcwd.rs @@ -12,11 +12,9 @@ pub fn getcwd( ) -> Errno { debug!("wasi[{}:{}]::getcwd", ctx.data().pid(), ctx.data().tid()); let env = ctx.data(); - let (memory, mut state, mut inodes) = env.get_memory_and_wasi_state_and_inodes_mut(&ctx, 0); + let (memory, mut state, inodes) = env.get_memory_and_wasi_state_and_inodes(&ctx, 0); - let (_, cur_dir) = wasi_try!(state - .fs - .get_current_dir(inodes.deref_mut(), crate::VIRTUAL_ROOT_FD,)); + let (_, cur_dir) = wasi_try!(state.fs.get_current_dir(inodes, crate::VIRTUAL_ROOT_FD,)); trace!( "wasi[{}:{}]::getcwd(current_dir={})", ctx.data().pid(), diff --git a/lib/wasi/src/syscalls/wasix/proc_exec.rs b/lib/wasi/src/syscalls/wasix/proc_exec.rs index e304c6ef476..48c5efa38a3 100644 --- a/lib/wasi/src/syscalls/wasix/proc_exec.rs +++ b/lib/wasi/src/syscalls/wasix/proc_exec.rs @@ -57,12 +57,8 @@ pub fn proc_exec( // Get the current working directory let (_, cur_dir) = { - let (memory, state, mut inodes) = - ctx.data().get_memory_and_wasi_state_and_inodes_mut(&ctx, 0); - match state - .fs - .get_current_dir(inodes.deref_mut(), crate::VIRTUAL_ROOT_FD) - { + let (memory, state, inodes) = ctx.data().get_memory_and_wasi_state_and_inodes(&ctx, 0); + match state.fs.get_current_dir(inodes, crate::VIRTUAL_ROOT_FD) { Ok(a) => a, Err(err) => { warn!("failed to create subprocess for fork - {}", err); diff --git a/lib/wasi/src/syscalls/wasix/proc_spawn.rs b/lib/wasi/src/syscalls/wasix/proc_spawn.rs index 89effd4ecd6..130f93718ca 100644 --- a/lib/wasi/src/syscalls/wasix/proc_spawn.rs +++ b/lib/wasi/src/syscalls/wasix/proc_spawn.rs @@ -117,7 +117,7 @@ pub fn proc_spawn_internal( } }; if let Some(args) = args { - let mut child_state = env.state.fork(true); + let mut child_state = env.state.fork(); child_state.args = args; child_env.state = Arc::new(child_state); } @@ -148,8 +148,8 @@ pub fn proc_spawn_internal( // Replace the STDIO let (stdin, stdout, stderr) = { - let (_, child_state, mut child_inodes) = - child_env.get_memory_and_wasi_state_and_inodes_mut(&new_store, 0); + let (_, child_state, child_inodes) = + child_env.get_memory_and_wasi_state_and_inodes(&new_store, 0); let mut conv_stdio_mode = |mode: WasiStdioMode, fd: WasiFd| -> Result { match mode { WasiStdioMode::Piped => { @@ -157,13 +157,13 @@ pub fn proc_spawn_internal( let pipe1 = pipes.rx; let pipe2 = pipes.tx; let inode1 = child_state.fs.create_inode_with_default_stat( - child_inodes.deref_mut(), + child_inodes, Kind::Pipe { pipe: pipe1 }, false, "pipe".into(), ); let inode2 = child_state.fs.create_inode_with_default_stat( - child_inodes.deref_mut(), + child_inodes, Kind::Pipe { pipe: pipe2 }, false, "pipe".into(), @@ -198,7 +198,7 @@ pub fn proc_spawn_internal( fd: u32::MAX, }), _ => { - child_state.fs.close_fd(child_inodes.deref(), fd); + child_state.fs.close_fd(fd); Ok(OptionFd { tag: OptionTag::None, fd: u32::MAX, diff --git a/lib/wasi/src/syscalls/wasix/sock_accept.rs b/lib/wasi/src/syscalls/wasix/sock_accept.rs index 1ab01c3d413..cebdba53222 100644 --- a/lib/wasi/src/syscalls/wasix/sock_accept.rs +++ b/lib/wasi/src/syscalls/wasix/sock_accept.rs @@ -47,7 +47,7 @@ pub fn sock_accept( )); let env = ctx.data(); - let (memory, state, mut inodes) = env.get_memory_and_wasi_state_and_inodes_mut(&ctx, 0); + let (memory, state, inodes) = env.get_memory_and_wasi_state_and_inodes(&ctx, 0); let kind = Kind::Socket { socket: InodeSocket::new(InodeSocketKind::TcpStream { @@ -56,10 +56,9 @@ pub fn sock_accept( read_timeout: None, }), }; - let inode = - state - .fs - .create_inode_with_default_stat(inodes.deref_mut(), kind, false, "socket".into()); + let inode = state + .fs + .create_inode_with_default_stat(inodes, kind, false, "socket".into()); let mut new_flags = Fdflags::empty(); if fd_flags.contains(Fdflags::NONBLOCK) { diff --git a/lib/wasi/src/syscalls/wasix/sock_open.rs b/lib/wasi/src/syscalls/wasix/sock_open.rs index 6f577cfc9d2..704eb39e189 100644 --- a/lib/wasi/src/syscalls/wasix/sock_open.rs +++ b/lib/wasi/src/syscalls/wasix/sock_open.rs @@ -30,7 +30,7 @@ pub fn sock_open( debug!("wasi[{}:{}]::sock_open", ctx.data().pid(), ctx.data().tid()); let env = ctx.data(); - let (memory, state, mut inodes) = env.get_memory_and_wasi_state_and_inodes_mut(&ctx, 0); + let (memory, state, inodes) = env.get_memory_and_wasi_state_and_inodes(&ctx, 0); let kind = match ty { Socktype::Stream | Socktype::Dgram => Kind::Socket { @@ -53,12 +53,10 @@ pub fn sock_open( _ => return Errno::Notsup, }; - let inode = state.fs.create_inode_with_default_stat( - inodes.deref_mut(), - kind, - false, - "socket".to_string().into(), - ); + let inode = + state + .fs + .create_inode_with_default_stat(inodes, kind, false, "socket".to_string().into()); let rights = Rights::all_socket(); let fd = wasi_try!(state .fs diff --git a/lib/wasi/src/syscalls/wasix/sock_recv.rs b/lib/wasi/src/syscalls/wasix/sock_recv.rs index 932d88c3c56..f3460e4fb89 100644 --- a/lib/wasi/src/syscalls/wasix/sock_recv.rs +++ b/lib/wasi/src/syscalls/wasix/sock_recv.rs @@ -25,27 +25,101 @@ pub fn sock_recv( ro_data_len: WasmPtr, ro_flags: WasmPtr, ) -> Result { - wasi_try_ok!(WasiEnv::process_signals_and_exit(&mut ctx)?); + let pid = ctx.data().pid(); + let tid = ctx.data().tid(); - let mut env = ctx.data(); + let res = sock_recv_internal::( + &mut ctx, + sock, + ri_data, + ri_data_len, + ri_flags, + ro_data_len, + ro_flags, + )?; + + let mut ret = Errno::Success; + let bytes_read = match res { + Ok(bytes_read) => { + debug!( + %bytes_read, + "wasi[{}:{}]::sock_recv (fd={}, flags={:?})", + ctx.data().pid(), + ctx.data().tid(), + sock, + ri_flags + ); + bytes_read + } + Err(err) => { + let socket_err = err.name(); + debug!( + %socket_err, + "wasi[{}:{}]::sock_recv (fd={}, flags={:?})", + ctx.data().pid(), + ctx.data().tid(), + sock, + ri_flags + ); + ret = err; + 0 + } + }; + + let env = ctx.data(); let memory = env.memory_view(&ctx); - let iovs_arr = wasi_try_mem_ok!(ri_data.slice(&memory, ri_data_len)); + + let bytes_read: M::Offset = wasi_try_ok!(bytes_read.try_into().map_err(|_| Errno::Overflow)); + wasi_try_mem_ok!(ro_flags.write(&memory, 0)); + wasi_try_mem_ok!(ro_data_len.write(&memory, bytes_read)); + + Ok(ret) +} + +/// ### `sock_recv()` +/// Receive a message from a socket. +/// Note: This is similar to `recv` in POSIX, though it also supports reading +/// the data into multiple buffers in the manner of `readv`. +/// +/// ## Parameters +/// +/// * `ri_data` - List of scatter/gather vectors to which to store data. +/// * `ri_flags` - Message flags. +/// +/// ## Return +/// +/// Number of bytes stored in ri_data and message flags. +fn sock_recv_internal( + ctx: &mut FunctionEnvMut<'_, WasiEnv>, + sock: WasiFd, + ri_data: WasmPtr<__wasi_iovec_t, M>, + ri_data_len: M::Offset, + ri_flags: RiFlags, + ro_data_len: WasmPtr, + ro_flags: WasmPtr, +) -> Result, WasiError> { + wasi_try_ok_ok!(WasiEnv::process_signals_and_exit(ctx)?); + + let mut env = ctx.data(); + let memory = env.memory_view(ctx); + let iovs_arr = wasi_try_mem_ok_ok!(ri_data.slice(&memory, ri_data_len)); let max_size = { let mut max_size = 0usize; for iovs in iovs_arr.iter() { - let iovs = wasi_try_mem_ok!(iovs.read()); - let buf_len: usize = wasi_try_ok!(iovs.buf_len.try_into().map_err(|_| Errno::Overflow)); + let iovs = wasi_try_mem_ok_ok!(iovs.read()); + let buf_len: usize = + wasi_try_ok_ok!(iovs.buf_len.try_into().map_err(|_| Errno::Overflow)); max_size += buf_len; } max_size }; - let bytes_read = { + let res = { if max_size <= 10240 { let mut buf: [MaybeUninit; 10240] = unsafe { MaybeUninit::uninit().assume_init() }; let writer = &mut buf[..max_size]; - let amt = wasi_try_ok!(__sock_asyncify( + let amt = wasi_try_ok_ok!(__sock_asyncify( env, sock, Rights::SOCK_RECV, @@ -55,12 +129,12 @@ pub fn sock_recv( if amt > 0 { let buf: &[MaybeUninit] = &buf[..amt]; let buf: &[u8] = unsafe { std::mem::transmute(buf) }; - wasi_try_ok!(copy_from_slice(buf, &memory, iovs_arr).map(|_| amt)) + copy_from_slice(buf, &memory, iovs_arr).map(|_| amt) } else { - 0 + Ok(0) } } else { - let data = wasi_try_ok!(__sock_asyncify( + let data = wasi_try_ok_ok!(__sock_asyncify( env, sock, Rights::SOCK_RECV, @@ -85,25 +159,12 @@ pub fn sock_recv( let data_len = data.len(); if data_len > 0 { let mut reader = &data[..]; - wasi_try_ok!(read_bytes(reader, &memory, iovs_arr).map(|_| data_len)) + read_bytes(reader, &memory, iovs_arr).map(|_| data_len) } else { - 0 + Ok(0) } } }; - debug!( - "wasi[{}:{}]::sock_recv (fd={}, read={}, flags={:?})", - ctx.data().pid(), - ctx.data().tid(), - sock, - bytes_read, - ri_flags - ); - - let bytes_read: M::Offset = wasi_try_ok!(bytes_read.try_into().map_err(|_| Errno::Overflow)); - wasi_try_mem_ok!(ro_flags.write(&memory, 0)); - wasi_try_mem_ok!(ro_data_len.write(&memory, bytes_read)); - - Ok(Errno::Success) + Ok(res) } diff --git a/lib/wasi/src/syscalls/wasix/sock_send.rs b/lib/wasi/src/syscalls/wasix/sock_send.rs index b62e0bb2333..d1e8f2ec978 100644 --- a/lib/wasi/src/syscalls/wasix/sock_send.rs +++ b/lib/wasi/src/syscalls/wasix/sock_send.rs @@ -36,17 +36,9 @@ pub fn sock_send( .map(|a| a.buf_len) .sum() }; - debug!( - "wasi[{}:{}]::sock_send (fd={}, buf_len={}, flags={:?})", - ctx.data().pid(), - ctx.data().tid(), - sock, - buf_len, - si_flags - ); - let buf_len: usize = wasi_try_ok!(buf_len.try_into().map_err(|_| Errno::Inval)); + let buf_len: usize = wasi_try_ok!(buf_len.try_into().map_err(|_| Errno::Overflow)); - let bytes_written = { + let res = { if buf_len <= 10240 { let mut buf: [MaybeUninit; 10240] = unsafe { MaybeUninit::uninit().assume_init() }; let writer = &mut buf[..buf_len]; @@ -55,23 +47,47 @@ pub fn sock_send( let reader = &buf[..written]; let reader: &[u8] = unsafe { std::mem::transmute(reader) }; - wasi_try_ok!(__sock_asyncify( - env, - sock, - Rights::SOCK_SEND, - |socket, fd| async move { socket.send(env.tasks.deref(), reader, fd.flags).await }, - )) + __sock_asyncify(env, sock, Rights::SOCK_SEND, |socket, fd| async move { + socket.send(env.tasks.deref(), reader, fd.flags).await + }) } else { let mut buf = Vec::with_capacity(buf_len); wasi_try_ok!(write_bytes(&mut buf, &memory, iovs_arr)); let reader = &buf; - wasi_try_ok!(__sock_asyncify( - env, + __sock_asyncify(env, sock, Rights::SOCK_SEND, |socket, fd| async move { + socket.send(env.tasks.deref(), reader, fd.flags).await + }) + } + }; + + let mut ret = Errno::Success; + let bytes_written = match res { + Ok(bytes_written) => { + debug!( + %bytes_written, + "wasi[{}:{}]::sock_send (fd={}, buf_len={}, flags={:?})", + ctx.data().pid(), + ctx.data().tid(), + sock, + buf_len, + si_flags + ); + bytes_written + } + Err(err) => { + let socket_err = err.name(); + debug!( + %socket_err, + "wasi[{}:{}]::sock_send (fd={}, buf_len={}, flags={:?})", + ctx.data().pid(), + ctx.data().tid(), sock, - Rights::SOCK_SEND, - |socket, fd| async move { socket.send(env.tasks.deref(), reader, fd.flags).await }, - )) + buf_len, + si_flags + ); + ret = err; + 0 } }; @@ -79,5 +95,5 @@ pub fn sock_send( wasi_try_ok!(bytes_written.try_into().map_err(|_| Errno::Overflow)); wasi_try_mem_ok!(ret_data_len.write(&memory, bytes_written)); - Ok(Errno::Success) + Ok(ret) } diff --git a/lib/wasi/src/syscalls/wasix/sock_send_file.rs b/lib/wasi/src/syscalls/wasix/sock_send_file.rs index 916174a6308..0a5946291bb 100644 --- a/lib/wasi/src/syscalls/wasix/sock_send_file.rs +++ b/lib/wasi/src/syscalls/wasix/sock_send_file.rs @@ -1,7 +1,7 @@ use wasmer_vfs::AsyncReadExt; use super::*; -use crate::syscalls::*; +use crate::{syscalls::*, WasiInodes}; /// ### `sock_send_file()` /// Sends the entire contents of a file down a socket @@ -56,13 +56,11 @@ pub fn sock_send_file( let fd_flags = fd_entry.flags; let data = { - let inodes = env.state.inodes.clone(); match in_fd { __WASI_STDIN_FILENO => { - let inodes = inodes.read().unwrap(); - let mut stdin = wasi_try_ok!(inodes - .stdin_mut(&state.fs.fd_map) - .map_err(fs_error_into_wasi_err)); + let mut stdin = + wasi_try_ok!(WasiInodes::stdin_mut(&state.fs.fd_map) + .map_err(fs_error_into_wasi_err)); let data = wasi_try_ok!(__asyncify(&mut ctx, None, async move { // TODO: optimize with MaybeUninit let mut buf = vec![0u8; sub_count as usize]; @@ -81,10 +79,7 @@ pub fn sock_send_file( } let offset = fd_entry.offset.load(Ordering::Acquire) as usize; - let inode_idx = fd_entry.inode; - let inodes = inodes.read().unwrap(); - let inode = &inodes.arena[inode_idx]; - + let inode = fd_entry.inode; let data = { let mut guard = inode.write(); match guard.deref_mut() { @@ -116,7 +111,6 @@ pub fn sock_send_file( let socket = socket.clone(); let tasks = tasks.clone(); drop(guard); - drop(inodes); let data = wasi_try_ok!(__asyncify(&mut ctx, None, async { let mut buf = Vec::with_capacity(sub_count as usize); diff --git a/lib/wasi/src/syscalls/wasix/thread_spawn.rs b/lib/wasi/src/syscalls/wasix/thread_spawn.rs index b4d88b60ac3..b205b2063aa 100644 --- a/lib/wasi/src/syscalls/wasix/thread_spawn.rs +++ b/lib/wasi/src/syscalls/wasix/thread_spawn.rs @@ -144,13 +144,35 @@ pub fn thread_spawn( let mut ret = Errno::Success; if let Err(err) = spawn.call(store, user_data_low as i32, user_data_high as i32) { - debug!( - "wasi[{}:{}]::thread_spawn - thread failed - start: {}", - ctx.data(&store).pid(), - ctx.data(&store).tid(), - err - ); - ret = Errno::Noexec; + match err.downcast::() { + Ok(WasiError::Exit(0)) => ret = Errno::Success, + Ok(WasiError::Exit(code)) => { + debug!( + %code, + "wasi[{}:{}]::thread_spawn - thread exited", + ctx.data(&store).pid(), + ctx.data(&store).tid(), + ); + ret = Errno::Noexec; + } + Ok(WasiError::UnknownWasiVersion) => { + debug!( + "wasi[{}:{}]::thread_spawn - thread failed as wasi version is unknown", + ctx.data(&store).pid(), + ctx.data(&store).tid(), + ); + ret = Errno::Noexec; + } + Err(err) => { + debug!( + "wasi[{}:{}]::thread_spawn - thread failed with runtime error: {}", + ctx.data(&store).pid(), + ctx.data(&store).tid(), + err + ); + ret = Errno::Noexec; + } + } } trace!( "wasi[{}:{}]::thread_spawn - thread callback finished (reactor={:?}, ret={})", diff --git a/lib/wasi/src/syscalls/wasix/ws_connect.rs b/lib/wasi/src/syscalls/wasix/ws_connect.rs index ccfb86a97ac..e1419fd92e0 100644 --- a/lib/wasi/src/syscalls/wasix/ws_connect.rs +++ b/lib/wasi/src/syscalls/wasix/ws_connect.rs @@ -35,16 +35,15 @@ pub fn ws_connect( })?); env = ctx.data(); - let (memory, state, mut inodes) = env.get_memory_and_wasi_state_and_inodes_mut(&ctx, 0); + let (memory, state, inodes) = env.get_memory_and_wasi_state_and_inodes(&ctx, 0); let kind = Kind::Socket { socket: InodeSocket::new(InodeSocketKind::WebSocket(socket)), }; - let inode = - state - .fs - .create_inode_with_default_stat(inodes.deref_mut(), kind, false, "socket".into()); + let inode = state + .fs + .create_inode_with_default_stat(inodes, kind, false, "socket".into()); let rights = Rights::all_socket(); let fd = wasi_try_ok!(state .fs From 8d46f7f2d72ad8f62b44cf3c795ea14d6bd33759 Mon Sep 17 00:00:00 2001 From: Michael-F-Bryan Date: Thu, 16 Feb 2023 00:41:03 +0800 Subject: [PATCH 4/8] Make AsStoreRef and friends work for anything that derefs to an AsStoreRef --- lib/api/src/sys/store.rs | 37 ++++++++++++++-------------- lib/compiler/src/engine/engineref.rs | 12 +++++++++ 2 files changed, 31 insertions(+), 18 deletions(-) diff --git a/lib/api/src/sys/store.rs b/lib/api/src/sys/store.rs index 6f7cf6ac9fd..9636bfa1163 100644 --- a/lib/api/src/sys/store.rs +++ b/lib/api/src/sys/store.rs @@ -1,6 +1,9 @@ use crate::sys::tunables::BaseTunables; use derivative::Derivative; -use std::fmt; +use std::{ + fmt, + ops::{Deref, DerefMut}, +}; #[cfg(feature = "compiler")] use wasmer_compiler::{AsEngineRef, Engine, EngineBuilder, EngineRef, Tunables}; use wasmer_types::OnCalledAction; @@ -220,13 +223,6 @@ impl AsEngineRef for Store { } } -#[cfg(feature = "compiler")] -impl AsEngineRef for &Store { - fn as_engine_ref(&self) -> EngineRef<'_> { - EngineRef::new(&self.engine) - } -} - #[cfg(feature = "compiler")] impl AsEngineRef for StoreRef<'_> { fn as_engine_ref(&self) -> EngineRef<'_> { @@ -374,21 +370,26 @@ impl AsStoreMut for StoreMut<'_> { } } -impl AsStoreRef for &'_ T { +impl

AsStoreRef for P +where + P: Deref, + P::Target: AsStoreRef, +{ fn as_store_ref(&self) -> StoreRef<'_> { - T::as_store_ref(*self) + (**self).as_store_ref() } } -impl AsStoreRef for &'_ mut T { - fn as_store_ref(&self) -> StoreRef<'_> { - T::as_store_ref(*self) - } -} -impl AsStoreMut for &'_ mut T { + +impl

AsStoreMut for P +where + P: DerefMut, + P::Target: AsStoreMut, +{ fn as_store_mut(&mut self) -> StoreMut<'_> { - T::as_store_mut(*self) + (**self).as_store_mut() } + fn objects_mut(&mut self) -> &mut StoreObjects { - T::objects_mut(*self) + (**self).objects_mut() } } diff --git a/lib/compiler/src/engine/engineref.rs b/lib/compiler/src/engine/engineref.rs index 107cd9b1153..f5fa32aa551 100644 --- a/lib/compiler/src/engine/engineref.rs +++ b/lib/compiler/src/engine/engineref.rs @@ -1,3 +1,5 @@ +use core::ops::Deref; + use super::Engine; use crate::Tunables; @@ -37,3 +39,13 @@ impl AsEngineRef for EngineRef<'_> { EngineRef { inner: self.inner } } } + +impl

AsEngineRef for P +where + P: Deref, + P::Target: AsEngineRef, +{ + fn as_engine_ref(&self) -> EngineRef<'_> { + (**self).as_engine_ref() + } +} From 4473be1b043cb772b2fa897835a75a6b2a8c2e56 Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Thu, 16 Feb 2023 10:46:18 +1100 Subject: [PATCH 5/8] Replaced OsError with concrete types --- Cargo.lock | 1 + lib/vnet/Cargo.toml | 1 + lib/vnet/src/lib.rs | 32 ++++++++++++++++---- lib/wasi/src/bin_factory/module_cache.rs | 2 +- lib/wasi/src/net/mod.rs | 38 ++---------------------- 5 files changed, 32 insertions(+), 42 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d3da7bab1f3..1ec6c9fc112 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5200,6 +5200,7 @@ version = "3.2.0-alpha.1" dependencies = [ "async-trait", "bytes", + "libc", "thiserror", "tracing", "wasmer-vfs", diff --git a/lib/vnet/Cargo.toml b/lib/vnet/Cargo.toml index 1694217da2f..0d04bcdd940 100644 --- a/lib/vnet/Cargo.toml +++ b/lib/vnet/Cargo.toml @@ -12,6 +12,7 @@ wasmer-vfs = { path = "../vfs", version = "=3.2.0-alpha.1", default-features = f bytes = "1" async-trait = { version = "^0.1" } tracing = "0.1" +libc = { version = "^0.2", default-features = false } [features] default = [] diff --git a/lib/vnet/src/lib.rs b/lib/vnet/src/lib.rs index fdbd1b11471..cdbb5ff0371 100644 --- a/lib/vnet/src/lib.rs +++ b/lib/vnet/src/lib.rs @@ -477,6 +477,9 @@ pub enum NetworkError { /// A pipe was closed #[error("broken pipe (was closed)")] BrokenPipe, + /// Insufficient memory + #[error("Insufficient memory")] + InsufficientMemory, /// The connection was aborted #[error("connection aborted")] ConnectionAborted, @@ -516,9 +519,9 @@ pub enum NetworkError { /// A call to write returned 0 #[error("write returned 0")] WriteZero, - /// OS error - #[error("operating system error({0})")] - OsError(i32), + /// Too many open files + #[error("too many open files")] + TooManyOpenFiles, /// The operation is not supported. #[error("unsupported")] Unsupported, @@ -551,7 +554,8 @@ pub fn net_error_into_io_err(net_error: NetworkError) -> std::io::Error { NetworkError::UnexpectedEof => ErrorKind::UnexpectedEof.into(), NetworkError::WouldBlock => ErrorKind::WouldBlock.into(), NetworkError::WriteZero => ErrorKind::WriteZero.into(), - NetworkError::OsError(code) => Error::from_raw_os_error(code), + NetworkError::TooManyOpenFiles => Error::from_raw_os_error(libc::EMFILE), + NetworkError::InsufficientMemory => Error::from_raw_os_error(libc::ENOMEM), NetworkError::Unsupported => ErrorKind::Unsupported.into(), NetworkError::UnknownError => ErrorKind::BrokenPipe.into(), } @@ -579,7 +583,25 @@ pub fn io_err_into_net_error(net_error: std::io::Error) -> NetworkError { ErrorKind::Unsupported => NetworkError::Unsupported, _ => { if let Some(code) = net_error.raw_os_error() { - NetworkError::OsError(code) + match code { + libc::EPERM => NetworkError::PermissionDenied, + libc::EBADF => NetworkError::InvalidFd, + libc::ECHILD => NetworkError::InvalidFd, + libc::EMFILE => NetworkError::TooManyOpenFiles, + libc::EINTR => NetworkError::Interrupted, + libc::EIO => NetworkError::IOError, + libc::ENXIO => NetworkError::IOError, + libc::EAGAIN => NetworkError::WouldBlock, + libc::ENOMEM => NetworkError::InsufficientMemory, + libc::EACCES => NetworkError::PermissionDenied, + libc::ENODEV => NetworkError::NoDevice, + libc::EINVAL => NetworkError::InvalidInput, + libc::EPIPE => NetworkError::BrokenPipe, + err => { + tracing::trace!("unknown os error {}", err); + NetworkError::UnknownError + } + } } else { NetworkError::UnknownError } diff --git a/lib/wasi/src/bin_factory/module_cache.rs b/lib/wasi/src/bin_factory/module_cache.rs index d943a0b1aa0..5e8ad83e788 100644 --- a/lib/wasi/src/bin_factory/module_cache.rs +++ b/lib/wasi/src/bin_factory/module_cache.rs @@ -278,7 +278,7 @@ mod tests { let mut store = Vec::new(); for _ in 0..2 { let webc = cache - .get_webc("sharrattj/dash", &rt, tasks.deref()) + .get_webc("sharrattj/dash", &rt, std::ops::Deref::deref(tasks)) .unwrap(); store.push(webc); tasks diff --git a/lib/wasi/src/net/mod.rs b/lib/wasi/src/net/mod.rs index 5d84b77d7c8..e64065ae72b 100644 --- a/lib/wasi/src/net/mod.rs +++ b/lib/wasi/src/net/mod.rs @@ -386,42 +386,8 @@ pub fn net_error_into_wasi_err(net_error: NetworkError) -> Errno { NetworkError::UnexpectedEof => Errno::Proto, NetworkError::WouldBlock => Errno::Again, NetworkError::WriteZero => Errno::Nospc, - NetworkError::OsError(code) => match code { - libc::EPERM => Errno::Perm, - libc::ENOENT => Errno::Noent, - libc::ESRCH => Errno::Srch, - libc::EINTR => Errno::Intr, - libc::EIO => Errno::Io, - libc::ENXIO => Errno::Nxio, - libc::E2BIG => Errno::Toobig, - libc::ENOEXEC => Errno::Noexec, - libc::EBADF => Errno::Badf, - libc::ECHILD => Errno::Badf, - libc::EAGAIN => Errno::Again, - libc::ENOMEM => Errno::Badf, - libc::EACCES => Errno::Access, - libc::EFAULT => Errno::Fault, - libc::EBUSY => Errno::Busy, - libc::EEXIST => Errno::Exist, - libc::EXDEV => Errno::Xdev, - libc::ENODEV => Errno::Nodev, - libc::ENOTDIR => Errno::Notdir, - libc::EISDIR => Errno::Isdir, - libc::EINVAL => Errno::Inval, - libc::ENFILE => Errno::Nfile, - libc::EMFILE => Errno::Mfile, - libc::ENOTTY => Errno::Notty, - libc::ETXTBSY => Errno::Txtbsy, - libc::EFBIG => Errno::Fbig, - libc::ENOSPC => Errno::Nospc, - libc::ESPIPE => Errno::Spipe, - libc::EROFS => Errno::Rofs, - libc::EMLINK => Errno::Mlink, - libc::EPIPE => Errno::Pipe, - libc::EDOM => Errno::Dom, - libc::ERANGE => Errno::Range, - _ => Errno::Io, - }, + NetworkError::TooManyOpenFiles => Errno::Mfile, + NetworkError::InsufficientMemory => Errno::Nomem, NetworkError::Unsupported => Errno::Notsup, NetworkError::UnknownError => Errno::Io, } From 4f117a6b63222d403a1e1bed03ff5fb15b3527ed Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Thu, 16 Feb 2023 11:57:06 +1100 Subject: [PATCH 6/8] Moved the notification implementation into a private scope --- lib/wasi/src/fs/fd.rs | 22 +---- lib/wasi/src/fs/inode_guard.rs | 30 +------ lib/wasi/src/fs/mod.rs | 4 +- lib/wasi/src/fs/notification.rs | 109 +++++++++++++++++++++++++ lib/wasi/src/syscalls/wasi/fd_event.rs | 9 +- lib/wasi/src/syscalls/wasi/fd_read.rs | 49 +---------- lib/wasi/src/syscalls/wasi/fd_write.rs | 9 +- 7 files changed, 127 insertions(+), 105 deletions(-) create mode 100644 lib/wasi/src/fs/notification.rs diff --git a/lib/wasi/src/fs/fd.rs b/lib/wasi/src/fs/fd.rs index c36780d1034..e96844dd0ca 100644 --- a/lib/wasi/src/fs/fd.rs +++ b/lib/wasi/src/fs/fd.rs @@ -1,9 +1,8 @@ use std::{ borrow::Cow, - collections::{HashMap, VecDeque}, + collections::HashMap, path::PathBuf, - sync::{atomic::AtomicU64, Arc, Mutex, RwLock, RwLockReadGuard, RwLockWriteGuard}, - task::Waker, + sync::{atomic::AtomicU64, Arc, RwLock, RwLockReadGuard, RwLockWriteGuard}, }; #[cfg(feature = "enable-serde")] @@ -13,7 +12,7 @@ use wasmer_wasi_types::wasi::{Fd as WasiFd, Fdflags, Filestat, Rights}; use crate::net::socket::InodeSocket; -use super::{InodeGuard, InodeWeakGuard}; +use super::{InodeGuard, InodeWeakGuard, NotificationInner}; #[derive(Debug, Clone)] #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] @@ -70,21 +69,6 @@ impl InodeVal { } } -#[derive(Debug)] -#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] -pub struct NotificationInner { - /// Used for event notifications by the user application or operating system - /// (positive number means there are events waiting to be processed) - pub counter: AtomicU64, - /// Counter used to prevent duplicate notification events - pub last_poll: AtomicU64, - /// Flag that indicates if this is operating - pub is_semaphore: bool, - /// Receiver that wakes sleeping threads - #[cfg_attr(feature = "enable-serde", serde(skip))] - pub wakers: Mutex>, -} - /// The core of the filesystem abstraction. Includes directories, /// files, and symlinks. #[derive(Debug)] diff --git a/lib/wasi/src/fs/inode_guard.rs b/lib/wasi/src/fs/inode_guard.rs index 6f1f832033b..98bf4af5c06 100644 --- a/lib/wasi/src/fs/inode_guard.rs +++ b/lib/wasi/src/fs/inode_guard.rs @@ -3,7 +3,7 @@ use std::{ io::{IoSlice, SeekFrom}, ops::{Deref, DerefMut}, pin::Pin, - sync::{atomic::Ordering, Arc, RwLock, RwLockReadGuard, RwLockWriteGuard}, + sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard}, task::{Context, Poll}, }; @@ -16,7 +16,7 @@ use wasmer_wasi_types::{ wasi::{Errno, Event, EventFdReadwrite, EventUnion, Eventrwflags, Subscription}, }; -use super::{fd::NotificationInner, InodeGuard, Kind}; +use super::{notification::NotificationInner, InodeGuard, Kind}; use crate::{ net::socket::{InodeSocketInner, InodeSocketKind}, state::{iterate_poll_events, PollEvent, PollEventSet, WasiState}, @@ -210,18 +210,7 @@ impl<'a> Future for InodeValFilePollGuardJoin<'a> { file.poll_read_ready(cx) } InodeValFilePollGuardMode::EventNotifications(inner) => { - { - let mut guard = inner.wakers.lock().unwrap(); - if guard.iter().any(|a| a.will_wake(waker)) == false { - guard.push_front(waker.clone()); - } - } - let val = inner.counter.load(Ordering::Acquire); - if inner.last_poll.swap(val, Ordering::AcqRel) != val { - Poll::Ready(Ok(val as usize)) - } else { - Poll::Pending - } + inner.poll(waker).map(|a| Ok(a)) } InodeValFilePollGuardMode::Socket { ref inner } => { let mut guard = inner.protected.write().unwrap(); @@ -309,18 +298,7 @@ impl<'a> Future for InodeValFilePollGuardJoin<'a> { file.poll_write_ready(cx) } InodeValFilePollGuardMode::EventNotifications(inner) => { - { - let mut guard = inner.wakers.lock().unwrap(); - if guard.iter().any(|a| a.will_wake(waker)) == false { - guard.push_front(waker.clone()); - } - } - let val = inner.counter.load(Ordering::Acquire); - if inner.last_poll.swap(val, Ordering::AcqRel) != val { - Poll::Ready(Ok(val as usize)) - } else { - Poll::Pending - } + inner.poll(waker).map(|a| Ok(a)) } InodeValFilePollGuardMode::Socket { ref inner } => { let mut guard = inner.protected.write().unwrap(); diff --git a/lib/wasi/src/fs/mod.rs b/lib/wasi/src/fs/mod.rs index 5d1b7b10188..fa734833dc6 100644 --- a/lib/wasi/src/fs/mod.rs +++ b/lib/wasi/src/fs/mod.rs @@ -1,5 +1,6 @@ mod fd; mod inode_guard; +mod notification; use std::{ borrow::{Borrow, Cow}, @@ -26,11 +27,12 @@ use wasmer_wasi_types::{ }, }; -pub use self::fd::{Fd, InodeVal, Kind, NotificationInner}; +pub use self::fd::{Fd, InodeVal, Kind}; pub(crate) use self::inode_guard::{ InodeValFilePollGuard, InodeValFilePollGuardJoin, InodeValFileReadGuard, InodeValFileWriteGuard, WasiStateFileGuard, }; +pub use self::notification::NotificationInner; use crate::syscalls::map_io_err; use crate::{bin_factory::BinaryPackage, state::PreopenedDir, ALL_RIGHTS}; diff --git a/lib/wasi/src/fs/notification.rs b/lib/wasi/src/fs/notification.rs new file mode 100644 index 00000000000..71972ad7b41 --- /dev/null +++ b/lib/wasi/src/fs/notification.rs @@ -0,0 +1,109 @@ +use std::{ + collections::VecDeque, + sync::Mutex, + task::{Poll, Waker}, +}; + +#[derive(Debug)] +#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] +struct NotificationState { + /// Used for event notifications by the user application or operating system + /// (positive number means there are events waiting to be processed) + counter: u64, + /// Counter used to prevent duplicate notification events + last_poll: u64, + /// Flag that indicates if this is operating + is_semaphore: bool, + /// All the registered wakers + #[cfg_attr(feature = "enable-serde", serde(skip))] + wakers: VecDeque, +} + +impl NotificationState { + fn add_waker(&mut self, waker: &Waker) { + if !self.wakers.iter().any(|a| a.will_wake(waker)) { + self.wakers.push_front(waker.clone()); + } + } + + fn wake_all(&mut self) { + self.last_poll = u64::MAX; + while let Some(waker) = self.wakers.pop_front() { + waker.wake(); + } + } + + fn inc(&mut self, val: u64) { + self.counter = self.counter + val; + self.wake_all(); + } + + fn dec(&mut self) -> u64 { + let val = self.counter; + if self.is_semaphore { + if self.counter > 0 { + self.counter = self.counter - 1; + if self.counter > 0 { + self.wake_all(); + } + } + } else { + self.counter = 0; + } + val + } +} + +#[derive(Debug)] +#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] +pub struct NotificationInner { + /// Receiver that wakes sleeping threads + #[cfg_attr(feature = "enable-serde", serde(skip))] + state: Mutex, +} + +impl NotificationInner { + pub fn new(initial_val: u64, is_semaphore: bool) -> Self { + Self { + state: Mutex::new(NotificationState { + counter: initial_val, + last_poll: u64::MAX, + is_semaphore, + wakers: Default::default(), + }), + } + } + pub fn poll(&self, waker: &Waker) -> Poll { + let mut state = self.state.lock().unwrap(); + state.add_waker(waker); + + if state.last_poll != state.counter { + state.last_poll = state.counter; + Poll::Ready(state.counter as usize) + } else { + Poll::Pending + } + } + + pub fn write(&self, val: u64) { + let mut state = self.state.lock().unwrap(); + state.inc(val); + } + + pub fn read(&self, waker: &Waker) -> Poll { + let mut state = self.state.lock().unwrap(); + state.add_waker(waker); + match state.dec() { + 0 => Poll::Pending, + res => Poll::Ready(res), + } + } + + pub fn try_read(&self) -> Option { + let mut state = self.state.lock().unwrap(); + match state.dec() { + 0 => None, + res => Some(res), + } + } +} diff --git a/lib/wasi/src/syscalls/wasi/fd_event.rs b/lib/wasi/src/syscalls/wasi/fd_event.rs index e4bc8146c30..abd9ff1ff1e 100644 --- a/lib/wasi/src/syscalls/wasi/fd_event.rs +++ b/lib/wasi/src/syscalls/wasi/fd_event.rs @@ -14,12 +14,9 @@ pub fn fd_event( let env = ctx.data(); let (memory, state, mut inodes) = env.get_memory_and_wasi_state_and_inodes(&ctx, 0); - let kind = Kind::EventNotifications(Arc::new(NotificationInner { - counter: AtomicU64::new(initial_val), - last_poll: AtomicU64::new(0), - is_semaphore: flags & EVENT_FD_FLAGS_SEMAPHORE != 0, - wakers: Default::default(), - })); + let is_semaphore = flags & EVENT_FD_FLAGS_SEMAPHORE != 0; + let kind = + Kind::EventNotifications(Arc::new(NotificationInner::new(initial_val, is_semaphore))); let inode = state.fs.create_inode_with_default_stat( inodes.deref(), diff --git a/lib/wasi/src/syscalls/wasi/fd_read.rs b/lib/wasi/src/syscalls/wasi/fd_read.rs index 65d3f5e71b6..0c947a5c1e8 100644 --- a/lib/wasi/src/syscalls/wasi/fd_read.rs +++ b/lib/wasi/src/syscalls/wasi/fd_read.rs @@ -355,51 +355,10 @@ fn fd_read_internal( impl Future for NotifyPoller { type Output = Result; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - if !self.non_blocking { - let waker = cx.waker(); - let mut guard = self.inner.wakers.lock().unwrap(); - if guard.iter().any(|w| w.will_wake(waker)) == false { - guard.push_front(waker.clone()); - } - } - - let wake_all = || { - self.inner.last_poll.store(u64::MAX, Ordering::Release); - let mut guard = self.inner.wakers.lock().unwrap(); - while let Some(waker) = guard.pop_front() { - waker.wake(); - } - }; - - loop { - let val = self.inner.counter.load(Ordering::Acquire); - if val > 0 { - let new_val = if self.inner.is_semaphore { val - 1 } else { 0 }; - if self - .inner - .counter - .compare_exchange( - val, - new_val, - Ordering::AcqRel, - Ordering::Acquire, - ) - .is_ok() - { - wake_all(); - return Poll::Ready(Ok(val)); - } else { - continue; - } - } - - // If its none blocking then exit - if self.non_blocking { - wake_all(); - return Poll::Ready(Err(Errno::Again)); - } - - return Poll::Pending; + if self.non_blocking { + Poll::Ready(self.inner.try_read().ok_or(Errno::Again)) + } else { + self.inner.read(cx.waker()).map(|a| Ok(a)) } } } diff --git a/lib/wasi/src/syscalls/wasi/fd_write.rs b/lib/wasi/src/syscalls/wasi/fd_write.rs index b59ecde0fdd..eb500a03141 100644 --- a/lib/wasi/src/syscalls/wasi/fd_write.rs +++ b/lib/wasi/src/syscalls/wasi/fd_write.rs @@ -208,14 +208,7 @@ fn fd_write_internal( } let val = u64::from_ne_bytes(unsafe { std::mem::transmute(val) }); - inner.counter.fetch_add(val, Ordering::AcqRel); - inner.last_poll.store(u64::MAX, Ordering::Release); - { - let mut guard = inner.wakers.lock().unwrap(); - while let Some(wake) = guard.pop_back() { - wake.wake(); - } - } + inner.write(val); (written, false) } From e82fc87a1d53f68b9a651b6afe96c3360fd30d1e Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Thu, 16 Feb 2023 12:06:33 +1100 Subject: [PATCH 7/8] Made the replace operation a bit easier to read --- lib/wasi/src/fs/inode_guard.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/wasi/src/fs/inode_guard.rs b/lib/wasi/src/fs/inode_guard.rs index 98bf4af5c06..1b1583e3580 100644 --- a/lib/wasi/src/fs/inode_guard.rs +++ b/lib/wasi/src/fs/inode_guard.rs @@ -1,6 +1,7 @@ use std::{ future::Future, io::{IoSlice, SeekFrom}, + mem::replace, ops::{Deref, DerefMut}, pin::Pin, sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard}, @@ -174,7 +175,7 @@ impl<'a> Future for InodeValFilePollGuardJoin<'a> { } }; if is_closed { - std::mem::replace(&mut guard.notifications.closed, true) == false + replace(&mut guard.notifications.closed, true) == false } else { false } @@ -218,7 +219,7 @@ impl<'a> Future for InodeValFilePollGuardJoin<'a> { match res { Poll::Ready(Err(err)) if is_err_closed(&err) => { tracing::trace!("socket read ready error (fd={}) - {}", fd, err); - if std::mem::replace(&mut guard.notifications.closed, true) == false { + if replace(&mut guard.notifications.closed, true) == false { Poll::Ready(Ok(0)) } else { Poll::Pending @@ -226,7 +227,7 @@ impl<'a> Future for InodeValFilePollGuardJoin<'a> { } Poll::Ready(Err(err)) => { tracing::debug!("poll socket error - {}", err); - if std::mem::replace(&mut guard.notifications.failed, true) == false { + if replace(&mut guard.notifications.failed, true) == false { Poll::Ready(Ok(0)) } else { Poll::Pending @@ -306,7 +307,7 @@ impl<'a> Future for InodeValFilePollGuardJoin<'a> { match res { Poll::Ready(Err(err)) if is_err_closed(&err) => { tracing::trace!("socket write ready error (fd={}) - {}", fd, err); - if std::mem::replace(&mut guard.notifications.closed, true) == false { + if replace(&mut guard.notifications.closed, true) == false { Poll::Ready(Ok(0)) } else { Poll::Pending @@ -314,7 +315,7 @@ impl<'a> Future for InodeValFilePollGuardJoin<'a> { } Poll::Ready(Err(err)) => { tracing::debug!("poll socket error - {}", err); - if std::mem::replace(&mut guard.notifications.failed, true) == false { + if replace(&mut guard.notifications.failed, true) == false { Poll::Ready(Ok(0)) } else { Poll::Pending From 31c6eca32eeefaae422bf17c0048d1bd8b0d0300 Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Thu, 16 Feb 2023 14:54:16 +1100 Subject: [PATCH 8/8] Moved the OwnedRwReadLock and OwnedRwWriteLock implementation(s) into a privata mod and added some extra protection against race conditions --- lib/wasi/src/fs/inode_guard.rs | 24 +-- lib/wasi/src/utils/mod.rs | 4 + lib/wasi/src/utils/owned_mutex_guard.rs | 247 ++++++++++++++++++++++++ 3 files changed, 258 insertions(+), 17 deletions(-) create mode 100644 lib/wasi/src/utils/owned_mutex_guard.rs diff --git a/lib/wasi/src/fs/inode_guard.rs b/lib/wasi/src/fs/inode_guard.rs index 1b1583e3580..29f44aa6796 100644 --- a/lib/wasi/src/fs/inode_guard.rs +++ b/lib/wasi/src/fs/inode_guard.rs @@ -4,7 +4,7 @@ use std::{ mem::replace, ops::{Deref, DerefMut}, pin::Pin, - sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard}, + sync::{Arc, RwLock}, task::{Context, Poll}, }; @@ -22,6 +22,7 @@ use crate::{ net::socket::{InodeSocketInner, InodeSocketKind}, state::{iterate_poll_events, PollEvent, PollEventSet, WasiState}, syscalls::map_io_err, + utils::{OwnedRwLockReadGuard, OwnedRwLockWriteGuard}, }; pub(crate) enum InodeValFilePollGuardMode { @@ -389,18 +390,13 @@ impl<'a> Future for InodeValFilePollGuardJoin<'a> { #[derive(Debug)] pub(crate) struct InodeValFileReadGuard { - guard: RwLockReadGuard<'static, Box>, - // we must keep a reference as the lifetime of the guard becomes owned using unsafe code - // (warning!! The fields of a struct are dropped in declaration order thus this must be at the end) - _file: Arc>>, + guard: OwnedRwLockReadGuard>, } impl InodeValFileReadGuard { pub(crate) fn new(file: &Arc>>) -> Self { - let guard = file.read().unwrap(); Self { - _file: file.clone(), - guard: unsafe { std::mem::transmute(guard) }, + guard: crate::utils::read_owned(file).unwrap(), } } } @@ -416,7 +412,7 @@ impl InodeValFileReadGuard { fd, peb, subscription, - mode: InodeValFilePollGuardMode::File(self._file), + mode: InodeValFilePollGuardMode::File(self.guard.into_inner()), } } } @@ -430,19 +426,13 @@ impl Deref for InodeValFileReadGuard { #[derive(Debug)] pub struct InodeValFileWriteGuard { - guard: RwLockWriteGuard<'static, Box>, - // we must keep a reference as the lifetime of the guard becomes owned using unsafe code - // (warning!! The fields of a struct are dropped in declaration order thus this must be at the end) - // (https://doc.rust-lang.org/reference/destructors.html#:~:text=The%20fields%20of%20a%20struct,first%20element%20to%20the%20last.) - _file: Arc>>, + guard: OwnedRwLockWriteGuard>, } impl InodeValFileWriteGuard { pub(crate) fn new(file: &Arc>>) -> Self { - let guard = file.write().unwrap(); Self { - _file: file.clone(), - guard: unsafe { std::mem::transmute(guard) }, + guard: crate::utils::write_owned(file).unwrap(), } } pub(crate) fn swap( diff --git a/lib/wasi/src/utils/mod.rs b/lib/wasi/src/utils/mod.rs index 595da6f69f4..260e81b6053 100644 --- a/lib/wasi/src/utils/mod.rs +++ b/lib/wasi/src/utils/mod.rs @@ -1,3 +1,4 @@ +mod owned_mutex_guard; pub mod store; mod thread_parker; @@ -10,6 +11,9 @@ use wasmer::Module; use wasmer_wasi_types::wasi::Errno; pub use self::thread_parker::WasiParkingLot; +pub(crate) use owned_mutex_guard::{ + read_owned, write_owned, OwnedRwLockReadGuard, OwnedRwLockWriteGuard, +}; /// Check if a provided module is compiled for some version of WASI. /// Use [`get_wasi_version`] to find out which version of WASI the module is. diff --git a/lib/wasi/src/utils/owned_mutex_guard.rs b/lib/wasi/src/utils/owned_mutex_guard.rs new file mode 100644 index 00000000000..9788506fdb0 --- /dev/null +++ b/lib/wasi/src/utils/owned_mutex_guard.rs @@ -0,0 +1,247 @@ +/// Extends the standard library RwLock to include an owned version of the read +/// and write locking functions. +/// +/// This implementation contains unsafe code and has two levels of protection +/// that prevent the lock from being released after the memory is freed. +/// +/// 1. The internals use a Option which is cleared before the Drop completes +/// 2. The Arc reference is placed as the last field which should be dropped last +/// (https://doc.rust-lang.org/reference/destructors.html#:~:text=The%20fields%20of%20a%20struct,first%20element%20to%20the%20last.) +use std::ops::{Deref, DerefMut}; +use std::sync::{Arc, LockResult, PoisonError, RwLock, RwLockReadGuard, RwLockWriteGuard}; + +/// Locks this rwlock with shared read access, blocking the current thread +/// until it can be acquired. +/// +/// The calling thread will be blocked until there are no more writers which +/// hold the lock. There may be other readers currently inside the lock when +/// this method returns. This method does not provide any guarantees with +/// respect to the ordering of whether contentious readers or writers will +/// acquire the lock first. +/// +/// Returns an RAII guard which will release this thread's shared access +/// once it is dropped. +/// +/// # Errors +/// +/// This function will return an error if the RwLock is poisoned. An RwLock +/// is poisoned whenever a writer panics while holding an exclusive lock. +/// The failure will occur immediately after the lock has been acquired. +/// +/// # Panics +/// +/// This function might panic when called if the lock is already held by the current thread. +/// +/// # Examples +/// +/// ``` +/// use std::sync::{Arc, RwLock}; +/// use std::thread; +/// use crate::utils::read_owned; +/// +/// let lock = Arc::new(RwLock::new(1)); +/// let c_lock = Arc::clone(&lock); +/// +/// let n = read_owned(&lock).unwrap(); +/// assert_eq!(*n, 1); +/// +/// thread::spawn(move || { +/// let r = read_owned(&c_lock); +/// assert!(r.is_ok()); +/// }).join().unwrap(); +/// ``` +pub(crate) fn read_owned(lock: &Arc>) -> LockResult> { + OwnedRwLockReadGuard::new(lock) +} + +/// Locks this rwlock with exclusive write access, blocking the current +/// thread until it can be acquired. +/// +/// This function will not return while other writers or other readers +/// currently have access to the lock. +/// +/// Returns an RAII guard which will drop the write access of this rwlock +/// when dropped. +/// +/// # Errors +/// +/// This function will return an error if the RwLock is poisoned. An RwLock +/// is poisoned whenever a writer panics while holding an exclusive lock. +/// An error will be returned when the lock is acquired. +/// +/// # Panics +/// +/// This function might panic when called if the lock is already held by the current thread. +/// +/// # Examples +/// +/// ``` +/// use std::sync::RwLock; +/// use crate::utils::write_owned; +/// +/// let lock = RwLock::new(1); +/// +/// let mut n = write_owned(&lock).unwrap(); +/// *n = 2; +/// ``` +pub(crate) fn write_owned(lock: &Arc>) -> LockResult> { + OwnedRwLockWriteGuard::new(lock) +} + +pub(crate) struct OwnedRwLockReadGuard { + // This option is guaranteed to be `.is_some()` while in scope and cleared during the `Drop` + guard: Option>, + // as a precaution we keep the reference as the last field so that it is destructed after the guard + // (https://doc.rust-lang.org/reference/destructors.html#:~:text=The%20fields%20of%20a%20struct,first%20element%20to%20the%20last.) + #[allow(unused)] + ownership: Arc>, +} + +impl Drop for OwnedRwLockReadGuard +where + T: Sized, +{ + fn drop(&mut self) { + // we must close the lock before we release the arc reference + self.guard.take(); + } +} + +impl std::fmt::Debug for OwnedRwLockReadGuard +where + T: std::fmt::Debug, +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(guard) = self.guard.as_ref() { + write!(f, "{:?}", guard) + } else { + write!(f, "none") + } + } +} + +impl std::fmt::Display for OwnedRwLockReadGuard +where + T: std::fmt::Display, +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(guard) = self.guard.as_ref() { + write!(f, "{}", guard) + } else { + write!(f, "none") + } + } +} + +impl OwnedRwLockReadGuard { + fn new(lock: &Arc>) -> LockResult { + let conv = |guard: RwLockReadGuard<'_, T>| { + let guard: RwLockReadGuard<'static, T> = unsafe { std::mem::transmute(guard) }; + Self { + ownership: lock.clone(), + guard: Some(guard), + } + }; + let guard = lock.read().map_err(|err| { + let guard = err.into_inner(); + PoisonError::new(conv(guard)) + })?; + Ok(conv(guard)) + } + + /// Converts this guard into an owned reference of the underlying lockable object + #[allow(dead_code)] + pub fn into_inner(self) -> Arc> { + self.ownership.clone() + } +} + +impl Deref for OwnedRwLockReadGuard { + type Target = T; + + fn deref(&self) -> &Self::Target { + self.guard.as_ref().unwrap() + } +} + +pub(crate) struct OwnedRwLockWriteGuard { + // This option is guaranteed to be `.is_some()` while in scope and cleared during the `Drop` + guard: Option>, + // as a precaution we keep the reference as the last field so that it is destructed after the guard + // (https://doc.rust-lang.org/reference/destructors.html#:~:text=The%20fields%20of%20a%20struct,first%20element%20to%20the%20last.) + #[allow(unused)] + ownership: Arc>, +} + +impl Drop for OwnedRwLockWriteGuard +where + T: Sized, +{ + fn drop(&mut self) { + // we must close the lock before we release the arc reference + self.guard.take(); + } +} + +impl std::fmt::Debug for OwnedRwLockWriteGuard +where + T: std::fmt::Debug, +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(guard) = self.guard.as_ref() { + write!(f, "{:?}", guard) + } else { + write!(f, "none") + } + } +} + +impl std::fmt::Display for OwnedRwLockWriteGuard +where + T: std::fmt::Display, +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(guard) = self.guard.as_ref() { + write!(f, "{}", guard) + } else { + write!(f, "none") + } + } +} + +impl OwnedRwLockWriteGuard { + fn new(lock: &Arc>) -> LockResult { + let conv = |guard: RwLockWriteGuard<'_, T>| { + let guard: RwLockWriteGuard<'static, T> = unsafe { std::mem::transmute(guard) }; + Self { + ownership: lock.clone(), + guard: Some(guard), + } + }; + let guard = lock.write().map_err(|err| { + let guard = err.into_inner(); + PoisonError::new(conv(guard)) + })?; + Ok(conv(guard)) + } + + /// Converts this guard into an owned reference of the underlying lockable object + #[allow(dead_code)] + pub fn into_inner(self) -> Arc> { + self.ownership.clone() + } +} + +impl Deref for OwnedRwLockWriteGuard { + type Target = T; + + fn deref(&self) -> &Self::Target { + self.guard.as_ref().unwrap() + } +} + +impl DerefMut for OwnedRwLockWriteGuard { + fn deref_mut(&mut self) -> &mut Self::Target { + self.guard.as_mut().unwrap() + } +}