Skip to content

Commit

Permalink
wasi: Partial unification of instance spawning
Browse files Browse the repository at this point in the history
Introduces a new WasiEnvInit type that should hold all information
required for initializing an instance.

An instance can then be created with WasiEnv::instantiate().
This method takes care of all the required setup steps to properly run a
wasi(X) module.
  • Loading branch information
theduke committed Feb 8, 2023
1 parent a112bea commit 8b0d1fc
Show file tree
Hide file tree
Showing 8 changed files with 336 additions and 195 deletions.
24 changes: 11 additions & 13 deletions lib/cli/src/commands/run/wasi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,14 +115,14 @@ impl Wasi {

let runtime = Arc::new(PluggableRuntimeImplementation::default());

let wasi_state_builder = WasiState::builder(program_name)
let builder = WasiState::builder(program_name)
.args(args)
.envs(self.env_vars.clone())
.uses(self.uses.clone())
.runtime(&runtime)
.runtime(runtime.clone())
.map_commands(map_commands.clone());

let wasi_state_builder = if wasmer_wasi::is_wasix_module(module) {
let mut builder = if wasmer_wasi::is_wasix_module(module) {
// If we preopen anything from the host then shallow copy it over
let root_fs = RootFileSystemBuilder::new()
.with_tty(Box::new(TtyFile::new(
Expand All @@ -143,18 +143,23 @@ impl Wasi {
}

// Open the root of the new filesystem
wasi_state_builder
builder
.sandbox_fs(root_fs)
.preopen_dir(Path::new("/"))
.unwrap()
.map_dir(".", "/")?
} else {
wasi_state_builder
builder
.fs(default_fs_backing())
.preopen_dirs(self.pre_opened_directories.clone())?
.map_dirs(self.mapped_dirs.clone())?
};

if self.http_client {
let caps = wasmer_wasi::http::HttpClientCapabilityV1::new_allow_all();
builder.capabilities_mut().http_client = caps;
}

#[cfg(feature = "experimental-io-devices")]
{
if self.enable_experimental_io_devices {
Expand All @@ -163,14 +168,7 @@ impl Wasi {
}
}

let mut wasi_env = wasi_state_builder.finalize(store)?;

if self.http_client {
let caps = wasmer_wasi::http::HttpClientCapabilityV1::new_allow_all();
wasi_env.data_mut(store).capabilities.http_client = caps;
}

let instance = wasmer_wasi::build_wasi_instance(module, &mut wasi_env, store)?;
let (instance, wasi_env) = builder.instantiate(module.clone(), store)?;
Ok((wasi_env.env, instance))
}

Expand Down
12 changes: 12 additions & 0 deletions lib/compiler/src/engine/trap/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,18 @@ impl RuntimeError {
}
}

/// Attempts to downcast the `RuntimeError` to a concrete type.
pub fn downcast_ref<T: Error + 'static>(&self) -> Option<&T> {
match self.inner.as_ref() {
// We only try to downcast user errors
RuntimeErrorInner {
source: RuntimeErrorSource::User(err),
..
} if err.is::<T>() => err.downcast_ref::<T>(),
_ => None,
}
}

/// Returns trap code, if it's a Trap
pub fn to_trap(self) -> Option<TrapCode> {
if let RuntimeErrorSource::Trap(trap_code) = self.inner.source {
Expand Down
25 changes: 0 additions & 25 deletions lib/wasi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -651,31 +651,6 @@ fn import_object_for_all_wasi_versions(
(imports, init)
}

pub fn build_wasi_instance(
module: &wasmer::Module,
env: &mut WasiFunctionEnv,
store: &mut impl AsStoreMut,
) -> Result<wasmer::Instance, anyhow::Error> {
// Allowed due to JS warning.
#[allow(unused_mut)]
let (mut import_object, init) = import_object_for_all_wasi_versions(module, store, &env.env);

cfg_if::cfg_if! {
if #[cfg(feature = "sys")] {
import_object.import_shared_memory(module, store);
} else {
// Prevent warning.
let _ = module;
}
}

let instance = wasmer::Instance::new(store, module, &import_object)?;
init(&instance, &store)?;
env.initialize(store, instance.clone())?;

Ok(instance)
}

/// Combines a state generating function with the import list for legacy WASI
fn generate_import_object_snapshot0(
store: &mut impl AsStoreMut,
Expand Down
52 changes: 22 additions & 30 deletions lib/wasi/src/os/console/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,15 +159,6 @@ impl Console {
// Build a new store that will be passed to the thread
let store = self.runtime.new_store();

// Create the control plane, process and thread
let control_plane = WasiControlPlane::default();
let wasi_process = control_plane
.new_process()
.expect("creating processes on new control planes should always work");
let wasi_thread = wasi_process
.new_thread()
.expect("creating the main thread should always work");

// Create the state
let mut builder = WasiState::builder(prog);
if let Some(stdin) = self.stdin.take() {
Expand All @@ -192,31 +183,29 @@ impl Console {
.map_dir(".", "/")
.unwrap();

let state = builder
let env_init = builder
.stdout(Box::new(RuntimeStdout::new(self.runtime.clone())))
.stderr(Box::new(RuntimeStderr::new(self.runtime.clone())))
.build()
.unwrap();
.compiled_modules(self.compiled_modules.clone())
.runtime(self.runtime.clone())
.capabilities(self.capabilities.clone())
.build_init()
// TODO: propagate better error
.map_err(|e| VirtualBusError::InternalError)?;

// Create the environment
let mut env = WasiEnv::new(
Arc::new(state),
self.compiled_modules.clone(),
wasi_process.clone(),
wasi_thread,
self.runtime.clone(),
);
env.capabilities = self.capabilities.clone();
// TODO: no unwrap!
let env = WasiEnv::from_init(env_init).unwrap();

// TODO: this should not happen here...
// Display the welcome message
let tasks = env.tasks.clone();
let tasks = env.tasks().clone();
if !self.whitelabel && !self.no_welcome {
tasks.block_on(self.draw_welcome());
}

let binary = if let Some(binary) =
self.compiled_modules
.get_webc(webc, self.runtime.deref(), env.tasks.deref())
.get_webc(webc, self.runtime.deref(), tasks.deref())
{
binary
} else {
Expand All @@ -230,13 +219,16 @@ impl Console {
return Err(crate::vbus::VirtualBusError::NotFound);
};

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);
}
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);
// }

// Build the config
// Run the binary
Expand Down
21 changes: 14 additions & 7 deletions lib/wasi/src/runners/wasi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//! WebC container support for running WASI modules
use crate::runners::WapmContainer;
use crate::{WasiFunctionEnv, WasiState};
use crate::{WasiEnv, WasiEnvBuilder, WasiState};
use anyhow::Context;
use serde::{Deserialize, Serialize};
use std::error::Error as StdError;
Expand Down Expand Up @@ -68,14 +68,21 @@ impl crate::runners::Runner for WasiRunner {
let mut module = Module::new(&self.store, atom_bytes)?;
module.set_name(&atom_name);

let env = prepare_webc_env(
let builder = prepare_webc_env(
&mut self.store,
container.webc.clone(),
&atom_name,
&self.args,
)?;

exec_module(&mut self.store, &module, env)?;
let init = builder.build_init()?;

let (instance, env) = WasiEnv::instantiate(init, module, &mut self.store)?;

let _result = instance
.exports
.get_function("_start")?
.call(&mut self.store, &[])?;

Ok(())
}
Expand All @@ -87,7 +94,7 @@ fn prepare_webc_env(
webc: Arc<WebCMmap>,
command: &str,
args: &[String],
) -> Result<WasiFunctionEnv, anyhow::Error> {
) -> Result<WasiEnvBuilder, anyhow::Error> {
use webc::FsEntryType;

let package_name = webc.get_package_name();
Expand All @@ -107,12 +114,12 @@ fn prepare_webc_env(
.collect::<Vec<_>>();

let filesystem = Box::new(WebcFileSystem::init(webc, &package_name));
let mut wasi_env = WasiState::builder(command).fs(filesystem).args(args);
let mut builder = WasiState::builder(command).fs(filesystem).args(args);
for f_name in top_level_dirs.iter() {
wasi_env.add_preopen_build(|p| p.directory(f_name).read(true).write(true).create(true))?;
builder.add_preopen_build(|p| p.directory(f_name).read(true).write(true).create(true))?;
}

Ok(wasi_env.finalize(store)?)
Ok(builder)
}

pub(crate) fn exec_module(
Expand Down
Loading

0 comments on commit 8b0d1fc

Please sign in to comment.