diff --git a/Cargo.lock b/Cargo.lock index dd71857fd54..58eae432123 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" @@ -1751,24 +1778,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", @@ -2439,7 +2465,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]] @@ -2456,6 +2492,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" @@ -3005,7 +3054,7 @@ dependencies = [ "cc", "libc", "once_cell", - "spin", + "spin 0.5.2", "untrusted", "web-sys", "winapi", @@ -3409,7 +3458,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", ] @@ -3526,6 +3575,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" @@ -5144,7 +5202,9 @@ version = "3.2.0-alpha.1" dependencies = [ "async-trait", "bytes", + "libc", "thiserror", + "tracing", "wasmer-vfs", ] @@ -5161,8 +5221,8 @@ dependencies = [ "cooked-waker", "derivative", "futures", - "generational-arena", "getrandom", + "heapless", "hex", "http", "lazy_static", diff --git a/lib/api/src/sys/mem_access.rs b/lib/api/src/sys/mem_access.rs index 3075ba60e21..753d403c5c0 100644 --- a/lib/api/src/sys/mem_access.rs +++ b/lib/api/src/sys/mem_access.rs @@ -315,7 +315,10 @@ impl<'a, T: ValueType> WasmSlice<'a, T> { /// Reads this `WasmSlice` into a `slice`. #[inline] - pub fn read_to_slice(self, buf: &mut [MaybeUninit]) -> Result { + 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) 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-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") 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() + } +} diff --git a/lib/vnet/Cargo.toml b/lib/vnet/Cargo.toml index 20129859033..9272d26b6a7 100644 --- a/lib/vnet/Cargo.toml +++ b/lib/vnet/Cargo.toml @@ -11,3 +11,5 @@ 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" +libc = { version = "^0.2", default-features = false } diff --git a/lib/vnet/src/lib.rs b/lib/vnet/src/lib.rs index a2c85134436..cdbb5ff0371 100644 --- a/lib/vnet/src/lib.rs +++ b/lib/vnet/src/lib.rs @@ -273,7 +273,7 @@ pub trait VirtualConnectedSocket: VirtualSocket + fmt::Debug + Send + Sync + 'st ) -> Poll>; /// Recv a packet from the socket - fn try_recv(&mut self, buf: &mut [MaybeUninit]) -> Result; + fn try_recv<'a>(&mut self, buf: &'a mut [MaybeUninit]) -> Result; } /// Connectionless sockets are able to send and receive datagrams and stream @@ -300,7 +300,7 @@ pub trait VirtualConnectionlessSocket: VirtualSocket + fmt::Debug + Send + Sync ) -> Poll>; /// Recv a packet from the socket - fn try_recv_from(&mut self, buf: &mut [MaybeUninit]) -> Result<(usize, SocketAddr)>; + 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 @@ -333,7 +333,7 @@ pub trait VirtualRawSocket: VirtualSocket + fmt::Debug + Send + Sync + 'static { ) -> Poll>; /// Recv a packet from the socket - fn try_recv(&mut self, buf: &mut [MaybeUninit]) -> 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 @@ -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,6 +519,9 @@ pub enum NetworkError { /// A call to write returned 0 #[error("write returned 0")] WriteZero, + /// Too many open files + #[error("too many open files")] + TooManyOpenFiles, /// The operation is not supported. #[error("unsupported")] Unsupported, @@ -525,6 +531,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(), @@ -547,6 +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::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(), } @@ -572,6 +581,30 @@ 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() { + 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-experimental-io-devices/src/lib.rs b/lib/wasi-experimental-io-devices/src/lib.rs index 7d8accb6f07..4d9010789f6 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::fs::WasiFs; use wasmer_wasi::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 162de00d07c..58e1854553d 100644 --- a/lib/wasi-local-networking/src/lib.rs +++ b/lib/wasi-local-networking/src/lib.rs @@ -155,6 +155,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) @@ -309,7 +315,7 @@ impl VirtualConnectedSocket for LocalTcpStream { } } - fn try_recv(&mut self, buf: &mut [MaybeUninit]) -> Result { + 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) } @@ -528,7 +534,7 @@ impl VirtualConnectionlessSocket for LocalUdpSocket { } } - fn try_recv_from(&mut self, buf: &mut [MaybeUninit]) -> Result<(usize, SocketAddr)> { + 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 .try_recv_from(buf) diff --git a/lib/wasi-types/schema/wasi/typenames.wit b/lib/wasi-types/schema/wasi/typenames.wit index 8288f3853e2..e2ca633e970 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 7c79cb90548..c9605026215 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" } @@ -57,6 +56,7 @@ termios = { version = "0.3", optional = true } 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" once_cell = "1.17.0" pin-project = "1.0.12" @@ -121,6 +121,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/bin_factory/module_cache.rs b/lib/wasi/src/bin_factory/module_cache.rs index c3e9aaf9b54..5e8ad83e788 100644 --- a/lib/wasi/src/bin_factory/module_cache.rs +++ b/lib/wasi/src/bin_factory/module_cache.rs @@ -251,7 +251,6 @@ impl ModuleCache { mod tests { use std::time::Duration; - use lazy_static::__Deref; use tracing_subscriber::{ filter, prelude::__tracing_subscriber_SubscriberExt, util::SubscriberInitExt, Layer, }; @@ -279,10 +278,12 @@ 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.block_on(tasks.sleep_now(Duration::from_secs(1))); + tasks + .runtime() + .block_on(tasks.sleep_now(Duration::from_secs(1))); } } } diff --git a/lib/wasi/src/fs/fd.rs b/lib/wasi/src/fs/fd.rs index dd601763e9c..e96844dd0ca 100644 --- a/lib/wasi/src/fs/fd.rs +++ b/lib/wasi/src/fs/fd.rs @@ -1,14 +1,10 @@ use std::{ borrow::Cow, - collections::{HashMap, VecDeque}, + collections::HashMap, path::PathBuf, - sync::{ - atomic::{AtomicBool, AtomicU32, AtomicU64}, - Arc, Mutex, RwLock, RwLockReadGuard, RwLockWriteGuard, - }, + sync::{atomic::AtomicU64, Arc, RwLock, RwLockReadGuard, RwLockWriteGuard}, }; -use generational_arena::Index as Inode; #[cfg(feature = "enable-serde")] use serde_derive::{Deserialize, Serialize}; use wasmer_vfs::{Pipe, VirtualFile}; @@ -16,12 +12,11 @@ use wasmer_wasi_types::wasi::{Fd as WasiFd, Fdflags, Filestat, Rights}; use crate::net::socket::InodeSocket; +use super::{InodeGuard, InodeWeakGuard, NotificationInner}; + #[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 +25,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, } @@ -104,19 +99,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 +130,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 3a3fbf5f694..29f44aa6796 100644 --- a/lib/wasi/src/fs/inode_guard.rs +++ b/lib/wasi/src/fs/inode_guard.rs @@ -1,19 +1,14 @@ use std::{ future::Future, io::{IoSlice, SeekFrom}, + mem::replace, ops::{Deref, DerefMut}, pin::Pin, - sync::{ - atomic::{AtomicU64, Ordering}, - Arc, Mutex, RwLock, RwLockReadGuard, RwLockWriteGuard, - }, + sync::{Arc, RwLock}, 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 +17,18 @@ use wasmer_wasi_types::{ wasi::{Errno, Event, EventFdReadwrite, EventUnion, Eventrwflags, Subscription}, }; -use super::Kind; +use super::{notification::NotificationInner, InodeGuard, Kind}; use crate::{ net::socket::{InodeSocketInner, InodeSocketKind}, state::{iterate_poll_events, PollEvent, PollEventSet, WasiState}, syscalls::map_io_err, - WasiInodes, + utils::{OwnedRwLockReadGuard, OwnedRwLockWriteGuard}, }; 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 +46,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 +77,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, .. } => { @@ -124,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, @@ -160,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 => { @@ -196,11 +157,8 @@ impl<'a> Future for InodeValFilePollGuardJoin<'a> { } InodeValFilePollGuardMode::EventNotifications { .. } => false, InodeValFilePollGuardMode::Socket { ref inner } => { - let mut guard = inner.write().unwrap(); - - 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 { @@ -216,11 +174,16 @@ impl<'a> Future for InodeValFilePollGuardJoin<'a> { | Poll::Ready(Err(NetworkError::UnexpectedEof)) => true, _ => false, } + }; + if is_closed { + 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_, @@ -237,192 +200,189 @@ 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)) - } else { - let counter = counter.clone(); - let mut waker = waker.get_mut().unwrap(); - let mut notifications = Pin::new(waker.deref_mut()); - notifications.poll_recv(cx).map(|_| { - let cnt = counter.load(Ordering::Acquire); - Ok(cnt as usize) - }) - } + InodeValFilePollGuardMode::EventNotifications(inner) => { + inner.poll(waker).map(|a| Ok(a)) } 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 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 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)) - } else { - let counter = counter.clone(); - let mut waker = waker.get_mut().unwrap(); - let mut notifications = Pin::new(waker.deref_mut()); - notifications.poll_recv(cx).map(|_| { - let cnt = counter.load(Ordering::Acquire); - Ok(cnt as usize) - }) - } + InodeValFilePollGuardMode::EventNotifications(inner) => { + inner.poll(waker).map(|a| Ok(a)) } 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 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 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 } @@ -430,17 +390,13 @@ impl<'a> Future for InodeValFilePollGuardJoin<'a> { #[derive(Debug)] pub(crate) struct InodeValFileReadGuard { - #[allow(dead_code)] - file: Arc>>, - guard: RwLockReadGuard<'static, Box>, + 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(), } } } @@ -456,7 +412,7 @@ impl InodeValFileReadGuard { fd, peb, subscription, - mode: InodeValFilePollGuardMode::File(self.file), + mode: InodeValFilePollGuardMode::File(self.guard.into_inner()), } } } @@ -470,17 +426,13 @@ impl Deref for InodeValFileReadGuard { #[derive(Debug)] pub struct InodeValFileWriteGuard { - #[allow(dead_code)] - file: Arc>>, - guard: RwLockWriteGuard<'static, Box>, + 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( @@ -506,32 +458,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 { @@ -540,11 +483,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 { @@ -556,8 +496,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 { @@ -566,8 +505,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 { @@ -576,8 +514,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 { @@ -586,8 +523,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 { @@ -596,8 +532,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 { @@ -606,8 +541,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 { @@ -616,8 +550,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 { @@ -626,8 +559,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) @@ -640,8 +572,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) @@ -653,8 +584,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) @@ -663,8 +593,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) @@ -680,8 +609,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) @@ -690,8 +618,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) @@ -700,8 +627,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) @@ -714,8 +640,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) @@ -724,8 +649,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() @@ -741,8 +665,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) @@ -751,3 +674,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 40ae35d88ed..fa734833dc6 100644 --- a/lib/wasi/src/fs/mod.rs +++ b/lib/wasi/src/fs/mod.rs @@ -1,29 +1,24 @@ mod fd; mod inode_guard; +mod notification; use std::{ borrow::{Borrow, Cow}, collections::{HashMap, HashSet}, ops::{Deref, DerefMut}, path::{Path, PathBuf}, - pin::Pin, sync::{ atomic::{AtomicBool, AtomicU32, AtomicU64, Ordering}, - Arc, Mutex, RwLock, RwLockWriteGuard, + Arc, Mutex, RwLock, Weak, }, }; -use crate::{ - state::{Stderr, Stdin, Stdout}, - utils::WasiDummyWaker, -}; -use cooked_waker::IntoWaker; -use generational_arena::{Arena, Index as Inode}; +use crate::state::{Stderr, Stdin, Stdout}; #[cfg(feature = "enable-serde")] use serde_derive::{Deserialize, Serialize}; use tokio::io::AsyncWriteExt; use tracing::{debug, trace}; -use wasmer_vfs::{FileSystem, FsError, OpenOptions, Pipe, VirtualFile}; +use wasmer_vfs::{FileSystem, FsError, OpenOptions, VirtualFile}; use wasmer_wasi_types::{ types::{__WASI_STDERR_FILENO, __WASI_STDIN_FILENO, __WASI_STDOUT_FILENO}, wasi::{ @@ -37,13 +32,9 @@ 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, - 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; @@ -78,97 +69,154 @@ 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 { - // TODO: make private! - pub arena: Arena, - pub orphan_fds: HashMap, + protected: Arc>, } impl WasiInodes { pub fn new() -> Self { Self { - arena: Arena::new(), - orphan_fds: HashMap::new(), + protected: Arc::new(RwLock::new(WasiInodesProtected { + seed: 1, + lookup: Default::default(), + })), } } - /// 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) + /// adds 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; } - } - /// 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) - } else { - self.orphan_fds.get_mut(&inode).ok_or(Errno::Badf) + // 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 + /// TODO: Review why this is dead + #[allow(dead_code)] 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), .. @@ -187,12 +235,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), .. @@ -283,10 +330,10 @@ pub struct WasiFs { pub name_map: HashMap, pub fd_map: Arc>>, pub next_fd: AtomicU32, - inode_counter: AtomicU64, pub current_dir: Mutex, #[cfg_attr(feature = "enable-serde", serde(skip, default))] pub root_fs: WasiFsRoot, + pub root_inode: InodeGuard, pub has_unioned: Arc>>, // TODO: remove @@ -308,70 +355,26 @@ 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 #[allow(clippy::await_holding_lock)] - 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 { - let kind = self.close_fd_ext(inodes, &mut guard, fd); - if let Ok(Some(kind)) = kind { - match kind { - Kind::File { handle, .. } => { - if let Some(handle) = handle { - if let Ok(mut lock) = handle.try_write() { - // TODO: make this function async - // The below code is a dumb hack to trigger close. - // It will only work properly on VirtualFile impls - // that close synchronously without ever returning - // Poll::Pending. - // This is enough to make it work for Pipe. - let waker = WasiDummyWaker.into_waker(); - let mut ctx = std::task::Context::from_waker(&waker); - let _ = Pin::new(&mut **lock).poll_shutdown(&mut ctx); - } - } - } - Kind::Socket { socket } => { - socket.close().ok(); - } - Kind::Pipe { pipe } => { - pipe.close(); - } - Kind::Dir { .. } => {} - Kind::Root { .. } => {} - Kind::Symlink { .. } => {} - Kind::Buffer { .. } => {} - Kind::EventNotifications { .. } => {} - } - } - } + guard.clear(); } /// Will conditionally union the binary file system with this one @@ -398,7 +401,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, @@ -407,7 +410,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(), }; @@ -434,10 +437,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() { @@ -472,7 +475,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(), } @@ -552,10 +555,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() @@ -589,21 +592,32 @@ impl WasiFs { /// Private helper function to init the filesystem, called in `new` and /// `new_with_preopen` - pub(crate) 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); @@ -611,43 +625,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 @@ -660,7 +671,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, @@ -675,7 +686,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) { @@ -684,7 +695,7 @@ impl WasiFs { } let kind = Kind::Dir { - parent: Some(cur_inode), + parent: cur_inode.downgrade(), path: PathBuf::from(""), entries: HashMap::new(), }; @@ -699,13 +710,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"), } @@ -733,7 +744,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, @@ -746,7 +757,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) { @@ -757,7 +768,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); @@ -766,13 +777,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"), } @@ -791,28 +802,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 { @@ -825,7 +835,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 { @@ -844,9 +854,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 { @@ -855,7 +865,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) @@ -875,18 +885,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() @@ -917,12 +927,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); } @@ -937,7 +947,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 { @@ -948,8 +959,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); @@ -963,7 +974,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(); @@ -984,7 +995,7 @@ impl WasiFs { should_insert = true; // load DIR Kind::Dir { - parent: Some(cur_inode), + parent: cur_inode.downgrade(), path: file.clone(), entries: Default::default(), } @@ -1002,7 +1013,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"); }; @@ -1049,14 +1060,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!( @@ -1069,8 +1080,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, @@ -1078,14 +1089,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(), ); } } @@ -1109,7 +1120,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); @@ -1153,7 +1164,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 { @@ -1181,7 +1192,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> { @@ -1205,8 +1215,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("/"), @@ -1236,23 +1246,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), @@ -1270,12 +1278,12 @@ 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 { - let start_inode = if !path.starts_with('/') && self.is_wasix() { + ) -> 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 } else { @@ -1288,11 +1296,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 @@ -1317,21 +1325,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 { @@ -1371,7 +1380,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 { @@ -1386,14 +1395,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) } @@ -1412,19 +1419,15 @@ impl WasiFs { } } - // TODO: remove allow once inodes are refactored (see comments on [`WasiState`]) - #[allow(clippy::await_holding_lock)] - 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 @@ -1434,21 +1437,19 @@ impl WasiFs { if fd.rights.contains(Rights::FD_DATASYNC) { return Err(Errno::Access); } - { - let guard = inodes.arena[fd.inode].read(); - match guard.deref() { - Kind::File { - handle: Some(file), .. - } => { - let mut file = file.write().unwrap(); - file.flush().await.map_err(|_| Errno::Io)? - } - // TODO: verify this behavior - Kind::Dir { .. } => return Err(Errno::Isdir), - Kind::Symlink { .. } => unimplemented!("WasiFs::flush Kind::Symlink"), - Kind::Buffer { .. } => (), - _ => return Err(Errno::Io), + + let guard = fd.inode.read(); + match guard.deref() { + Kind::File { + handle: Some(file), .. + } => { + let mut file = file.write().unwrap(); + file.flush().await.map_err(|_| Errno::Io)? } + // TODO: verify this behavior + Kind::Dir { .. } => return Err(Errno::Isdir), + Kind::Buffer { .. } => (), + _ => return Err(Errno::Io), } } } @@ -1458,23 +1459,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) } @@ -1482,13 +1483,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), @@ -1502,12 +1502,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( @@ -1516,9 +1519,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) } @@ -1529,7 +1532,7 @@ impl WasiFs { rights_inheriting: Rights, flags: Fdflags, open_flags: u16, - inode: Inode, + inode: InodeGuard, idx: WasiFd, ) -> Result<(), Errno> { let is_stdio = matches!( @@ -1539,7 +1542,6 @@ impl WasiFs { self.fd_map.write().unwrap().insert( idx, Fd { - ref_cnt: Arc::new(AtomicU32::new(1)), rights, rights_inheriting, flags, @@ -1554,12 +1556,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, @@ -1580,29 +1580,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) + 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_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), - }) - } - - fn create_stdout(&self, inodes: &mut WasiInodes) { + fn create_stdout(&self, inodes: &WasiInodes) { self.create_std_dev_inner( inodes, Box::new(Stdout::default()), @@ -1612,7 +1595,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()), @@ -1622,7 +1605,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()), @@ -1635,25 +1618,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(), @@ -1663,7 +1645,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, @@ -1676,7 +1657,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) => { @@ -1706,8 +1687,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)? @@ -1740,110 +1720,20 @@ 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(None); - } - 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(); - let kind = match guard.deref_mut() { - Kind::File { - ref mut handle, - path, - fd, - } => { - let item = handle.take(); - Some(Kind::File { - handle: item, - path: path.to_owned(), - fd: *fd, - }) - } - Kind::Socket { ref mut socket, .. } => { - let mut closed_socket = InodeSocket::new(InodeSocketKind::Closed); - std::mem::swap(socket, &mut closed_socket); - Some(Kind::Socket { - socket: closed_socket, - }) + 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::Pipe { ref mut pipe } => { - let mut swap = Pipe::new(); - std::mem::swap(pipe, &mut swap); - Some(Kind::Pipe { pipe: swap }) + Err(err) => { + trace!(%fd, "closing file descriptor failed - {}", err); } - 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? - } - } - None - } - _ => 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 { .. } => None, - Kind::Root { .. } => return Err(Errno::Access), - Kind::Symlink { .. } | Kind::Buffer { .. } => return Err(Errno::Inval), - }; - - Ok(kind) + } + Ok(()) } } 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/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..e64065ae72b 100644 --- a/lib/wasi/src/net/mod.rs +++ b/lib/wasi/src/net/mod.rs @@ -386,6 +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::TooManyOpenFiles => Errno::Mfile, + NetworkError::InsufficientMemory => Errno::Nomem, 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 d7be1aedecf..c4d2ddddc02 100644 --- a/lib/wasi/src/net/socket.rs +++ b/lib/wasi/src/net/socket.rs @@ -65,7 +65,6 @@ pub enum InodeSocketKind { socket: Box, peer: Option, }, - Closed, } pub enum WasiSocketOption { @@ -153,20 +152,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(), + }), + }), } } @@ -183,7 +201,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, @@ -254,7 +272,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, @@ -282,10 +300,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); @@ -327,7 +341,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 { @@ -341,7 +355,6 @@ impl InodeSocket { } } InodeSocketKind::PreSocket { .. } => Poll::Ready(Err(Errno::Notconn)), - InodeSocketKind::Closed => Poll::Ready(Err(Errno::Io)), _ => Poll::Ready(Err(Errno::Notsup)), } } @@ -354,7 +367,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, .. } => { @@ -364,7 +377,6 @@ impl InodeSocket { InodeSocketKind::UdpSocket { .. } => {} InodeSocketKind::Raw(_) => {} InodeSocketKind::PreSocket { .. } => return Err(Errno::Notconn), - InodeSocketKind::Closed => return Err(Errno::Notconn), }; Ok(()) } @@ -378,12 +390,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, .. } => { @@ -393,13 +405,12 @@ impl InodeSocket { InodeSocketKind::UdpSocket { .. } => 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) } } @@ -417,7 +428,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, @@ -452,7 +463,6 @@ impl InodeSocket { target_peer.replace(peer); return Ok(None); } - InodeSocketKind::Closed => return Err(Errno::Io), _ => return Err(Errno::Notsup), } }; @@ -469,19 +479,18 @@ 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::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 { @@ -507,13 +516,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 { @@ -544,13 +552,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, @@ -589,14 +596,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, @@ -631,13 +637,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); @@ -647,14 +652,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()) @@ -662,13 +666,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); @@ -678,14 +681,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()) @@ -693,31 +695,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), } } @@ -727,7 +726,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, @@ -764,13 +763,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, @@ -798,13 +796,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) @@ -813,13 +810,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) @@ -828,79 +824,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), } } @@ -920,14 +909,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::Raw(sock) => { if self.nonblocking { @@ -969,14 +958,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) } } @@ -997,7 +985,7 @@ impl InodeSocket { #[derive(Debug)] struct SocketSender<'a, 'b> { - sock: &'a RwLock, + inner: &'a InodeSocketInner, data: &'b [u8], addr: SocketAddr, nonblocking: bool, @@ -1005,7 +993,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 { @@ -1031,14 +1019,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) } } @@ -1058,7 +1045,7 @@ impl InodeSocket { #[derive(Debug)] struct SocketReceiver<'a, 'b> { - sock: &'a RwLock, + inner: &'a InodeSocketInner, data: &'b mut [MaybeUninit], nonblocking: bool, } @@ -1068,7 +1055,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::Raw(sock) => { if self.nonblocking { @@ -1122,14 +1109,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) } } @@ -1149,7 +1135,7 @@ impl InodeSocket { #[derive(Debug)] struct SocketReceiver<'a, 'b> { - sock: &'a RwLock, + inner: &'a InodeSocketInner, data: &'b mut [MaybeUninit], nonblocking: bool, } @@ -1159,7 +1145,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 { @@ -1185,33 +1171,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 { .. } @@ -1225,7 +1209,7 @@ impl InodeSocket { } } -impl InodeSocketInner { +impl InodeSocketProtected { pub fn poll_read_ready( &mut self, cx: &mut std::task::Context<'_>, @@ -1239,9 +1223,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)) - } } } @@ -1258,9 +1239,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 db3417c066d..b897f617ffb 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 env = config.take().ok_or(VirtualBusError::UnknownError)?; // Set the arguments of the environment by replacing the state - let mut state = env.state.fork(true); + let mut state = env.state.fork(); args.insert(0, what.clone()); state.args = args; env.state = Arc::new(state); diff --git a/lib/wasi/src/os/task/thread.rs b/lib/wasi/src/os/task/thread.rs index d43cd3b771b..6a07b3263b6 100644 --- a/lib/wasi/src/os/task/thread.rs +++ b/lib/wasi/src/os/task/thread.rs @@ -197,8 +197,10 @@ impl WasiThread { 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 && !guard.1.iter().any(|w| w.will_wake(waker)) { - guard.1.push(waker.clone()); + if !has_signals { + if guard.1.iter().any(|w| w.will_wake(waker)) == false { + guard.1.push(waker.clone()); + } } has_signals } diff --git a/lib/wasi/src/runtime/task_manager/mod.rs b/lib/wasi/src/runtime/task_manager/mod.rs index 6bddb8b0e3c..7aa0e5ef439 100644 --- a/lib/wasi/src/runtime/task_manager/mod.rs +++ b/lib/wasi/src/runtime/task_manager/mod.rs @@ -5,17 +5,12 @@ pub mod tokio; use std::{pin::Pin, time::Duration}; use ::tokio::runtime::Handle; - use futures::Future; use wasmer::MemoryType; #[cfg(feature = "sys")] use wasmer_types::MemoryStyle; - -#[cfg(feature = "sys")] -use wasmer::vm::VMMemory; -#[cfg(feature = "js")] -use wasmer::VMMemory; +use wasmer_vm::VMMemory; use crate::os::task::thread::WasiThreadError; diff --git a/lib/wasi/src/state/builder.rs b/lib/wasi/src/state/builder.rs index 766eacc3a20..b81701de6f8 100644 --- a/lib/wasi/src/state/builder.rs +++ b/lib/wasi/src/state/builder.rs @@ -2,9 +2,8 @@ use std::{ collections::HashMap, - ops::{Deref, DerefMut}, path::{Path, PathBuf}, - sync::{Arc, RwLock}, + sync::Arc, }; use rand::Rng; @@ -53,7 +52,7 @@ pub struct WasiEnvBuilder { pub(super) compiled_modules: Option>, #[allow(clippy::type_complexity)] pub(super) setup_fs_fn: - Option Result<(), String> + Send>>, + Option Result<(), String> + Send>>, pub(super) stdout: Option>, pub(super) stderr: Option>, pub(super) stdin: Option>, @@ -121,7 +120,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) @@ -619,43 +618,35 @@ impl WasiEnvBuilder { .unwrap_or_else(|| WasiFsRoot::Sandbox(Arc::new(TmpFileSystem::new()))); // self.preopens are checked in [`PreopenDirBuilder::build`] - let inodes = RwLock::new(crate::state::WasiInodes::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.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.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); let envs = self .envs diff --git a/lib/wasi/src/state/env.rs b/lib/wasi/src/state/env.rs index 7561e1c5918..8428b808659 100644 --- a/lib/wasi/src/state/env.rs +++ b/lib/wasi/src/state/env.rs @@ -1,15 +1,5 @@ -use std::{ - collections::HashMap, - ops::Deref, - path::PathBuf, - sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard}, -}; +use std::{collections::HashMap, ops::Deref, path::PathBuf, sync::Arc}; -use crate::{ - bin_factory::ModuleCache, fs::WasiFsRoot, import_object_for_all_wasi_versions, - runtime::SpawnType, SpawnedMemory, WasiControlPlane, WasiEnvBuilder, WasiFunctionEnv, - WasiRuntimeError, -}; use derivative::Derivative; use rand::Rng; use tracing::{trace, warn}; @@ -21,12 +11,13 @@ use wasmer_vfs::{FsError, VirtualFile}; use wasmer_vnet::DynVirtualNetworking; use wasmer_wasi_types::{ types::Signal, - wasi::{Errno, ExitCode, Fd as WasiFd, Snapshot0Clockid}, + wasi::{Errno, ExitCode, Snapshot0Clockid}, }; use crate::{ - bin_factory::BinFactory, - fs::WasiInodes, + bin_factory::{BinFactory, ModuleCache}, + fs::{WasiFsRoot, WasiInodes}, + import_object_for_all_wasi_versions, os::{ command::builtins::cmd_wasmer::CmdWasmer, task::{ @@ -35,8 +26,10 @@ use crate::{ thread::{WasiThread, WasiThreadHandle, WasiThreadId}, }, }, + runtime::SpawnType, syscalls::platform_clock_time_get, - VirtualTaskManager, WasiError, WasiRuntime, WasiStateCreationError, WasiVFork, + SpawnedMemory, VirtualTaskManager, WasiControlPlane, WasiEnvBuilder, WasiError, + WasiFunctionEnv, WasiRuntime, WasiRuntimeError, WasiStateCreationError, WasiVFork, DEFAULT_STACK_SIZE, }; @@ -242,7 +235,7 @@ impl WasiEnvInit { Self { state: WasiState { secret: rand::thread_rng().gen::<[u8; 32]>(), - inodes: Arc::new(RwLock::new(inodes)), + inodes, fs, threading: Default::default(), futexs: Default::default(), @@ -281,6 +274,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(crate) state: Arc, @@ -322,6 +317,7 @@ impl WasiEnv { pub(crate) fn duplicate(&self) -> Self { Self { process: self.process.clone(), + poll_seed: self.poll_seed, thread: self.thread.clone(), vfork: self.vfork.as_ref().map(|v| v.duplicate()), stack_base: self.stack_base, @@ -344,7 +340,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 = self.bin_factory.clone(); @@ -352,6 +348,7 @@ impl WasiEnv { process, thread, vfork: None, + poll_seed: 0, stack_base: self.stack_base, stack_start: self.stack_start, bin_factory, @@ -389,6 +386,7 @@ impl WasiEnv { process, thread: thread.as_thread(), vfork: None, + poll_seed: 0, stack_base: DEFAULT_STACK_SIZE, stack_start: 0, state: Arc::new(init.state), @@ -726,7 +724,7 @@ impl WasiEnv { /// Expects one of `__WASI_STDIN_FILENO`, `__WASI_STDOUT_FILENO`, `__WASI_STDERR_FILENO`. pub fn std_dev_get( &self, - fd: WasiFd, + fd: crate::syscalls::WasiFd, ) -> Result>, FsError> { self.state.std_dev_get(fd) } @@ -745,21 +743,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) } @@ -903,9 +890,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 6a59a0090b1..fb407ca99af 100644 --- a/lib/wasi/src/state/mod.rs +++ b/lib/wasi/src/state/mod.rs @@ -32,7 +32,6 @@ use std::{ use crate::vbus::VirtualBusInvocation; 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, @@ -158,7 +158,7 @@ pub(crate) struct WasiState { pub secret: [u8; 32], pub fs: WasiFs, - pub inodes: Arc>, + pub inodes: WasiInodes, pub threading: RwLock, pub futexs: Mutex>, pub clock_offset: Mutex>, @@ -275,9 +275,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 223a93a14bf..bc5d7be8f16 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, WasiError, WasiFunctionEnv, WasiInstanceHandles, WasiRuntime, @@ -118,6 +119,7 @@ use crate::{ MAX_SYMLINKS, }, utils::store::InstanceSnapshot, + WasiInodes, }; pub(crate) use crate::{net::net_error_into_wasi_err, utils::WasiParkingLot}; @@ -176,16 +178,8 @@ pub(crate) fn copy_to_slice( .slice(memory, iov_inner.buf_len) .map_err(mem_error_to_wasi)?; - cfg_if::cfg_if! { - if #[cfg(target_family = "wasm")] { - if amt != bytes.read_to_slice(left).map_err(mem_error_to_wasi)? { - return Err(Errno::Fault); - } - } else { - if amt != bytes.read_to_slice(left).map_err(mem_error_to_wasi)? { - return Err(Errno::Fault); - } - } + if amt != bytes.read_to_slice(left).map_err(mem_error_to_wasi)? { + return Err(Errno::Fault); } write_loc = right; @@ -258,12 +252,9 @@ pub(crate) fn read_bytes( #[allow(clippy::await_holding_lock)] 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(ctx, 0); - let (memory, state, inodes) = env.get_memory_and_wasi_state_and_inodes_mut(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) } @@ -370,8 +361,8 @@ where /// thus allowed for asynchronous operations to execute. It has built in functionality /// to (optionally) timeout the IO, force exit the process, callback signals and pump /// synchronous IO engine -pub(crate) fn __asyncify_light( - env: &WasiEnv, +pub(crate) fn __asyncify_light<'a, T, Fut>( + env: &'a WasiEnv, timeout: Option, work: Fut, ) -> Result, WasiError> @@ -466,8 +457,8 @@ impl std::future::Future for InfiniteSleep { /// Performs an immutable operation on the socket while running in an asynchronous runtime /// This has built in signal support -pub(crate) fn __sock_asyncify( - env: &WasiEnv, +pub(crate) fn __sock_asyncify<'a, T, F, Fut>( + env: &'a WasiEnv, sock: WasiFd, rights: Rights, actor: F, @@ -476,19 +467,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() { @@ -525,24 +510,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); @@ -569,17 +548,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(); @@ -592,14 +566,16 @@ where // Start the work using the socket actor(socket, fd_entry) } - _ => Err(Errno::Notsock), + _ => { + return Err(Errno::Notsock); + } } } /// Performs mutable work on a socket under an asynchronous runtime with /// built in signal processing -pub(crate) fn __sock_actor_mut( - ctx: &mut FunctionEnvMut<'_, WasiEnv>, +pub(crate) fn __sock_actor_mut<'a, T, F>( + ctx: &'a mut FunctionEnvMut<'_, WasiEnv>, sock: WasiFd, rights: Rights, actor: F, @@ -611,24 +587,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) @@ -651,10 +621,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", @@ -668,15 +635,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); @@ -690,8 +654,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 } => { @@ -1105,7 +1067,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); } @@ -1129,8 +1091,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 2292fbba3c5..698f6d77604 100644 --- a/lib/wasi/src/syscalls/wasi/fd_datasync.rs +++ b/lib/wasi/src/syscalls/wasi/fd_datasync.rs @@ -19,17 +19,8 @@ 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)), - is_semaphore: flags & EVENT_FD_FLAGS_SEMAPHORE != 0, - wakers: Default::default(), - immediate: Arc::new(AtomicBool::new(false)), - }; + 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_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 8fc7e16b15f..0c947a5c1e8 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,62 @@ 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; + + // 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 { + Poll::Ready(self.inner.try_read().ok_or(Errno::Again)) } else { - continue; + self.inner.read(cx.waker()).map(|a| Ok(a)) } } + } - // If its none blocking then exit - if fd_flags.contains(Fdflags::NONBLOCK) { - return Ok(Errno::Again); - } - - // 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 +391,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 +400,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 163e1c098b6..c91d54c790c 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,8 +60,7 @@ 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, .. } => { @@ -70,7 +69,6 @@ pub fn fd_seek( 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 f7396f260e8..e5ba7cfeac3 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); // TODO: remove allow once inodes are refactored (see comments on [`WasiState`]) #[allow(clippy::await_holding_lock)] @@ -49,11 +47,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,14 +208,7 @@ fn fd_write_internal( } let val = u64::from_ne_bytes(unsafe { std::mem::transmute(val) }); - counter.fetch_add(val, Ordering::AcqRel); - { - let mut guard = wakers.lock().unwrap(); - immediate.store(true, Ordering::Release); - while let Some(wake) = guard.pop_back() { - let _ = wake.send(()); - } - } + inner.write(val); (written, false) } @@ -252,9 +236,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 59f2b340c57..35472aea1fe 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(crate) 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(crate) 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 67aec37713b..863ba4a94ac 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); + } } } } @@ -178,7 +185,7 @@ pub(crate) fn poll_oneoff_internal( } } *fd = Some(file_descriptor); - *peb |= (PollEvent::PollIn as PollEventSet); + *peb = *peb | (PollEvent::PollIn as PollEventSet); file_descriptor } Eventtype::FdWrite => { @@ -196,7 +203,7 @@ pub(crate) fn poll_oneoff_internal( } } *fd = Some(file_descriptor); - *peb |= (PollEvent::PollOut as PollEventSet); + *peb = *peb | (PollEvent::PollOut as PollEventSet); file_descriptor } Eventtype::Clock => { @@ -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,12 @@ 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) - .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::stderr(&state.fs.fd_map) .map(|g| g.into_poll_guard(fd, peb, s)) .map_err(fs_error_into_wasi_err)) } __WASI_STDOUT_FILENO => { - wasi_try_ok_ok!(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 +278,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 +304,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/fd_pipe.rs b/lib/wasi/src/syscalls/wasix/fd_pipe.rs index 4c418165727..308f8d7d452 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 = BidiPipe::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/futex_wake.rs b/lib/wasi/src/syscalls/wasix/futex_wake.rs index aa1e1a09162..7202c8b617e 100644 --- a/lib/wasi/src/syscalls/wasix/futex_wake.rs +++ b/lib/wasi/src/syscalls/wasix/futex_wake.rs @@ -23,9 +23,7 @@ pub fn futex_wake( let woken = { let mut guard = state.futexs.lock().unwrap(); if let Some(futex) = guard.get_mut(&pointer) { - if let Some(w) = futex.wakers.pop() { - w.wake() - } + futex.wakers.pop().map(|w| w.wake()); if futex.wakers.is_empty() { guard.remove(&pointer); } 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 f061c9f8810..5eef36e748d 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 b40f9324d85..23a5e827428 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 b86dafb24c4..c116d0ee376 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,14 @@ 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) { + new_flags.set(Fdflags::NONBLOCK, true); + } 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 cdabb2c25de..8ac20477027 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 27a37918230..7dca883a360 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 63f9cea86f9..cc0133edccc 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 31815b6019c..db866164986 100644 --- a/lib/wasi/src/syscalls/wasix/thread_spawn.rs +++ b/lib/wasi/src/syscalls/wasix/thread_spawn.rs @@ -147,13 +147,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/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() + } +}