Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added some fixes for the browser version to work again #3635

Merged
merged 10 commits into from
Mar 2, 2023
10 changes: 7 additions & 3 deletions lib/api/src/js/externals/global.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,9 +130,13 @@ impl Global {
/// ```
pub fn get(&self, store: &impl AsStoreRef) -> Value {
unsafe {
let raw = self.handle.global.value().as_f64().unwrap();
let ty = self.handle.ty;
Value::from_raw(store, ty.ty, raw)
match self.handle.global.value().as_f64() {
Some(raw) => {
let ty = self.handle.ty;
Value::from_raw(store, ty.ty, raw)
}
None => Value::null(),
}
}
}

Expand Down
18 changes: 15 additions & 3 deletions lib/api/src/js/store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -309,14 +309,26 @@ mod objects {
self.globals.iter()
}

/// Return an vector of all globals and converted to u128
pub fn as_u128_globals(&self) -> Vec<u128> {
theduke marked this conversation as resolved.
Show resolved Hide resolved
self.iter_globals()
.map(|v| v.global.value().as_f64().unwrap() as u128)
.collect()
}

/// Set a global, at index idx. Will panic if idx is out of range
/// Safety: the caller should check taht the raw value is compatible
/// with destination VMGlobal type
pub fn set_global_unchecked(&self, idx: usize, val: u128) {
pub fn set_global_unchecked(&self, idx: usize, new_val: u128) {
assert!(idx < self.globals.len());

let value = JsValue::from(val);
self.globals[idx].global.set_value(&value);
let g = &self.globals[idx].global;
let cur_val = g.value().as_f64().unwrap();
let new_val = new_val as f64;
if cur_val != new_val {
let new_value = JsValue::from(new_val);
g.set_value(&new_value);
}
}
}

Expand Down
6 changes: 6 additions & 0 deletions lib/api/src/js/vm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,12 @@ impl VMMemory {
}
}

impl From<VMMemory> for JsValue {
fn from(value: VMMemory) -> Self {
JsValue::from(value.memory)
}
}

#[derive(Clone, Debug, PartialEq)]
pub struct VMGlobal {
pub(crate) global: Global,
Expand Down
7 changes: 7 additions & 0 deletions lib/vm/src/store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,13 @@ impl StoreObjects {
self.globals.iter()
}

/// Return an vector of all globals and converted to u128
pub fn as_u128_globals(&self) -> Vec<u128> {
theduke marked this conversation as resolved.
Show resolved Hide resolved
self.iter_globals()
.map(|v| unsafe { v.vmglobal().as_ref().val.u128 })
.collect()
}

/// Set a global, at index idx. Will panic if idx is out of range
/// Safety: the caller should check taht the raw value is compatible
/// with destination VMGlobal type
Expand Down
65 changes: 18 additions & 47 deletions lib/wasi/src/bin_factory/exec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,7 @@ pub fn spawn_exec(
#[cfg(not(feature = "sys"))]
let compiler = "generic";

#[cfg(feature = "sys")]
let module = compiled_modules.get_compiled_module(&store, binary.hash().as_str(), compiler);
#[cfg(not(feature = "sys"))]
let module = compiled_modules.get_compiled_module(binary.hash().as_str(), compiler);

let module = match (module, binary.entry.as_ref()) {
(Some(a), _) => a,
(None, Some(entry)) => {
Expand Down Expand Up @@ -104,36 +100,25 @@ pub fn spawn_exec_module(
let tasks_outer = tasks.clone();

let task = {
let spawn_type = memory_spawn;
let mut store = store;

move || {
move |mut store, module, memory| {
// Create the WasiFunctionEnv
let mut wasi_env = env;
wasi_env.runtime = runtime;
let memory = match wasi_env.tasks().build_memory(spawn_type) {
Ok(m) => m,
Err(err) => {
error!("wasi[{}]::wasm could not build memory error ({})", pid, err);
wasi_env.cleanup(Some(Errno::Noexec as ExitCode));
return;
}
};

let thread = WasiThreadRunGuard::new(wasi_env.thread.clone());

let mut wasi_env = WasiFunctionEnv::new(&mut store, wasi_env);

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

let instance = match Instance::new(&mut store, &module, &import_object) {
Ok(a) => a,
Err(err) => {
Expand All @@ -148,7 +133,9 @@ pub fn spawn_exec_module(
init(&instance, &store).unwrap();

// Initialize the WASI environment
if let Err(err) = wasi_env.initialize(&mut store, instance.clone()) {
if let Err(err) =
wasi_env.initialize_with_memory(&mut store, instance.clone(), imported_memory)
{
error!("wasi[{}]::wasi initialize error ({})", pid, err);
wasi_env
.data(&store)
Expand Down Expand Up @@ -199,28 +186,12 @@ pub fn spawn_exec_module(
}
};

// TODO: handle this better - required because of Module not being Send.
#[cfg(feature = "js")]
let task = {
struct UnsafeWrapper {
inner: Box<dyn FnOnce() + 'static>,
}

unsafe impl Send for UnsafeWrapper {}

let inner = UnsafeWrapper {
inner: Box::new(task),
};

move || {
(inner.inner)();
}
};

tasks_outer.task_wasm(Box::new(task)).map_err(|err| {
error!("wasi[{}]::failed to launch module - {}", pid, err);
VirtualBusError::UnknownError
})?
tasks_outer
.task_wasm(Box::new(task), store, module, memory_spawn)
.map_err(|err| {
error!("wasi[{}]::failed to launch module - {}", pid, err);
VirtualBusError::UnknownError
})?
};

Ok(join_handle)
Expand Down
44 changes: 22 additions & 22 deletions lib/wasi/src/bin_factory/module_cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ impl ModuleCache {
pub fn get_compiled_module(
&self,
#[cfg(feature = "sys")] engine: &impl wasmer::AsEngineRef,
#[cfg(feature = "js")] engine: &impl wasmer::AsStoreRef,
theduke marked this conversation as resolved.
Show resolved Hide resolved
data_hash: &str,
compiler: &str,
) -> Option<Module> {
Expand Down Expand Up @@ -215,34 +216,33 @@ impl ModuleCache {
}
}

#[cfg(feature = "sys")]
{
// slow path
let path = std::path::Path::new(self.cache_compile_dir.as_str())
.join(format!("{}.bin", key).as_str());
if let Ok(data) = std::fs::read(path) {
let mut decoder = weezl::decode::Decoder::new(weezl::BitOrder::Msb, 8);
if let Ok(data) = decoder.decode(&data[..]) {
let module_bytes = bytes::Bytes::from(data);

// Load the module
let module = unsafe { Module::deserialize(engine, &module_bytes[..]).unwrap() };

if let Some(cache) = &self.cached_modules {
let mut cache = cache.write().unwrap();
cache.insert(key.clone(), module.clone());
}
// slow path
let path = std::path::Path::new(self.cache_compile_dir.as_str())
.join(format!("{}.bin", key).as_str());
if let Ok(data) = std::fs::read(path.as_path()) {
tracing::trace!("bin file found: {:?} [len={}]", path.as_path(), data.len());
let mut decoder = weezl::decode::Decoder::new(weezl::BitOrder::Msb, 8);
if let Ok(data) = decoder.decode(&data[..]) {
let module_bytes = bytes::Bytes::from(data);

THREAD_LOCAL_CACHED_MODULES.with(|cache| {
let mut cache = cache.borrow_mut();
cache.insert(key.clone(), module.clone());
});
return Some(module);
// Load the module
let module = unsafe { Module::deserialize(engine, &module_bytes[..]).unwrap() };

if let Some(cache) = &self.cached_modules {
let mut cache = cache.write().unwrap();
cache.insert(key.clone(), module.clone());
}

THREAD_LOCAL_CACHED_MODULES.with(|cache| {
let mut cache = cache.borrow_mut();
cache.insert(key.clone(), module.clone());
});
return Some(module);
}
}

// Not found
tracing::trace!("bin file not found: {:?}", path);
None
}

Expand Down
23 changes: 16 additions & 7 deletions lib/wasi/src/os/console/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -223,13 +223,22 @@ impl Console {
let wasi_process = env.process.clone();

// TODO: fetching dependencies should be moved to the builder!
// if let Err(err) = env.uses(self.uses.clone()) {
// tasks.block_on(async {
// let _ = self.runtime.stderr(format!("{}\r\n", err).as_bytes()).await;
// });
// tracing::debug!("failed to load used dependency - {}", err);
// return Err(crate::vbus::VirtualBusError::BadRequest);
// }
// TODO: the Console only makes sense in the context of SSH and the terminal.
// We should make this just take a WasiBuilder and the console related configs
// and not add so much custom logic in here.
if let Err(err) = env.uses(self.uses.clone()) {
syrusakbary marked this conversation as resolved.
Show resolved Hide resolved
syrusakbary marked this conversation as resolved.
Show resolved Hide resolved
let mut stderr = self.stderr.clone();
tasks.block_on(async {
wasmer_vfs::AsyncWriteExt::write_all(
&mut stderr,
format!("{}\r\n", err).as_bytes(),
)
.await
.ok();
});
tracing::debug!("failed to load used dependency - {}", err);
return Err(VirtualBusError::BadRequest);
}

// Build the config
// Run the binary
Expand Down
2 changes: 1 addition & 1 deletion lib/wasi/src/os/task/process.rs
Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,7 @@ impl WasiProcess {
children.clone()
};
if children.is_empty() {
return Err(Errno::Child);
return Ok(None);
}

let mut waits = Vec::new();
Expand Down
10 changes: 8 additions & 2 deletions lib/wasi/src/runtime/task_manager/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use std::{pin::Pin, time::Duration};

use ::tokio::runtime::Handle;
use futures::Future;
use wasmer::MemoryType;
use wasmer::{MemoryType, Module, Store};

#[cfg(feature = "js")]
use wasmer::VMMemory;
Expand Down Expand Up @@ -67,7 +67,13 @@ pub trait VirtualTaskManager: std::fmt::Debug + Send + Sync + 'static {
/// Starts an asynchronous task will will run on a dedicated thread
/// pulled from the worker pool that has a stateful thread local variable
/// It is ok for this task to block execution and any async futures within its scope
fn task_wasm(&self, task: Box<dyn FnOnce() + Send + 'static>) -> Result<(), WasiThreadError>;
fn task_wasm(
theduke marked this conversation as resolved.
Show resolved Hide resolved
&self,
task: Box<dyn FnOnce(Store, Module, Option<VMMemory>) + Send + 'static>,
store: Store,
module: Module,
spawn_type: SpawnType,
) -> Result<(), WasiThreadError>;

/// Starts an asynchronous task will will run on a dedicated thread
/// pulled from the worker pool. It is ok for this task to block execution
Expand Down
16 changes: 13 additions & 3 deletions lib/wasi/src/runtime/task_manager/tokio.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ use std::{pin::Pin, time::Duration};

use futures::Future;
use tokio::runtime::Handle;
use wasmer::vm::{VMMemory, VMSharedMemory};
use wasmer::{
vm::{VMMemory, VMSharedMemory},
Module, Store,
};

use crate::os::task::thread::WasiThreadError;

Expand Down Expand Up @@ -120,10 +123,17 @@ impl VirtualTaskManager for TokioTaskManager {
}

/// See [`VirtualTaskManager::enter`].
fn task_wasm(&self, task: Box<dyn FnOnce() + Send + 'static>) -> Result<(), WasiThreadError> {
fn task_wasm(
&self,
task: Box<dyn FnOnce(Store, Module, Option<VMMemory>) + Send + 'static>,
store: Store,
module: Module,
spawn_type: SpawnType,
) -> Result<(), WasiThreadError> {
let memory = self.build_memory(spawn_type)?;
self.0.spawn_blocking(move || {
// Invoke the callback
task();
task(store, module, memory);
});
Ok(())
}
Expand Down
34 changes: 20 additions & 14 deletions lib/wasi/src/state/env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -447,13 +447,14 @@ impl WasiEnv {
// Let's instantiate the module with the imports.
let (mut import_object, instance_init_callback) =
import_object_for_all_wasi_versions(&module, &mut store, &func_env.env);
if let Some(memory) = memory {
import_object.define(
"env",
"memory",
Memory::new_from_existing(&mut store, memory),
);
}

let imported_memory = if let Some(memory) = memory {
let imported_memory = Memory::new_from_existing(&mut store, memory);
import_object.define("env", "memory", imported_memory.clone());
Some(imported_memory)
} else {
None
};

// Construct the instance.
let instance = match Instance::new(&mut store, &module, &import_object) {
Expand All @@ -471,7 +472,9 @@ impl WasiEnv {
instance_init_callback(&instance, &store).unwrap();

// Initialize the WASI environment
if let Err(err) = func_env.initialize(&mut store, instance.clone()) {
if let Err(err) =
func_env.initialize_with_memory(&mut store, instance.clone(), imported_memory)
{
tracing::error!("wasi[{}]::wasi initialize error ({})", pid, err);
func_env
.data(&store)
Expand Down Expand Up @@ -895,13 +898,16 @@ impl WasiEnv {
let (tx, rx) = std::sync::mpsc::channel();
self.tasks()
.task_dedicated(Box::new(move || {
let tasks_inner = tasks.clone();
tasks.runtime().block_on(async {
let fut = tokio::time::timeout(CLEANUP_TIMEOUT, state.fs.close_all());

if let Err(err) = fut.await {
tracing::warn!(
"WasiEnv::cleanup has timed out after {CLEANUP_TIMEOUT:?}: {err}"
);
let timeout = tasks_inner.sleep_now(CLEANUP_TIMEOUT);
tokio::select! {
_ = timeout => {
tracing::warn!(
"WasiEnv::cleanup has timed out after {CLEANUP_TIMEOUT:?}"
);
},
_ = state.fs.close_all() => { }
}
});
tx.send(()).ok();
Expand Down
Loading