From 2d12e72fc1bd08ee5a9f9474f0a4ecdaf116b23e Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Wed, 7 Jun 2023 23:06:16 +1000 Subject: [PATCH 01/21] Fixes for wasmer.sh --- lib/wasi-web/Cargo.lock | 31 +++++---- lib/wasi-web/Cargo.toml | 2 +- lib/wasi-web/src/glue.rs | 3 +- lib/wasi-web/src/runtime.rs | 11 +--- .../runtime/package_loader/builtin_loader.rs | 63 ++++++++++++------- lib/wasi/src/runtime/resolver/utils.rs | 2 +- lib/wasi/src/runtime/resolver/wapm_source.rs | 2 +- 7 files changed, 66 insertions(+), 48 deletions(-) diff --git a/lib/wasi-web/Cargo.lock b/lib/wasi-web/Cargo.lock index 8e7d9cc088f..0b8cdae2c55 100644 --- a/lib/wasi-web/Cargo.lock +++ b/lib/wasi-web/Cargo.lock @@ -1317,6 +1317,12 @@ dependencies = [ "bytecheck", ] +[[package]] +name = "replace_with" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3a8614ee435691de62bcffcf4a66d91b3594bf1428a5722e79103249a095690" + [[package]] name = "rkyv" version = "0.7.41" @@ -2044,7 +2050,7 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "virtual-fs" -version = "0.3.0" +version = "0.4.0" dependencies = [ "anyhow", "async-trait", @@ -2054,6 +2060,7 @@ dependencies = [ "indexmap", "lazy_static", "pin-project-lite", + "replace_with", "slab", "thiserror", "tokio", @@ -2063,7 +2070,7 @@ dependencies = [ [[package]] name = "virtual-net" -version = "0.2.0" +version = "0.3.0" dependencies = [ "async-trait", "bytes", @@ -2137,7 +2144,7 @@ dependencies = [ [[package]] name = "wai-bindgen-wasmer" -version = "0.5.0" +version = "0.6.0" dependencies = [ "anyhow", "bitflags", @@ -2321,7 +2328,7 @@ dependencies = [ [[package]] name = "wasmer" -version = "4.0.0-alpha.1" +version = "4.0.0-beta.1" dependencies = [ "bytes", "cfg-if", @@ -2348,7 +2355,7 @@ dependencies = [ [[package]] name = "wasmer-compiler" -version = "4.0.0-alpha.1" +version = "4.0.0-beta.1" dependencies = [ "backtrace", "cfg-if", @@ -2368,7 +2375,7 @@ dependencies = [ [[package]] name = "wasmer-derive" -version = "4.0.0-alpha.1" +version = "4.0.0-beta.1" dependencies = [ "proc-macro-error", "proc-macro2", @@ -2378,7 +2385,7 @@ dependencies = [ [[package]] name = "wasmer-types" -version = "4.0.0-alpha.1" +version = "4.0.0-beta.1" dependencies = [ "bytecheck", "enum-iterator", @@ -2393,7 +2400,7 @@ dependencies = [ [[package]] name = "wasmer-vm" -version = "4.0.0-alpha.1" +version = "4.0.0-beta.1" dependencies = [ "backtrace", "cc", @@ -2418,7 +2425,7 @@ dependencies = [ [[package]] name = "wasmer-wasix" -version = "0.5.0" +version = "0.6.0" dependencies = [ "anyhow", "async-trait", @@ -2470,7 +2477,7 @@ dependencies = [ [[package]] name = "wasmer-wasix-types" -version = "0.5.0" +version = "0.6.0" dependencies = [ "anyhow", "bitflags", @@ -2578,9 +2585,9 @@ dependencies = [ [[package]] name = "webc" -version = "5.0.0" +version = "5.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06bee486f9207604f99bfa3c95afcd03272d95db5872c6c1b11470be4390d514" +checksum = "42af14e63ed784e4f813bd5fb35bc84016fa8b245879809547247da6051107f0" dependencies = [ "anyhow", "base64 0.21.0", diff --git a/lib/wasi-web/Cargo.toml b/lib/wasi-web/Cargo.toml index 15d8edf9d95..7fcdb2f3279 100644 --- a/lib/wasi-web/Cargo.toml +++ b/lib/wasi-web/Cargo.toml @@ -17,7 +17,7 @@ rust-version = "1.67" [dependencies] wasmer = { path = "../api", default_features = false, features = [ "js-default" ] } -wasmer-wasix = { path = "../wasi", version = "0.5.0", default-features = false, features = [ "js-default" ] } +wasmer-wasix = { path = "../wasi", default-features = false, features = [ "js-default" ] } #wasm-bindgen = { version = "0.2", features = [ "nightly", "serde-serialize" ] } wasm-bindgen = { version = "0.2", features = [ "serde-serialize" ] } wasm-bindgen-futures = "0.4" diff --git a/lib/wasi-web/src/glue.rs b/lib/wasi-web/src/glue.rs index e41026cdede..552d4583b3b 100644 --- a/lib/wasi-web/src/glue.rs +++ b/lib/wasi-web/src/glue.rs @@ -39,7 +39,8 @@ pub fn main() { pub const DEFAULT_BOOT_WEBC: &str = "sharrattj/bash"; //pub const DEFAULT_BOOT_WEBC: &str = "sharrattj/dash"; -pub const DEFAULT_BOOT_USES: [&str; 2] = ["sharrattj/coreutils", "sharrattj/catsay"]; +//pub const DEFAULT_BOOT_USES: [&str; 2] = ["sharrattj/coreutils", "sharrattj/catsay"]; +pub const DEFAULT_BOOT_USES: [&str; 1] = ["sharrattj/coreutils"]; #[wasm_bindgen] pub fn start() -> Result<(), JsValue> { diff --git a/lib/wasi-web/src/runtime.rs b/lib/wasi-web/src/runtime.rs index 8fc8d3ea5a3..96226791305 100644 --- a/lib/wasi-web/src/runtime.rs +++ b/lib/wasi-web/src/runtime.rs @@ -21,7 +21,7 @@ use wasmer_wasix::{ http::{DynHttpClient, HttpRequest, HttpResponse}, os::{TtyBridge, TtyOptions}, runtime::{ - module_cache::{FileSystemCache, ModuleCache, ThreadLocalCache}, + module_cache::{ModuleCache, SharedCache, ThreadLocalCache}, package_loader::{BuiltinPackageLoader, PackageLoader}, resolver::{Source, WapmSource}, task_manager::TaskWasm, @@ -88,13 +88,8 @@ impl WebRuntime { // even if the filesystem cache fails (i.e. because we're compiled to // wasm32-unknown-unknown and running in a browser), the in-memory layer // should still work. - let package_loader = BuiltinPackageLoader::new_with_client( - BuiltinPackageLoader::default_cache_dir(&wasmer_dir), - http_client.clone(), - ); - let module_cache = ThreadLocalCache::default().with_fallback(FileSystemCache::new( - FileSystemCache::default_cache_dir(&wasmer_dir), - )); + let package_loader = BuiltinPackageLoader::new_only_client(http_client.clone()); + let module_cache = ThreadLocalCache::default().with_fallback(SharedCache::default()); WebRuntime { pool, tasks: runtime, diff --git a/lib/wasi/src/runtime/package_loader/builtin_loader.rs b/lib/wasi/src/runtime/package_loader/builtin_loader.rs index ea1f62da2c5..65611ccc458 100644 --- a/lib/wasi/src/runtime/package_loader/builtin_loader.rs +++ b/lib/wasi/src/runtime/package_loader/builtin_loader.rs @@ -30,7 +30,7 @@ use crate::{ pub struct BuiltinPackageLoader { client: Arc, in_memory: InMemoryCache, - fs: FileSystemCache, + cache: Option, } impl BuiltinPackageLoader { @@ -44,9 +44,17 @@ impl BuiltinPackageLoader { client: Arc, ) -> Self { BuiltinPackageLoader { - fs: FileSystemCache { + cache: Some(FileSystemCache { cache_dir: cache_dir.into(), - }, + }), + in_memory: InMemoryCache::default(), + client, + } + } + + pub fn new_only_client(client: Arc) -> Self { + BuiltinPackageLoader { + cache: None, in_memory: InMemoryCache::default(), client, } @@ -77,11 +85,13 @@ impl BuiltinPackageLoader { return Ok(Some(cached)); } - if let Some(cached) = self.fs.lookup(hash).await? { - // Note: We want to propagate it to the in-memory cache, too - tracing::debug!("Copying from the filesystem cache to the in-memory cache",); - self.in_memory.save(&cached, *hash); - return Ok(Some(cached)); + if let Some(cache) = self.cache.as_ref() { + if let Some(cached) = cache.lookup(hash).await? { + // Note: We want to propagate it to the in-memory cache, too + tracing::debug!("Copying from the filesystem cache to the in-memory cache",); + self.in_memory.save(&cached, *hash); + return Ok(Some(cached)); + } } Ok(None) @@ -135,22 +145,27 @@ impl BuiltinPackageLoader { dist: &DistributionInfo, ) -> Result { // First, save it to disk - self.fs.save(webc, dist).await?; - - // Now try to load it again. The resulting container should use - // a memory-mapped file rather than an in-memory buffer. - match self.fs.lookup(&dist.webc_sha256).await? { - Some(container) => { - // we also want to make sure it's in the in-memory cache - self.in_memory.save(&container, dist.webc_sha256); - - Ok(container) + if let Some(cache) = self.cache.as_ref() { + cache.save(webc, dist).await?; + + // Now try to load it again. The resulting container should use + // a memory-mapped file rather than an in-memory buffer. + match cache.lookup(&dist.webc_sha256).await? { + Some(container) => Ok(container), + None => { + // Something really weird has occurred and we can't see the + // saved file. Just error out and let the fallback code do its + // thing. + Err(Error::msg("Unable to load the downloaded memory from disk")) + } } - None => { - // Something really weird has occurred and we can't see the - // saved file. Just error out and let the fallback code do its - // thing. - Err(Error::msg("Unable to load the downloaded memory from disk")) + } else { + match Container::from_bytes(webc.to_vec()) { + Ok(container) => { + self.in_memory.save(&container, dist.webc_sha256); + Ok(container) + } + Err(e) => Err(Error::new(e).context("Unable to load container")), } } } @@ -393,7 +408,7 @@ mod tests { let manifest = container.manifest(); assert_eq!(manifest.entrypoint.as_deref(), Some("python")); // it should have been automatically saved to disk - let path = loader.fs.path(&summary.dist.webc_sha256); + let path = loader.cache.path(&summary.dist.webc_sha256); assert!(path.exists()); assert_eq!(std::fs::read(&path).unwrap(), PYTHON); // and cached in memory for next time diff --git a/lib/wasi/src/runtime/resolver/utils.rs b/lib/wasi/src/runtime/resolver/utils.rs index 03c018cc5f4..578884f7440 100644 --- a/lib/wasi/src/runtime/resolver/utils.rs +++ b/lib/wasi/src/runtime/resolver/utils.rs @@ -1,6 +1,6 @@ use std::path::{Path, PathBuf}; -use anyhow::{Context, Error}; +use anyhow::Error; use http::{HeaderMap, StatusCode}; use url::Url; diff --git a/lib/wasi/src/runtime/resolver/wapm_source.rs b/lib/wasi/src/runtime/resolver/wapm_source.rs index 71029ff5d45..9e98dcc5cd4 100644 --- a/lib/wasi/src/runtime/resolver/wapm_source.rs +++ b/lib/wasi/src/runtime/resolver/wapm_source.rs @@ -23,7 +23,7 @@ pub struct WapmSource { impl WapmSource { pub const WAPM_DEV_ENDPOINT: &str = "https://registry.wapm.dev/graphql"; - pub const WAPM_PROD_ENDPOINT: &str = "https://registry.wapm.io/graphql"; + pub const WAPM_PROD_ENDPOINT: &str = "https://registry.wasmer.io/graphql"; pub fn new(registry_endpoint: Url, client: Arc) -> Self { WapmSource { From 90cc4fced429b15f6390cb9565b85ed32fc232d2 Mon Sep 17 00:00:00 2001 From: Michael-F-Bryan Date: Wed, 7 Jun 2023 22:41:14 +0800 Subject: [PATCH 02/21] Don't ignore failing wasmer-web builds --- .github/workflows/web.yaml | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/.github/workflows/web.yaml b/.github/workflows/web.yaml index 07fcaeab10d..a12b0802cbe 100644 --- a/.github/workflows/web.yaml +++ b/.github/workflows/web.yaml @@ -15,19 +15,15 @@ on: jobs: web: name: Build and Test (wasmer-web) - strategy: - matrix: - os: [ubuntu-latest] - rust: [nightly] - runs-on: ${{ matrix.os }} + runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v2 - name: Install Rust - uses: actions/checkout@v3 + uses: dtolnay/rust-toolchain@stable with: - toolchain: ${{ matrix.rust }} + toolchain: nightly - name: Show Rust version run: | @@ -40,7 +36,6 @@ jobs: cargo fmt --check - name: Check - continue-on-error: true env: CARGO_NET_GIT_FETCH_WITH_CLI: "true" run: | From f62df4ed89b4b09b2d490cc9a15e88898a25b7cc Mon Sep 17 00:00:00 2001 From: Michael-F-Bryan Date: Wed, 7 Jun 2023 22:43:34 +0800 Subject: [PATCH 03/21] Removing some dead code --- lib/wasi-web/src/glue.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/wasi-web/src/glue.rs b/lib/wasi-web/src/glue.rs index 552d4583b3b..4e413a9690a 100644 --- a/lib/wasi-web/src/glue.rs +++ b/lib/wasi-web/src/glue.rs @@ -38,8 +38,6 @@ pub fn main() { } pub const DEFAULT_BOOT_WEBC: &str = "sharrattj/bash"; -//pub const DEFAULT_BOOT_WEBC: &str = "sharrattj/dash"; -//pub const DEFAULT_BOOT_USES: [&str; 2] = ["sharrattj/coreutils", "sharrattj/catsay"]; pub const DEFAULT_BOOT_USES: [&str; 1] = ["sharrattj/coreutils"]; #[wasm_bindgen] From 9492a18bd255442959877346327fc28d3d413a3f Mon Sep 17 00:00:00 2001 From: Michael-F-Bryan Date: Wed, 7 Jun 2023 22:45:33 +0800 Subject: [PATCH 04/21] We shouldn't have removed the Context import --- lib/wasix/src/runtime/resolver/utils.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/wasix/src/runtime/resolver/utils.rs b/lib/wasix/src/runtime/resolver/utils.rs index 578884f7440..03c018cc5f4 100644 --- a/lib/wasix/src/runtime/resolver/utils.rs +++ b/lib/wasix/src/runtime/resolver/utils.rs @@ -1,6 +1,6 @@ use std::path::{Path, PathBuf}; -use anyhow::Error; +use anyhow::{Context, Error}; use http::{HeaderMap, StatusCode}; use url::Url; From a03670f68e02d677018aef0816dc5c453c13e930 Mon Sep 17 00:00:00 2001 From: Michael-F-Bryan Date: Thu, 8 Jun 2023 14:28:05 +0800 Subject: [PATCH 05/21] Fixed a test that wasn't updated --- lib/wasix/src/runtime/package_loader/builtin_loader.rs | 6 +++++- lib/wasix/src/runtime/resolver/utils.rs | 4 +++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/wasix/src/runtime/package_loader/builtin_loader.rs b/lib/wasix/src/runtime/package_loader/builtin_loader.rs index dc6edb9f2ea..7f707e61c2e 100644 --- a/lib/wasix/src/runtime/package_loader/builtin_loader.rs +++ b/lib/wasix/src/runtime/package_loader/builtin_loader.rs @@ -408,7 +408,11 @@ mod tests { let manifest = container.manifest(); assert_eq!(manifest.entrypoint.as_deref(), Some("python")); // it should have been automatically saved to disk - let path = loader.cache.path(&summary.dist.webc_sha256); + let path = loader + .cache + .as_ref() + .unwrap() + .path(&summary.dist.webc_sha256); assert!(path.exists()); assert_eq!(std::fs::read(&path).unwrap(), PYTHON); // and cached in memory for next time diff --git a/lib/wasix/src/runtime/resolver/utils.rs b/lib/wasix/src/runtime/resolver/utils.rs index 03c018cc5f4..34cc5b5d72b 100644 --- a/lib/wasix/src/runtime/resolver/utils.rs +++ b/lib/wasix/src/runtime/resolver/utils.rs @@ -1,6 +1,6 @@ use std::path::{Path, PathBuf}; -use anyhow::{Context, Error}; +use anyhow::Error; use http::{HeaderMap, StatusCode}; use url::Url; @@ -62,6 +62,8 @@ pub(crate) fn file_path_from_url(url: &Url) -> Result { // Note: The Url::to_file_path() method is platform-specific cfg_if::cfg_if! { if #[cfg(any(unix, windows, target_os = "redox", target_os = "wasi"))] { + use anyhow::Context; + if let Ok(path) = url.to_file_path() { return Ok(path); } From 2db23a4f7b224c4ccd1ea158f6d0a730009e717d Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Fri, 9 Jun 2023 10:05:42 +1000 Subject: [PATCH 06/21] Fix so that conditional_union works again --- lib/wasix/src/bin_factory/exec.rs | 9 ++++++++- lib/wasix/src/fs/mod.rs | 24 ++++++++++++++---------- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/lib/wasix/src/bin_factory/exec.rs b/lib/wasix/src/bin_factory/exec.rs index 894d255a372..1fb22d357ab 100644 --- a/lib/wasix/src/bin_factory/exec.rs +++ b/lib/wasix/src/bin_factory/exec.rs @@ -63,7 +63,14 @@ pub async fn spawn_exec( }; // If the file system has not already been union'ed then do so - env.state.fs.conditional_union(&binary); + env.state + .fs + .conditional_union(&binary) + .await + .map_err(|err| { + tracing::error!("failed to union file system - {}", err); + SpawnError::InternalError + })?; tracing::debug!("{:?}", env.state.fs); // Now run the module diff --git a/lib/wasix/src/fs/mod.rs b/lib/wasix/src/fs/mod.rs index a581fcf9a37..6d5a6e39e3b 100644 --- a/lib/wasix/src/fs/mod.rs +++ b/lib/wasix/src/fs/mod.rs @@ -489,21 +489,25 @@ impl WasiFs { /// Will conditionally union the binary file system with this one /// if it has not already been unioned - pub fn conditional_union(&self, binary: &BinaryPackage) -> bool { - let sandbox_fs = match &self.root_fs { - WasiFsRoot::Sandbox(fs) => fs, - WasiFsRoot::Backing(_) => { - tracing::error!("can not perform a union on a backing file system"); - return false; - } - }; + pub async fn conditional_union( + &self, + binary: &BinaryPackage, + ) -> Result<(), virtual_fs::FsError> { let package_name = binary.package_name.to_string(); let mut guard = self.has_unioned.lock().unwrap(); if !guard.contains(&package_name) { guard.insert(package_name); - sandbox_fs.union(&binary.webc_fs); + + match self.root_fs { + WasiFsRoot::Sandbox(ref sandbox_fs) => { + sandbox_fs.union(&binary.webc_fs); + } + WasiFsRoot::Backing(ref fs) => { + merge_filesystems(&binary.webc_fs, fs.deref()).await?; + } + } } - true + Ok(()) } /// Created for the builder API. like `new` but with more information From bba569aa1e7705b6110a56fb08fb068b0170a776 Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Fri, 9 Jun 2023 10:19:03 +1000 Subject: [PATCH 07/21] Disabled asynchronous threading for now --- lib/wasi-web/src/glue.rs | 2 +- lib/wasix/src/lib.rs | 4 ---- lib/wasix/src/state/builder.rs | 1 + lib/wasix/src/state/env.rs | 10 ++++++++++ 4 files changed, 12 insertions(+), 5 deletions(-) diff --git a/lib/wasi-web/src/glue.rs b/lib/wasi-web/src/glue.rs index 4e413a9690a..215619c47fe 100644 --- a/lib/wasi-web/src/glue.rs +++ b/lib/wasi-web/src/glue.rs @@ -188,7 +188,7 @@ pub fn start() -> Result<(), JsValue> { let mut capabilities = Capabilities::default(); capabilities.threading.max_threads = Some(50); - capabilities.threading.enable_asynchronous_threading = true; + capabilities.threading.enable_asynchronous_threading = false; console = console.with_capabilities(capabilities); let (tx, mut rx) = mpsc::unbounded_channel(); diff --git a/lib/wasix/src/lib.rs b/lib/wasix/src/lib.rs index 9185d7ef873..8a355013b1a 100644 --- a/lib/wasix/src/lib.rs +++ b/lib/wasix/src/lib.rs @@ -674,10 +674,6 @@ fn import_object_for_all_wasi_versions( "wasix_64v1" => exports_wasix_64v1, }; - for import in module.imports() { - tracing::trace!("import {}.{}", import.module(), import.name()); - } - // TODO: clean this up! cfg_if::cfg_if! { if #[cfg(feature = "sys")] { diff --git a/lib/wasix/src/state/builder.rs b/lib/wasix/src/state/builder.rs index dab216904ac..cf7937b7237 100644 --- a/lib/wasix/src/state/builder.rs +++ b/lib/wasix/src/state/builder.rs @@ -743,6 +743,7 @@ impl WasiEnvBuilder { thread: None, call_initialize: true, can_deep_sleep: false, + extra_tracing: true, }; Ok(init) diff --git a/lib/wasix/src/state/env.rs b/lib/wasix/src/state/env.rs index 44b17b6c103..b9b2c8cc14c 100644 --- a/lib/wasix/src/state/env.rs +++ b/lib/wasix/src/state/env.rs @@ -226,6 +226,9 @@ pub struct WasiEnvInit { /// Indicates if the calling environment is capable of deep sleeping pub can_deep_sleep: bool, + + /// Indicates if extra tracing should be output + pub extra_tracing: bool, } impl WasiEnvInit { @@ -261,6 +264,7 @@ impl WasiEnvInit { thread: None, call_initialize: self.call_initialize, can_deep_sleep: self.can_deep_sleep, + extra_tracing: false, } } } @@ -444,6 +448,12 @@ impl WasiEnv { let call_initialize = init.call_initialize; let spawn_type = init.memory_ty.take(); + if init.extra_tracing { + for import in module.imports() { + tracing::trace!("import {}.{}", import.module(), import.name()); + } + } + let env = Self::from_init(init)?; let pid = env.process.pid(); From a9e956472a1b4c1ed681f7f60d8cb19589029733 Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Fri, 9 Jun 2023 11:00:36 +1000 Subject: [PATCH 08/21] Switched from bash to dash and enabled async threading again --- lib/wasi-web/src/glue.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/wasi-web/src/glue.rs b/lib/wasi-web/src/glue.rs index 215619c47fe..35ce7d23add 100644 --- a/lib/wasi-web/src/glue.rs +++ b/lib/wasi-web/src/glue.rs @@ -37,7 +37,7 @@ pub fn main() { set_panic_hook(); } -pub const DEFAULT_BOOT_WEBC: &str = "sharrattj/bash"; +pub const DEFAULT_BOOT_WEBC: &str = "sharrattj/dash"; pub const DEFAULT_BOOT_USES: [&str; 1] = ["sharrattj/coreutils"]; #[wasm_bindgen] @@ -188,7 +188,7 @@ pub fn start() -> Result<(), JsValue> { let mut capabilities = Capabilities::default(); capabilities.threading.max_threads = Some(50); - capabilities.threading.enable_asynchronous_threading = false; + capabilities.threading.enable_asynchronous_threading = true; console = console.with_capabilities(capabilities); let (tx, mut rx) = mpsc::unbounded_channel(); From b6c2bef03427a9610052c915e8a2f6d6f0651361 Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Mon, 12 Jun 2023 08:55:26 +1000 Subject: [PATCH 09/21] Fixed a runtime in a runtime bug --- lib/wasix/src/bin_factory/exec.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/wasix/src/bin_factory/exec.rs b/lib/wasix/src/bin_factory/exec.rs index 1fb22d357ab..df4c8524622 100644 --- a/lib/wasix/src/bin_factory/exec.rs +++ b/lib/wasix/src/bin_factory/exec.rs @@ -41,7 +41,7 @@ pub async fn spawn_exec( SpawnError::CompileError }); if module.is_err() { - env.blocking_cleanup(Some(Errno::Noexec.into())); + env.cleanup(Some(Errno::Noexec.into())).await; } let module = module?; @@ -57,7 +57,7 @@ pub async fn spawn_exec( } (None, None) => { error!("package has no entry [{}]", name,); - env.blocking_cleanup(Some(Errno::Noexec.into())); + env.cleanup(Some(Errno::Noexec.into())).await; return Err(SpawnError::CompileError); } }; From cc56b9ef9d5206985fb6b7506cfdf9921c68d05f Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Fri, 16 Jun 2023 21:55:11 +1000 Subject: [PATCH 10/21] Multiple fixes for wasmer.sh --- lib/wasi-web/Cargo.lock | 20 ++++---- lib/wasi-web/Cargo.toml | 4 +- lib/wasi-web/src/pool.rs | 29 +++++++++-- lib/wasi-web/src/runtime.rs | 2 +- lib/wasix/src/fs/mod.rs | 65 +++++++++++++++--------- lib/wasix/src/state/env.rs | 50 +++++++++++------- lib/wasix/src/utils/owned_mutex_guard.rs | 4 ++ 7 files changed, 116 insertions(+), 58 deletions(-) diff --git a/lib/wasi-web/Cargo.lock b/lib/wasi-web/Cargo.lock index 2632a2284f7..842cb80a0ce 100644 --- a/lib/wasi-web/Cargo.lock +++ b/lib/wasi-web/Cargo.lock @@ -2085,7 +2085,7 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "virtual-fs" -version = "0.5.0" +version = "0.6.0" dependencies = [ "anyhow", "async-trait", @@ -2180,7 +2180,7 @@ dependencies = [ [[package]] name = "wai-bindgen-wasmer" -version = "0.7.0" +version = "0.8.0" dependencies = [ "anyhow", "bitflags", @@ -2364,7 +2364,7 @@ dependencies = [ [[package]] name = "wasmer" -version = "4.0.0-beta.2" +version = "4.0.0-beta.3" dependencies = [ "bytes", "cfg-if", @@ -2391,7 +2391,7 @@ dependencies = [ [[package]] name = "wasmer-compiler" -version = "4.0.0-beta.2" +version = "4.0.0-beta.3" dependencies = [ "backtrace", "cfg-if", @@ -2411,7 +2411,7 @@ dependencies = [ [[package]] name = "wasmer-derive" -version = "4.0.0-beta.2" +version = "4.0.0-beta.3" dependencies = [ "proc-macro-error", "proc-macro2", @@ -2421,7 +2421,7 @@ dependencies = [ [[package]] name = "wasmer-types" -version = "4.0.0-beta.2" +version = "4.0.0-beta.3" dependencies = [ "bytecheck", "enum-iterator", @@ -2436,7 +2436,7 @@ dependencies = [ [[package]] name = "wasmer-vm" -version = "4.0.0-beta.2" +version = "4.0.0-beta.3" dependencies = [ "backtrace", "cc", @@ -2461,7 +2461,7 @@ dependencies = [ [[package]] name = "wasmer-wasix" -version = "0.7.0" +version = "0.8.0" dependencies = [ "anyhow", "async-trait", @@ -2515,7 +2515,7 @@ dependencies = [ [[package]] name = "wasmer-wasix-types" -version = "0.7.0" +version = "0.8.0" dependencies = [ "anyhow", "bitflags", @@ -2536,7 +2536,7 @@ dependencies = [ [[package]] name = "wasmer-web" -version = "0.4.0" +version = "0.5.0" dependencies = [ "anyhow", "async-trait", diff --git a/lib/wasi-web/Cargo.toml b/lib/wasi-web/Cargo.toml index deb7d9beb18..10267616df5 100644 --- a/lib/wasi-web/Cargo.toml +++ b/lib/wasi-web/Cargo.toml @@ -23,8 +23,8 @@ wasm-bindgen = { version = "0.2", features = [ "serde-serialize" ] } wasm-bindgen-futures = "0.4" console_error_panic_hook = "^0.1" js-sys = "0.3" -#tracing = { version = "^0.1", features = [ "log", "release_max_level_info" ] } -tracing = { version = "^0.1", features = [ "log" ] } +tracing = { version = "^0.1", features = [ "log", "release_max_level_info" ] } +#tracing = { version = "^0.1", features = [ "log" ] } tracing-futures = { version = "^0.2" } tracing-subscriber = { version = "^0.2" } tracing-wasm = { version = "^0.2" } diff --git a/lib/wasi-web/src/pool.rs b/lib/wasi-web/src/pool.rs index bf8c9c20a57..18417b8d1a8 100644 --- a/lib/wasi-web/src/pool.rs +++ b/lib/wasi-web/src/pool.rs @@ -80,6 +80,7 @@ struct WasmRunCommand { trigger: Option, update_layout: bool, result: Option>, + pool: WebThreadPool, } trait AssertSendSync: Send + Sync {} @@ -389,6 +390,7 @@ impl WebThreadPool { snapshot, update_layout, result: None, + pool: self.clone(), }); let task = Box::into_raw(task); @@ -705,9 +707,17 @@ pub fn wasm_entry_point( ) { // Grab the run wrapper that passes us the rust variables (and extract the callback) let task = task_ptr as *mut WasmRunCommand; - let task = unsafe { Box::from_raw(task) }; + let mut task = unsafe { Box::from_raw(task) }; let run = (*task).run; + // If there is a trigger then run it + let trigger = task.trigger.take(); + if let Some(trigger) = trigger { + let trigger_run = trigger.run; + let tasks = task.env.tasks(); + task.result = Some(tasks.block_on(trigger_run())); + } + // Invoke the callback which will run the web assembly module if let Some((ctx, store)) = _build_ctx_and_store( wasm_module, @@ -738,6 +748,18 @@ pub fn schedule_wasm_task( // We will pass it on now let trigger = task.trigger.take(); + let trigger_rx = if let Some(trigger) = trigger { + let (tx, rx) = tokio::sync::mpsc::unbounded_channel(); + task.pool.spawn_shared(Box::new(|| { + Box::pin(async move { + let run = trigger.run; + tx.send(run().await).ok(); + }) + })); + Some(rx) + } else { + None + }; // We will now spawn the process in its own thread let mut opts = WorkerOptions::new(); @@ -745,9 +767,8 @@ pub fn schedule_wasm_task( opts.name(&*format!("Wasm-Thread")); wasm_bindgen_futures::spawn_local(async move { - if let Some(trigger) = trigger { - let run = trigger.run; - task.result = Some(run().await); + if let Some(mut trigger_rx) = trigger_rx { + task.result = trigger_rx.recv().await; } let task = Box::into_raw(task); diff --git a/lib/wasi-web/src/runtime.rs b/lib/wasi-web/src/runtime.rs index a8f9f0e2044..864e2c47443 100644 --- a/lib/wasi-web/src/runtime.rs +++ b/lib/wasi-web/src/runtime.rs @@ -89,7 +89,7 @@ impl WebRuntime { // wasm32-unknown-unknown and running in a browser), the in-memory layer // should still work. let package_loader = BuiltinPackageLoader::new_only_client(http_client.clone()); - let module_cache = ThreadLocalCache::default().with_fallback(SharedCache::default()); + let module_cache = ThreadLocalCache::default(); WebRuntime { pool, tasks: runtime, diff --git a/lib/wasix/src/fs/mod.rs b/lib/wasix/src/fs/mod.rs index deec998e59e..2f14db8c410 100644 --- a/lib/wasix/src/fs/mod.rs +++ b/lib/wasix/src/fs/mod.rs @@ -7,14 +7,16 @@ use std::{ collections::{HashMap, HashSet, VecDeque}, ops::{Deref, DerefMut}, path::{Component, Path, PathBuf}, + pin::Pin, sync::{ atomic::{AtomicBool, AtomicU32, AtomicU64, Ordering}, Arc, Mutex, RwLock, Weak, }, + task::{Context, Poll}, }; use crate::state::{Stderr, Stdin, Stdout}; -use futures::{future::BoxFuture, TryStreamExt}; +use futures::{future::BoxFuture, Future, TryStreamExt}; #[cfg(feature = "enable-serde")] use serde_derive::{Deserialize, Serialize}; use tokio::io::AsyncWriteExt; @@ -1526,35 +1528,52 @@ impl WasiFs { pub async fn flush(&self, fd: WasiFd) -> Result<(), Errno> { match fd { __WASI_STDIN_FILENO => (), - __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 => WasiInodes::stderr_mut(&self.fd_map) - .map_err(fs_error_into_wasi_err)? - .flush() - .await - .map_err(map_io_err)?, + __WASI_STDOUT_FILENO => { + let mut file = + WasiInodes::stdout_mut(&self.fd_map).map_err(fs_error_into_wasi_err)?; + file.flush().await.map_err(map_io_err)? + } + __WASI_STDERR_FILENO => { + let mut file = + WasiInodes::stderr_mut(&self.fd_map).map_err(fs_error_into_wasi_err)?; + file.flush().await.map_err(map_io_err)? + } _ => { let fd = self.get_fd(fd)?; if fd.rights.contains(Rights::FD_DATASYNC) { return Err(Errno::Access); } - 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)? + let work = { + let guard = fd.inode.read(); + match guard.deref() { + Kind::File { + handle: Some(file), .. + } => { + struct FlushPoller { + file: Arc>>, + } + impl Future for FlushPoller { + type Output = Result<(), Errno>; + fn poll( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll { + let mut file = self.file.write().unwrap(); + Pin::new(file.as_mut()) + .poll_flush(cx) + .map_err(|_| Errno::Io) + } + } + FlushPoller { file: file.clone() } + } + // TODO: verify this behavior + Kind::Dir { .. } => return Err(Errno::Isdir), + Kind::Buffer { .. } => return Ok(()), + _ => return Err(Errno::Io), } - // TODO: verify this behavior - Kind::Dir { .. } => return Err(Errno::Isdir), - Kind::Buffer { .. } => (), - _ => return Err(Errno::Io), - } + }; + work.await? } } Ok(()) diff --git a/lib/wasix/src/state/env.rs b/lib/wasix/src/state/env.rs index b9b2c8cc14c..516461d71ba 100644 --- a/lib/wasix/src/state/env.rs +++ b/lib/wasix/src/state/env.rs @@ -7,6 +7,7 @@ use std::{ }; use derivative::Derivative; +use futures::future::BoxFuture; use rand::Rng; use tracing::{trace, warn}; use virtual_fs::{AsyncWriteExt, FileSystem, FsError, VirtualFile}; @@ -31,7 +32,7 @@ use crate::{ thread::{WasiMemoryLayout, WasiThread, WasiThreadHandle, WasiThreadId}, }, runtime::{resolver::PackageSpecifier, SpawnMemoryType}, - syscalls::{__asyncify_light, platform_clock_time_get}, + syscalls::platform_clock_time_get, Runtime, VirtualTaskManager, WasiControlPlane, WasiEnvBuilder, WasiError, WasiFunctionEnv, WasiRuntimeError, WasiStateCreationError, WasiVFork, }; @@ -1010,39 +1011,52 @@ impl WasiEnv { /// Cleans up all the open files (if this is the main thread) #[allow(clippy::await_holding_lock)] pub fn blocking_cleanup(&self, exit_code: Option) { - __asyncify_light(self, None, async { - self.cleanup(exit_code).await; - Ok(()) - }) - .ok(); + let cleanup = self.cleanup(exit_code); + + #[cfg(feature = "js")] + self.tasks() + .task_shared(Box::new(|| { + Box::pin(async move { + cleanup.await; + }) + })) + .ok(); + + #[cfg(feature = "sys")] + self.tasks().block_on(cleanup); } /// Cleans up all the open files (if this is the main thread) #[allow(clippy::await_holding_lock)] - pub async fn cleanup(&self, exit_code: Option) { + pub fn cleanup(&self, exit_code: Option) -> BoxFuture<'static, ()> { const CLEANUP_TIMEOUT: Duration = Duration::from_secs(10); // 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()); - // Perform the clean operation using the asynchronous runtime - let timeout = self.tasks().sleep_now(CLEANUP_TIMEOUT); - tokio::select! { - _ = timeout => { - tracing::warn!( - "WasiEnv::cleanup has timed out after {CLEANUP_TIMEOUT:?}" - ); - }, - _ = self.state.fs.close_all() => { } - } - // Now send a signal that the thread is terminated self.process.signal_process(Signal::Sigquit); // Terminate the process let exit_code = exit_code.unwrap_or_else(|| Errno::Canceled.into()); self.process.terminate(exit_code); + + let timeout = self.tasks().sleep_now(CLEANUP_TIMEOUT); + let state = self.state.clone(); + Box::pin(async move { + // Perform the clean operation using the asynchronous runtime + tokio::select! { + _ = timeout => { + tracing::warn!( + "WasiEnv::cleanup has timed out after {CLEANUP_TIMEOUT:?}" + ); + }, + _ = state.fs.close_all() => { } + } + }) + } else { + Box::pin(async {}) } } } diff --git a/lib/wasix/src/utils/owned_mutex_guard.rs b/lib/wasix/src/utils/owned_mutex_guard.rs index 430d0fd3500..4edc7103d12 100644 --- a/lib/wasix/src/utils/owned_mutex_guard.rs +++ b/lib/wasix/src/utils/owned_mutex_guard.rs @@ -97,6 +97,8 @@ pub(crate) struct OwnedRwLockReadGuard { ownership: Arc>, } +unsafe impl Send for OwnedRwLockReadGuard where T: Send {} + impl Drop for OwnedRwLockReadGuard where T: Sized, @@ -173,6 +175,8 @@ pub(crate) struct OwnedRwLockWriteGuard { ownership: Arc>, } +unsafe impl Send for OwnedRwLockWriteGuard where T: Send {} + impl Drop for OwnedRwLockWriteGuard where T: Sized, From 1741c96201e1521e4e15ab943dcf86046a8eafd8 Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Fri, 16 Jun 2023 22:41:06 +1000 Subject: [PATCH 11/21] Updated the website version and added a config file --- lib/wasi-web/README.md | 8 -------- lib/wasi-web/app.yaml | 4 ++++ lib/wasi-web/cfg/config.toml | 13 +++++++++++++ lib/wasi-web/wasmer.toml | 11 +++++++++++ 4 files changed, 28 insertions(+), 8 deletions(-) create mode 100644 lib/wasi-web/app.yaml create mode 100644 lib/wasi-web/cfg/config.toml create mode 100644 lib/wasi-web/wasmer.toml diff --git a/lib/wasi-web/README.md b/lib/wasi-web/README.md index 1c13821dea1..df54ca8c982 100644 --- a/lib/wasi-web/README.md +++ b/lib/wasi-web/README.md @@ -1,11 +1,3 @@ # https://wasmer.sh WebSite Contains all the files needed to serve the wasmer.sh website natively from WebAssmebly - this includes a HTTP server - -# deploy - -cargo install wasmer-cli --git https://github.com/wasmerio/wasmer --branch upgrade-edge-cli -wasmer app create static-site -or wasmer app create -now produce an app.yaml -wasmer deploy deploys it \ No newline at end of file diff --git a/lib/wasi-web/app.yaml b/lib/wasi-web/app.yaml new file mode 100644 index 00000000000..03104bbb4fe --- /dev/null +++ b/lib/wasi-web/app.yaml @@ -0,0 +1,4 @@ +--- +kind: wasmer.io/App.v0 +name: wasmer-sh +package: wasmer/wasmer-sh diff --git a/lib/wasi-web/cfg/config.toml b/lib/wasi-web/cfg/config.toml new file mode 100644 index 00000000000..c0309689fb8 --- /dev/null +++ b/lib/wasi-web/cfg/config.toml @@ -0,0 +1,13 @@ +[general] +host = "::" +port = 80 +root = "/public" +log-level = "info" +cache-control-headers = false +compression = true + +[advanced] + +[[advanced.headers]] +source = "**" +headers = { Access-Control-Allow-Origin = "*", Cross-Origin-Embedder-Policy = "require-corp", Cross-Origin-Opener-Policy = "same-origin", Cache-Control = "max-age=600" } diff --git a/lib/wasi-web/wasmer.toml b/lib/wasi-web/wasmer.toml new file mode 100644 index 00000000000..aa1d14c23d5 --- /dev/null +++ b/lib/wasi-web/wasmer.toml @@ -0,0 +1,11 @@ +[package] +name = 'wasmer/wasmer-sh' +version = '0.2.2' +description = 'Container that holds the wasmer.sh website.' + +[dependencies] +"sharrattj/static-web-server" = '1' + +[fs] +public = 'dist' +cfg = 'cfg' From e5e2b4b721fbc00e0ce6a5525944a48655be5739 Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Sat, 17 Jun 2023 23:22:12 +1000 Subject: [PATCH 12/21] Performance fixes for wasmer.sh --- lib/wasi-web/build.sh | 3 + lib/wasi-web/public/core.js | 83 ++++++ lib/wasi-web/public/index.html | 50 ++-- lib/wasi-web/public/worker.js | 81 +----- lib/wasi-web/src/lib.rs | 1 + lib/wasi-web/src/module_cache.rs | 157 +++++++++++ lib/wasi-web/src/pool.rs | 310 +++++++++++++++------- lib/wasi-web/src/runtime.rs | 5 +- lib/wasi-web/wasmer.toml | 2 +- lib/wasi-web/webpack.config.mjs | 69 ++--- lib/wasix/src/runtime/task_manager/mod.rs | 3 + 11 files changed, 535 insertions(+), 229 deletions(-) create mode 100755 lib/wasi-web/build.sh create mode 100644 lib/wasi-web/public/core.js create mode 100644 lib/wasi-web/src/module_cache.rs diff --git a/lib/wasi-web/build.sh b/lib/wasi-web/build.sh new file mode 100755 index 00000000000..60f3c557a03 --- /dev/null +++ b/lib/wasi-web/build.sh @@ -0,0 +1,3 @@ +#/bin/bash -e +rm -f dist/* +npm run build diff --git a/lib/wasi-web/public/core.js b/lib/wasi-web/public/core.js new file mode 100644 index 00000000000..178df2cec1e --- /dev/null +++ b/lib/wasi-web/public/core.js @@ -0,0 +1,83 @@ +import { schedule_wasm_task, register_web_worker, return_web_worker, get_web_worker, claim_web_worker } from '../../..'; + +// WebWorkers are returned to a pool so that they can be reused, this +// is important as the cost of starting another web worker is a very +// costly process as it needs to do a fetch back to the main servers +// to load the main WASM module + +// The purpose of this file is two-fold: +// First: Expose a function to start a web worker. This function must +// not be inlined into the Rust lib, as otherwise bundlers could not +// bundle it -- huh. +export function startWorker(module, memory, state, opts) { + try { + const worker = new Worker(new URL('./worker.js', + import.meta.url), opts); + + // When the worker wants to schedule some work it will + // post a message back to the main thread, otherwise it + // might actually post itself back to the main thread + // when its finished + worker.onmessage = async ev => { + // Web worker has scheduled some work which the main + // thread must orchestrate + let [task, module, memory] = ev.data; + await schedule_wasm_task(task, module, memory); + }; + worker.postMessage([module, memory, state]); + } catch (err) { + return new Promise((res, rej) => { + rej(err); + }); + } + return new Promise((res, rej) => { + res(); + }); +} +export function startWasm(module, memory, ctx, opts, wasm_module, wasm_memory, wasm_cache) { + return new Promise(async (res, rej) => { + try { + // Attempt to get a worker from the pool before we + // create a new web worker as its quite expensive + var worker = null; + var worker_id = claim_web_worker(); + + // If the worker is not available then create a new one + if (worker_id == null) { + worker = new Worker(new URL('./worker.js', + import.meta.url), opts); + + // Register the web worker in the thread pool + worker_id = register_web_worker(worker); + } else { + // If we have claimed a web worker then we need to + // get a reference to it + worker = get_web_worker(worker_id); + } + + // When the worker wants to schedule some work it will + // post a message back to the main thread, otherwise it + // might actually post itself back to the main thread + // when its finished + worker.onmessage = async ev => { + if (ev.data.length == 3) { + // Web worker has scheduled some work which the main + // thread must orchestrate + let [task, module, memory] = ev.data; + await schedule_wasm_task(task, module, memory); + } else { + // Web worker has finished and is now returned to the + // main pool so it can be reused + let [id] = ev.data; + return_web_worker(id); + } + }; + worker.postMessage([worker_id, module, memory, ctx, wasm_module, wasm_memory, wasm_cache]); + } catch (err) { + return new Promise((res, rej) => { + rej(err); + }); + } + res(); + }); +} diff --git a/lib/wasi-web/public/index.html b/lib/wasi-web/public/index.html index 0dab10908a0..a0a9c5f4b2a 100644 --- a/lib/wasi-web/public/index.html +++ b/lib/wasi-web/public/index.html @@ -1,26 +1,30 @@ - - - - - - - - - - - - - - - wasmer.sh - - - - - -
- - + + + + + + + + + + + + + + + wasmer.sh + + + + + + +
+ + + \ No newline at end of file diff --git a/lib/wasi-web/public/worker.js b/lib/wasi-web/public/worker.js index 66daa15ed94..f57ff6051e1 100644 --- a/lib/wasi-web/public/worker.js +++ b/lib/wasi-web/public/worker.js @@ -1,52 +1,3 @@ -import { schedule_wasm_task } from '../../..'; - -// The purpose of this file is two-fold: -// First: Expose a function to start a web worker. This function must -// not be inlined into the Rust lib, as otherwise bundlers could not -// bundle it -- huh. -export function startWorker(module, memory, state, opts, helper) { - try { - const worker = new Worker(new URL('./worker.js', - import.meta.url), opts); - - // When the worker wants to schedule some work it will - // post a message back to the main thread - worker.onmessage = async ev => { - let [task, module, memory] = ev.data; - schedule_wasm_task(task, module, memory); - }; - worker.postMessage([module, memory, state, helper.mainJS()]); - } catch (err) { - return new Promise((res, rej) => { - rej(err); - }); - } - return new Promise((res, rej) => { - res(); - }); -} -export function startWasm(module, memory, ctx, opts, helper, wasm_module, wasm_memory) { - try { - const worker = new Worker(new URL('./worker.js', - import.meta.url), opts); - - // When the worker wants to schedule some work it will - // post a message back to the main thread - worker.onmessage = async ev => { - let [task, module, memory] = ev.data; - schedule_wasm_task(task, module, memory); - }; - worker.postMessage([module, memory, ctx, helper.mainJS(), wasm_module, wasm_memory]); - } catch (err) { - return new Promise((res, rej) => { - rej(err); - }); - } - return new Promise((res, rej) => { - res(); - }); -} - export function scheduleTask(task, module, memory) { postMessage([task, module, memory]); } @@ -68,39 +19,35 @@ if (isWorker()) { // When bundling with webpack, this file is relative to the wasm // module file (or package.json) located in `../../..` generated by // wasm-pack. - // When using it without any bundlers, the module that - // provided the `helper` object below is loaded; in other words - // the main wasm module. - if (ev.data.length == 4) { - let [module, memory, state, mainJS] = ev.data; - const importFrom = (typeof __webpack_require__ === 'function') ? import('../../..') : import(mainJS); + if (ev.data.length == 3) { + let [module, memory, state] = ev.data; const { default: init, worker_entry_point, - } = await importFrom; + } = await import('../../..'); await init(module, memory); worker_entry_point(state); } else { + var is_returned = false; try { - // There shouldn't be any additional messages after the first so we - // need to unhook it - self.onmessage = ev => { - console.error("wasm threads can only run a single process then exit", ev); - } - - let [module, memory, ctx, mainJS, wasm_module, wasm_memory] = ev.data; - const importFrom = (typeof __webpack_require__ === 'function') ? import('../../..') : import(mainJS); + let [id, module, memory, ctx, wasm_module, wasm_memory, wasm_cache] = ev.data; const { default: init, wasm_entry_point, - } = await importFrom; + } = await import('../../..'); await init(module, memory); - wasm_entry_point(ctx, wasm_module, wasm_memory); + wasm_entry_point(ctx, wasm_module, wasm_memory, wasm_cache); + + // Return the web worker to the thread pool + postMessage([id]); + is_returned = true; } finally { //Terminate the worker - close(); + if (is_returned == false) { + close(); + } } } } diff --git a/lib/wasi-web/src/lib.rs b/lib/wasi-web/src/lib.rs index 169c15810e1..15eb0b235ed 100644 --- a/lib/wasi-web/src/lib.rs +++ b/lib/wasi-web/src/lib.rs @@ -1,6 +1,7 @@ mod common; mod glue; mod interval; +mod module_cache; mod pool; mod runtime; diff --git a/lib/wasi-web/src/module_cache.rs b/lib/wasi-web/src/module_cache.rs new file mode 100644 index 00000000000..aaa97e6d177 --- /dev/null +++ b/lib/wasi-web/src/module_cache.rs @@ -0,0 +1,157 @@ +use std::{cell::RefCell, collections::HashMap}; + +use bytes::Bytes; +use wasm_bindgen::{JsCast, JsValue}; +use wasmer::{Engine, Module}; +use wasmer_wasix::runtime::module_cache::{CacheError, ModuleCache, ModuleHash}; + +use crate::pool::{schedule_task, WebRunCommand}; + +std::thread_local! { + static CACHED_MODULES: RefCell> + = RefCell::new(HashMap::new()); +} + +/// A cache that will cache modules in a thread-local variable. +#[derive(Debug, Default)] +#[non_exhaustive] +pub struct WebWorkerModuleCache {} + +impl WebWorkerModuleCache { + fn lookup(&self, key: ModuleHash, deterministic_id: &str) -> Option { + let key = (key, deterministic_id.to_string()); + CACHED_MODULES.with(|m| m.borrow().get(&key).cloned()) + } + + fn insert(&self, key: ModuleHash, module: &Module, deterministic_id: &str) -> bool { + let key = (key, deterministic_id.to_string()); + let old = CACHED_MODULES.with(|m| m.borrow_mut().insert(key, module.clone())); + let already_exists = old.is_some(); + already_exists + } + + fn cache_in_main(&self, key: ModuleHash, module: &Module, deterministic_id: &str) { + let deterministic_id = deterministic_id.to_string(); + let task = Box::new(WebRunCommand::ExecModule { + run: Box::new(move |module| { + let key = (key, deterministic_id); + CACHED_MODULES.with(|m| m.borrow_mut().insert(key, module.clone())); + }), + module_bytes: module.serialize().unwrap(), + }); + let task = Box::into_raw(task); + + let module = JsValue::from(module.clone()) + .dyn_into::() + .unwrap(); + schedule_task(JsValue::from(task as u32), module, JsValue::NULL); + } + + pub fn export() -> JsValue { + CACHED_MODULES.with(|m| { + // Annotation is here to prevent spurious IDE warnings. + #[allow(unused_unsafe)] + unsafe { + let entries = js_sys::Array::new_with_length(m.borrow().len() as u32); + + let mut i = 0; + for ((key, deterministic_id), module) in m.borrow().iter() { + let entry = js_sys::Object::new(); + + js_sys::Reflect::set( + &entry, + &"key".into(), + &JsValue::from(base64::encode(key.as_bytes())), + ) + .unwrap(); + + js_sys::Reflect::set( + &entry, + &"deterministic_id".into(), + &JsValue::from(deterministic_id.clone()), + ) + .unwrap(); + + js_sys::Reflect::set(&entry, &"module".into(), &JsValue::from(module.clone())) + .unwrap(); + + let module_bytes = Box::new(module.serialize().unwrap()); + let module_bytes = Box::into_raw(module_bytes); + js_sys::Reflect::set( + &entry, + &"module_bytes".into(), + &JsValue::from(module_bytes as u32), + ) + .unwrap(); + + entries.set(i, JsValue::from(entry)); + i += 1; + } + + JsValue::from(entries) + } + }) + } + + pub fn import(cache: JsValue) { + CACHED_MODULES.with(|m| { + // Annotation is here to prevent spurious IDE warnings. + #[allow(unused_unsafe)] + unsafe { + let entries = JsValue::from(cache).dyn_into::().unwrap(); + + for i in 0..entries.length() { + let entry = entries.get(i); + + let key = js_sys::Reflect::get(&entry, &"key".into()).unwrap(); + let key = JsValue::as_string(&key).unwrap(); + let key = base64::decode(key).unwrap(); + let key: [u8; 32] = key.try_into().unwrap(); + let key = ModuleHash::from_bytes(key); + + let deterministic_id = + js_sys::Reflect::get(&entry, &"deterministic_id".into()).unwrap(); + let deterministic_id = JsValue::as_string(&deterministic_id).unwrap(); + + let module_bytes = + js_sys::Reflect::get(&entry, &"module_bytes".into()).unwrap(); + let module_bytes: u32 = module_bytes.as_f64().unwrap() as u32; + let module_bytes = module_bytes as *mut Bytes; + let module_bytes = unsafe { Box::from_raw(module_bytes) }; + + let module = js_sys::Reflect::get(&entry, &"module".into()).unwrap(); + let module = module.dyn_into::().unwrap(); + let module: Module = (module, *module_bytes).into(); + + let key = (key, deterministic_id); + m.borrow_mut().insert(key, module.clone()); + } + } + }); + } +} + +#[async_trait::async_trait] +impl ModuleCache for WebWorkerModuleCache { + async fn load(&self, key: ModuleHash, engine: &Engine) -> Result { + self.lookup(key, engine.deterministic_id()) + .ok_or(CacheError::NotFound) + } + + async fn save( + &self, + key: ModuleHash, + engine: &Engine, + module: &Module, + ) -> Result<(), CacheError> { + let already_exists = self.insert(key, module, engine.deterministic_id()); + + // We also send the module to the main thread via a postMessage + // which they relays it to all the web works + if already_exists == false { + self.cache_in_main(key, module, engine.deterministic_id()); + } + + Ok(()) + } +} diff --git a/lib/wasi-web/src/pool.rs b/lib/wasi-web/src/pool.rs index 18417b8d1a8..ea41dece7e1 100644 --- a/lib/wasi-web/src/pool.rs +++ b/lib/wasi-web/src/pool.rs @@ -32,7 +32,8 @@ use wasmer_wasix::{ capture_snapshot, runtime::{ task_manager::{ - TaskWasm, TaskWasmRun, TaskWasmRunProperties, WasmResumeTask, WasmResumeTrigger, + TaskExecModule, TaskWasm, TaskWasmRun, TaskWasmRunProperties, WasmResumeTask, + WasmResumeTrigger, }, SpawnMemoryType, }, @@ -45,7 +46,7 @@ use web_sys::{DedicatedWorkerGlobalScope, WorkerOptions, WorkerType}; use xterm_js_rs::Terminal; use super::{common::*, interval::*}; -use crate::runtime::WebTaskManager; +use crate::{module_cache::WebWorkerModuleCache, runtime::WebTaskManager}; pub type BoxRun<'a> = Box; @@ -53,7 +54,7 @@ pub type BoxRunAsync<'a, T> = Box Pin + 'static>> + Send + 'a>; #[derive(Debug, Clone)] -enum WasmMemoryType { +pub enum WasmMemoryType { CreateMemory, CreateMemoryOfType(MemoryType), ShareMemory(MemoryType), @@ -61,7 +62,7 @@ enum WasmMemoryType { #[derive(Derivative)] #[derivative(Debug)] -struct WasmRunTrigger { +pub struct WasmRunTrigger { #[derivative(Debug = "ignore")] run: Box, memory_ty: MemoryType, @@ -70,17 +71,24 @@ struct WasmRunTrigger { #[derive(Derivative)] #[derivative(Debug)] -struct WasmRunCommand { - #[derivative(Debug = "ignore")] - run: Box, - run_type: WasmMemoryType, - env: WasiEnv, - module_bytes: Bytes, - snapshot: Option, - trigger: Option, - update_layout: bool, - result: Option>, - pool: WebThreadPool, +pub enum WebRunCommand { + ExecModule { + #[derivative(Debug = "ignore")] + run: Box, + module_bytes: Bytes, + }, + SpawnWasm { + #[derivative(Debug = "ignore")] + run: Box, + run_type: WasmMemoryType, + env: WasiEnv, + module_bytes: Bytes, + snapshot: Option, + trigger: Option, + update_layout: bool, + result: Option>, + pool: WebThreadPool, + }, } trait AssertSendSync: Send + Sync {} @@ -165,23 +173,7 @@ pub struct ThreadState { init: Mutex>, } -#[wasm_bindgen] -pub struct LoaderHelper {} -#[wasm_bindgen] -impl LoaderHelper { - #[wasm_bindgen(js_name = mainJS)] - pub fn main_js(&self) -> JsString { - #[wasm_bindgen] - extern "C" { - #[wasm_bindgen(js_namespace = ["import", "meta"], js_name = url)] - static URL: JsString; - } - - URL.clone() - } -} - -#[wasm_bindgen(module = "/public/worker.js")] +#[wasm_bindgen(module = "/public/core.js")] extern "C" { #[wasm_bindgen(js_name = "startWorker")] fn start_worker( @@ -189,7 +181,6 @@ extern "C" { memory: JsValue, shared_data: JsValue, opts: WorkerOptions, - builder: LoaderHelper, ) -> Promise; #[wasm_bindgen(js_name = "startWasm")] @@ -198,13 +189,16 @@ extern "C" { memory: JsValue, ctx: JsValue, opts: WorkerOptions, - builder: LoaderHelper, wasm_module: js_sys::WebAssembly::Module, wasm_memory: JsValue, + wasm_cache: JsValue, ) -> Promise; +} +#[wasm_bindgen(module = "/public/worker.js")] +extern "C" { #[wasm_bindgen(js_name = "scheduleTask")] - fn schedule_task(task: JsValue, module: js_sys::WebAssembly::Module, memory: JsValue); + pub fn schedule_task(task: JsValue, module: js_sys::WebAssembly::Module, memory: JsValue); } fn copy_memory(memory: JsValue, ty: MemoryType) -> Result { @@ -377,7 +371,7 @@ impl WebThreadPool { } }; - let task = Box::new(WasmRunCommand { + let task = Box::new(WebRunCommand::SpawnWasm { trigger: trigger.map(|trigger| WasmRunTrigger { run: trigger, memory_ty: memory_ty.expect("triggers must have the a known memory type"), @@ -523,7 +517,6 @@ impl PoolState { wasm_bindgen::memory(), JsValue::from(ptr as u32), opts, - LoaderHelper {}, )); wasm_bindgen_futures::spawn_local(async move { @@ -704,85 +697,198 @@ pub fn wasm_entry_point( task_ptr: u32, wasm_module: js_sys::WebAssembly::Module, wasm_memory: JsValue, + wasm_cache: JsValue, ) { + // Import the WASM cache + WebWorkerModuleCache::import(wasm_cache); + // Grab the run wrapper that passes us the rust variables (and extract the callback) - let task = task_ptr as *mut WasmRunCommand; - let mut task = unsafe { Box::from_raw(task) }; - let run = (*task).run; - - // If there is a trigger then run it - let trigger = task.trigger.take(); - if let Some(trigger) = trigger { - let trigger_run = trigger.run; - let tasks = task.env.tasks(); - task.result = Some(tasks.block_on(trigger_run())); + let task = task_ptr as *mut WebRunCommand; + let task = unsafe { Box::from_raw(task) }; + match *task { + WebRunCommand::ExecModule { run, module_bytes } => { + let module: Module = (wasm_module, module_bytes).into(); + run(module); + } + WebRunCommand::SpawnWasm { + run, + run_type, + env, + module_bytes, + snapshot, + mut trigger, + update_layout, + mut result, + .. + } => { + // If there is a trigger then run it + let trigger = trigger.take(); + if let Some(trigger) = trigger { + let trigger_run = trigger.run; + let tasks = env.tasks(); + result = Some(tasks.block_on(trigger_run())); + } + + // Invoke the callback which will run the web assembly module + if let Some((ctx, store)) = _build_ctx_and_store( + wasm_module, + wasm_memory, + module_bytes, + env, + run_type, + snapshot, + update_layout, + ) { + run(TaskWasmRunProperties { + ctx, + store, + trigger_result: result, + }); + }; + } } +} - // Invoke the callback which will run the web assembly module - if let Some((ctx, store)) = _build_ctx_and_store( - wasm_module, - wasm_memory, - task.module_bytes, - task.env, - task.run_type, - task.snapshot, - task.update_layout, - ) { - run(TaskWasmRunProperties { - ctx, - store, - trigger_result: task.result, +struct WebWorker { + worker: JsValue, + available: bool, +} + +std::thread_local! { + static WEB_WORKER_POOL: RefCell> + = RefCell::new(Vec::new()); +} + +#[wasm_bindgen()] +pub fn register_web_worker(web_worker: JsValue) -> u32 { + WEB_WORKER_POOL.with(|u| { + let mut workers = u.borrow_mut(); + workers.push(WebWorker { + worker: web_worker, + available: false, }); - }; + workers.len() - 1 + }) as u32 +} + +#[wasm_bindgen()] +pub fn return_web_worker(id: u32) { + WEB_WORKER_POOL.with(|u| { + let mut workers = u.borrow_mut(); + let worker = workers.get_mut(id as usize); + if let Some(worker) = worker { + worker.available = true; + } + }); } #[wasm_bindgen()] -pub fn schedule_wasm_task( +pub fn get_web_worker(id: u32) -> JsValue { + WEB_WORKER_POOL.with(|u| { + let workers = u.borrow(); + if let Some(worker) = workers.get(id as usize) { + worker.worker.clone() + } else { + JsValue::NULL + } + }) +} + +#[wasm_bindgen()] +pub fn claim_web_worker() -> JsValue { + WEB_WORKER_POOL.with(|u| { + let mut workers = u.borrow_mut(); + for (n, worker) in workers.iter_mut().enumerate() { + if worker.available { + worker.available = false; + return JsValue::from(n); + } + } + JsValue::NULL + }) +} + +#[wasm_bindgen()] +pub async fn schedule_wasm_task( task_ptr: u32, wasm_module: js_sys::WebAssembly::Module, wasm_memory: JsValue, ) { // Grab the run wrapper that passes us the rust variables - let task = task_ptr as *mut WasmRunCommand; - let mut task = unsafe { Box::from_raw(task) }; - - // We will pass it on now - let trigger = task.trigger.take(); - let trigger_rx = if let Some(trigger) = trigger { - let (tx, rx) = tokio::sync::mpsc::unbounded_channel(); - task.pool.spawn_shared(Box::new(|| { - Box::pin(async move { - let run = trigger.run; - tx.send(run().await).ok(); - }) - })); - Some(rx) - } else { - None - }; + let task = task_ptr as *mut WebRunCommand; + let task = unsafe { Box::from_raw(task) }; + match *task { + WebRunCommand::ExecModule { run, module_bytes } => { + let module: Module = (wasm_module, module_bytes).into(); + run(module); + } + WebRunCommand::SpawnWasm { + run, + run_type, + env, + module_bytes, + snapshot, + mut trigger, + update_layout, + mut result, + pool, + } => { + // We will pass it on now + let trigger = trigger.take(); + let trigger_rx = if let Some(trigger) = trigger { + let (tx, rx) = tokio::sync::mpsc::unbounded_channel(); + + // We execute the trigger on another thread as any atomic operations (such as wait) + // are not allowed on the main thread and even through the tokio is asynchronous that + // does not mean it does not have short synchronous blocking events (which it does) + pool.spawn_shared(Box::new(|| { + Box::pin(async move { + let run = trigger.run; + let ret = run().await; + tx.send(ret).ok(); + }) + })); + Some(rx) + } else { + None + }; - // We will now spawn the process in its own thread - let mut opts = WorkerOptions::new(); - opts.type_(WorkerType::Module); - opts.name(&*format!("Wasm-Thread")); + // Export the cache + let wasm_cache = WebWorkerModuleCache::export(); - wasm_bindgen_futures::spawn_local(async move { - if let Some(mut trigger_rx) = trigger_rx { - task.result = trigger_rx.recv().await; - } + // We will now spawn the process in its own thread + let mut opts = WorkerOptions::new(); + opts.type_(WorkerType::Module); + opts.name(&*format!("Wasm-Thread")); - let task = Box::into_raw(task); - let result = wasm_bindgen_futures::JsFuture::from(start_wasm( - wasm_bindgen::module() - .dyn_into::() - .unwrap(), - wasm_bindgen::memory(), - JsValue::from(task as u32), - opts, - LoaderHelper {}, - wasm_module, - wasm_memory, - )); - _process_worker_result(result, None).await - }); + if let Some(mut trigger_rx) = trigger_rx { + result = trigger_rx.recv().await; + } + + let task = Box::new(WebRunCommand::SpawnWasm { + run, + run_type, + env, + module_bytes, + snapshot, + trigger: None, + update_layout, + result, + pool, + }); + let task = Box::into_raw(task); + let result = wasm_bindgen_futures::JsFuture::from(start_wasm( + wasm_bindgen::module() + .dyn_into::() + .unwrap(), + wasm_bindgen::memory(), + JsValue::from(task as u32), + opts, + wasm_module, + wasm_memory, + wasm_cache, + )); + _process_worker_result(result, None).await + } + } } diff --git a/lib/wasi-web/src/runtime.rs b/lib/wasi-web/src/runtime.rs index 864e2c47443..8f76f9deeea 100644 --- a/lib/wasi-web/src/runtime.rs +++ b/lib/wasi-web/src/runtime.rs @@ -21,7 +21,7 @@ use wasmer_wasix::{ http::{DynHttpClient, HttpRequest, HttpResponse}, os::{TtyBridge, TtyOptions}, runtime::{ - module_cache::{ModuleCache, SharedCache, ThreadLocalCache}, + module_cache::ModuleCache, package_loader::{BuiltinPackageLoader, PackageLoader}, resolver::{Source, WapmSource}, task_manager::TaskWasm, @@ -37,6 +37,7 @@ use super::webgl::WebGl; #[cfg(feature = "webgl")] use super::webgl::WebGlCommand; use super::{common::*, pool::WebThreadPool}; +use crate::module_cache::WebWorkerModuleCache; #[derive(Debug)] pub(crate) enum TerminalCommandRx { @@ -89,7 +90,7 @@ impl WebRuntime { // wasm32-unknown-unknown and running in a browser), the in-memory layer // should still work. let package_loader = BuiltinPackageLoader::new_only_client(http_client.clone()); - let module_cache = ThreadLocalCache::default(); + let module_cache = WebWorkerModuleCache::default(); WebRuntime { pool, tasks: runtime, diff --git a/lib/wasi-web/wasmer.toml b/lib/wasi-web/wasmer.toml index aa1d14c23d5..c0020c94cdb 100644 --- a/lib/wasi-web/wasmer.toml +++ b/lib/wasi-web/wasmer.toml @@ -1,6 +1,6 @@ [package] name = 'wasmer/wasmer-sh' -version = '0.2.2' +version = '0.1.3' description = 'Container that holds the wasmer.sh website.' [dependencies] diff --git a/lib/wasi-web/webpack.config.mjs b/lib/wasi-web/webpack.config.mjs index 2aaa67cda59..5a661059240 100644 --- a/lib/wasi-web/webpack.config.mjs +++ b/lib/wasi-web/webpack.config.mjs @@ -16,50 +16,51 @@ export default { { from: resolve(__dirname, "public/index.html") }, { from: resolve(__dirname, "public/wasmer.css") }, { from: resolve(__dirname, "public/worker.js") }, + { from: resolve(__dirname, "public/favicon.png") }, ], }), new WasmPackPlugin({ - crateDirectory: resolve(__dirname, './'), + crateDirectory: resolve(__dirname, './'), - // Check https://rustwasm.github.io/wasm-pack/book/commands/build.html for - // the available set of arguments. - // - // Optional space delimited arguments to appear before the wasm-pack - // command. Default arguments are `--verbose`. - args: '--log-level warn', - // Default arguments are `--typescript --target browser --mode normal`. - extraArgs: '--target web', + // Check https://rustwasm.github.io/wasm-pack/book/commands/build.html for + // the available set of arguments. + // + // Optional space delimited arguments to appear before the wasm-pack + // command. Default arguments are `--verbose`. + args: '--log-level warn', + // Default arguments are `--typescript --target browser --mode normal`. + extraArgs: '--target web', - // Optional array of absolute paths to directories, changes to which - // will trigger the build. - // watchDirectories: [ - // path.resolve(__dirname, "another-crate/src") - // ], + // Optional array of absolute paths to directories, changes to which + // will trigger the build. + // watchDirectories: [ + // path.resolve(__dirname, "another-crate/src") + // ], - // The same as the `--out-dir` option for `wasm-pack` - // outDir: "pkg", + // The same as the `--out-dir` option for `wasm-pack` + // outDir: "pkg", - // The same as the `--out-name` option for `wasm-pack` - // outName: "index", + // The same as the `--out-name` option for `wasm-pack` + // outName: "index", - // If defined, `forceWatch` will force activate/deactivate watch mode for - // `.rs` files. - // - // The default (not set) aligns watch mode for `.rs` files to Webpack's - // watch mode. - // forceWatch: true, + // If defined, `forceWatch` will force activate/deactivate watch mode for + // `.rs` files. + // + // The default (not set) aligns watch mode for `.rs` files to Webpack's + // watch mode. + // forceWatch: true, - // If defined, `forceMode` will force the compilation mode for `wasm-pack` - // - // Possible values are `development` and `production`. - // - // the mode `development` makes `wasm-pack` build in `debug` mode. - // the mode `production` makes `wasm-pack` build in `release` mode. - // forceMode: "development", + // If defined, `forceMode` will force the compilation mode for `wasm-pack` + // + // Possible values are `development` and `production`. + // + // the mode `development` makes `wasm-pack` build in `debug` mode. + // the mode `production` makes `wasm-pack` build in `release` mode. + // forceMode: "development", - // Controls plugin output verbosity, either 'info' or 'error'. - // Defaults to 'info'. - // pluginLogLevel: 'info' + // Controls plugin output verbosity, either 'info' or 'error'. + // Defaults to 'info'. + // pluginLogLevel: 'info' }), ], devServer: { diff --git a/lib/wasix/src/runtime/task_manager/mod.rs b/lib/wasix/src/runtime/task_manager/mod.rs index 7495881fb1d..f04cd3ee791 100644 --- a/lib/wasix/src/runtime/task_manager/mod.rs +++ b/lib/wasix/src/runtime/task_manager/mod.rs @@ -48,6 +48,9 @@ pub struct TaskWasmRunProperties { /// Callback that will be invoked pub type TaskWasmRun = dyn FnOnce(TaskWasmRunProperties) + Send + 'static; +/// Callback that will be invoked +pub type TaskExecModule = dyn FnOnce(Module) + Send + 'static; + /// Represents a WASM task that will be executed on a dedicated thread pub struct TaskWasm<'a, 'b> { pub run: Box, From 6b66d68526b868a37e9337b44e1af246aeb05b4f Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Mon, 26 Jun 2023 05:30:48 +1000 Subject: [PATCH 13/21] Corrected the versioning information --- lib/wasi-web/wasmer.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/wasi-web/wasmer.toml b/lib/wasi-web/wasmer.toml index c0020c94cdb..fe41fb072b3 100644 --- a/lib/wasi-web/wasmer.toml +++ b/lib/wasi-web/wasmer.toml @@ -1,10 +1,10 @@ [package] -name = 'wasmer/wasmer-sh' +name = 'wasmer/wasmer-sh-async' version = '0.1.3' description = 'Container that holds the wasmer.sh website.' [dependencies] -"sharrattj/static-web-server" = '1' +"wasmer/static-web-server-async" = '1' [fs] public = 'dist' From 48e7a92ea8019007daec883c94ecb7afb87687fe Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Mon, 26 Jun 2023 05:38:30 +1000 Subject: [PATCH 14/21] Increased the number of background worker threads --- lib/wasi-web/cfg/config.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/wasi-web/cfg/config.toml b/lib/wasi-web/cfg/config.toml index c0309689fb8..f922e474006 100644 --- a/lib/wasi-web/cfg/config.toml +++ b/lib/wasi-web/cfg/config.toml @@ -5,6 +5,8 @@ root = "/public" log-level = "info" cache-control-headers = false compression = true +threads-multiplier = 16 +max-blocking-threads = 32 [advanced] From eb8cdfbe13433286a40ea1e7a228fac4b08af495 Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Mon, 26 Jun 2023 05:38:44 +1000 Subject: [PATCH 15/21] Increased the number of background worker threads --- lib/wasi-web/wasmer.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/wasi-web/wasmer.toml b/lib/wasi-web/wasmer.toml index fe41fb072b3..8bf805bc265 100644 --- a/lib/wasi-web/wasmer.toml +++ b/lib/wasi-web/wasmer.toml @@ -1,6 +1,6 @@ [package] name = 'wasmer/wasmer-sh-async' -version = '0.1.3' +version = '0.1.4' description = 'Container that holds the wasmer.sh website.' [dependencies] From 0dedce3baf30ff2c6759f720f26996e8344a4c39 Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Mon, 26 Jun 2023 05:55:53 +1000 Subject: [PATCH 16/21] Bumped the version --- lib/wasi-web/wasmer.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/wasi-web/wasmer.toml b/lib/wasi-web/wasmer.toml index 8bf805bc265..7a5df6e7c5b 100644 --- a/lib/wasi-web/wasmer.toml +++ b/lib/wasi-web/wasmer.toml @@ -1,6 +1,6 @@ [package] name = 'wasmer/wasmer-sh-async' -version = '0.1.4' +version = '0.1.5' description = 'Container that holds the wasmer.sh website.' [dependencies] From 525bd8cee73b1ac1eb3f53729f7b9f9a4da2a2f1 Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Mon, 26 Jun 2023 07:20:44 +1000 Subject: [PATCH 17/21] Released new verison --- lib/wasi-web/wasmer.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/wasi-web/wasmer.toml b/lib/wasi-web/wasmer.toml index 7a5df6e7c5b..e684ff8a0f6 100644 --- a/lib/wasi-web/wasmer.toml +++ b/lib/wasi-web/wasmer.toml @@ -1,6 +1,6 @@ [package] name = 'wasmer/wasmer-sh-async' -version = '0.1.5' +version = '1.0.3' description = 'Container that holds the wasmer.sh website.' [dependencies] From 2925ee99a3f166f73a02507b8e826562f1246f01 Mon Sep 17 00:00:00 2001 From: Michael-F-Bryan Date: Wed, 28 Jun 2023 13:33:50 +0800 Subject: [PATCH 18/21] Pin wasmer-web to "nightly-2023-05-25" --- .github/workflows/web.yaml | 36 ++++++++++++++++---------------- lib/wasi-web/rust-toolchain.toml | 2 +- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/.github/workflows/web.yaml b/.github/workflows/web.yaml index ec1813073b7..4b638b86a18 100644 --- a/.github/workflows/web.yaml +++ b/.github/workflows/web.yaml @@ -1,21 +1,30 @@ # Separate workflow for the wasmer-web crate. # -# Automatically cancel previous workflow runs when a new commit is pushed. -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - name: Wasmer Web + on: push: branches: - main pull_request: + +# Automatically cancel previous workflow runs when a new commit is pushed. +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +env: + CARGO_NET_GIT_FETCH_WITH_CLI: "true" + jobs: web: name: Build and Test (wasmer-web) runs-on: ubuntu-latest + defaults: + run: + + working-directory: lib/wasi-web steps: - name: Checkout code uses: actions/checkout@v2 @@ -23,23 +32,14 @@ jobs: - name: Install Rust uses: dtolnay/rust-toolchain@stable with: - toolchain: nightly + toolchain: nightly-2023-05-25 - name: Show Rust version - run: | - rustc --version + run: rustc --version --verbose - name: Check formatting shell: bash - run: | - cd lib/wasi-web - rustup override set nightly-2023-05-25 - rustup component add rustfmt - cargo fmt --check + run: cargo fmt --check - name: Check - env: - CARGO_NET_GIT_FETCH_WITH_CLI: "true" - run: | - cd lib/wasi-web - cargo check + run: cargo check --verbose --locked diff --git a/lib/wasi-web/rust-toolchain.toml b/lib/wasi-web/rust-toolchain.toml index 46b3698776c..306f42698a1 100644 --- a/lib/wasi-web/rust-toolchain.toml +++ b/lib/wasi-web/rust-toolchain.toml @@ -1,5 +1,5 @@ [toolchain] -channel = "nightly" +channel = "nightly-2023-05-25" components = [ "rustfmt", "rust-src", "clippy" ] targets = [ "wasm32-unknown-unknown" ] profile = "minimal" From 3f7c30f29aaa180ec73c15560590f009a362b20d Mon Sep 17 00:00:00 2001 From: Michael-F-Bryan Date: Wed, 28 Jun 2023 13:36:48 +0800 Subject: [PATCH 19/21] Add caching to wasmer web's CI --- .github/workflows/web.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/web.yaml b/.github/workflows/web.yaml index 4b638b86a18..3aa2c3c5d94 100644 --- a/.github/workflows/web.yaml +++ b/.github/workflows/web.yaml @@ -29,6 +29,9 @@ jobs: - name: Checkout code uses: actions/checkout@v2 + - name: Rust Cache + uses: Swatinem/rust-cache@v2 + - name: Install Rust uses: dtolnay/rust-toolchain@stable with: From 05923338e250ba4e80a60f2406896c67c3303df4 Mon Sep 17 00:00:00 2001 From: Michael-F-Bryan Date: Wed, 28 Jun 2023 16:57:48 +0800 Subject: [PATCH 20/21] Fixed the BuiltinPackageLoader tests --- .../runtime/package_loader/builtin_loader.rs | 38 +++++++++---------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/lib/wasix/src/runtime/package_loader/builtin_loader.rs b/lib/wasix/src/runtime/package_loader/builtin_loader.rs index d24e18b0e9b..3b9afef9f50 100644 --- a/lib/wasix/src/runtime/package_loader/builtin_loader.rs +++ b/lib/wasix/src/runtime/package_loader/builtin_loader.rs @@ -151,28 +151,26 @@ impl BuiltinPackageLoader { webc: &[u8], dist: &DistributionInfo, ) -> Result { + let cache = self + .cache + .as_ref() + .context("Caching to the filesystem isn't supported")?; + // First, save it to disk - if let Some(cache) = self.cache.as_ref() { - cache.save(webc, dist).await?; - - // Now try to load it again. The resulting container should use - // a memory-mapped file rather than an in-memory buffer. - match cache.lookup(&dist.webc_sha256).await? { - Some(container) => Ok(container), - None => { - // Something really weird has occurred and we can't see the - // saved file. Just error out and let the fallback code do its - // thing. - Err(Error::msg("Unable to load the downloaded memory from disk")) - } + cache.save(webc, dist).await?; + + // Now try to load it again. The resulting container should use + // a memory-mapped file rather than an in-memory buffer. + match cache.lookup(&dist.webc_sha256).await? { + Some(container) => { + self.in_memory.save(&container, dist.webc_sha256); + Ok(container) } - } else { - match Container::from_bytes(webc.to_vec()) { - Ok(container) => { - self.in_memory.save(&container, dist.webc_sha256); - Ok(container) - } - Err(e) => Err(Error::new(e).context("Unable to load container")), + None => { + // Something really weird has occurred and we can't see the + // saved file. Just error out and let the fallback code do its + // thing. + Err(Error::msg("Unable to load the downloaded memory from disk")) } } } From 56c1575013b4812cde97370bff8e6f738266376d Mon Sep 17 00:00:00 2001 From: Michael-F-Bryan Date: Wed, 28 Jun 2023 17:07:26 +0800 Subject: [PATCH 21/21] Not having a filesystem cache shouldn't be an error --- .../runtime/package_loader/builtin_loader.rs | 100 +++++++++--------- 1 file changed, 48 insertions(+), 52 deletions(-) diff --git a/lib/wasix/src/runtime/package_loader/builtin_loader.rs b/lib/wasix/src/runtime/package_loader/builtin_loader.rs index 3b9afef9f50..c559c030050 100644 --- a/lib/wasix/src/runtime/package_loader/builtin_loader.rs +++ b/lib/wasix/src/runtime/package_loader/builtin_loader.rs @@ -144,36 +144,6 @@ impl BuiltinPackageLoader { Ok(body.into()) } - - #[tracing::instrument(level = "debug", skip_all)] - async fn save_and_load_as_mmapped( - &self, - webc: &[u8], - dist: &DistributionInfo, - ) -> Result { - let cache = self - .cache - .as_ref() - .context("Caching to the filesystem isn't supported")?; - - // First, save it to disk - cache.save(webc, dist).await?; - - // Now try to load it again. The resulting container should use - // a memory-mapped file rather than an in-memory buffer. - match cache.lookup(&dist.webc_sha256).await? { - Some(container) => { - self.in_memory.save(&container, dist.webc_sha256); - Ok(container) - } - None => { - // Something really weird has occurred and we can't see the - // saved file. Just error out and let the fallback code do its - // thing. - Err(Error::msg("Unable to load the downloaded memory from disk")) - } - } - } } #[async_trait::async_trait] @@ -201,30 +171,34 @@ impl PackageLoader for BuiltinPackageLoader { // We want to cache the container we downloaded, but we want to do it // in a smart way to keep memory usage down. - match self.save_and_load_as_mmapped(&bytes, &summary.dist).await { - Ok(container) => { - tracing::debug!("Cached to disk"); - // The happy path - we've saved to both caches and loaded the - // container from disk (hopefully using mmap) so we're done. - return Ok(container); - } - Err(e) => { - tracing::warn!( - error=&*e, - pkg.name=%summary.pkg.name, - pkg.version=%summary.pkg.version, - pkg.hash=%summary.dist.webc_sha256, - pkg.url=%summary.dist.webc, - "Unable to save the downloaded package to disk", - ); - // The sad path - looks like we'll need to keep the whole thing - // in memory. - let container = Container::from_bytes(bytes)?; - // We still want to cache it, of course - self.in_memory.save(&container, summary.dist.webc_sha256); - Ok(container) + if let Some(cache) = &self.cache { + match cache.save_and_load_as_mmapped(&bytes, &summary.dist).await { + Ok(container) => { + tracing::debug!("Cached to disk"); + self.in_memory.save(&container, summary.dist.webc_sha256); + // The happy path - we've saved to both caches and loaded the + // container from disk (hopefully using mmap) so we're done. + return Ok(container); + } + Err(e) => { + tracing::warn!( + error=&*e, + pkg.name=%summary.pkg.name, + pkg.version=%summary.pkg.version, + pkg.hash=%summary.dist.webc_sha256, + pkg.url=%summary.dist.webc, + "Unable to save the downloaded package to disk", + ); + } } } + + // The sad path - looks like we don't have a filesystem cache so we'll + // need to keep the whole thing in memory. + let container = Container::from_bytes(bytes)?; + // We still want to cache it in memory, of course + self.in_memory.save(&container, summary.dist.webc_sha256); + Ok(container) } async fn load_package_tree( @@ -306,6 +280,28 @@ impl FileSystemCache { Ok(()) } + #[tracing::instrument(level = "debug", skip_all)] + async fn save_and_load_as_mmapped( + &self, + webc: &[u8], + dist: &DistributionInfo, + ) -> Result { + // First, save it to disk + self.save(webc, dist).await?; + + // Now try to load it again. The resulting container should use + // a memory-mapped file rather than an in-memory buffer. + match self.lookup(&dist.webc_sha256).await? { + Some(container) => Ok(container), + None => { + // Something really weird has occurred and we can't see the + // saved file. Just error out and let the fallback code do its + // thing. + Err(Error::msg("Unable to load the downloaded memory from disk")) + } + } + } + fn path(&self, hash: &WebcHash) -> PathBuf { let hash = hash.as_bytes(); let mut filename = String::with_capacity(hash.len() * 2);