Skip to content

Commit

Permalink
add support for spawning local wasm files (#4819)
Browse files Browse the repository at this point in the history
  • Loading branch information
maminrayej authored Jun 7, 2024
2 parents 76dada0 + 5e4df24 commit 80435ac
Show file tree
Hide file tree
Showing 7 changed files with 141 additions and 61 deletions.
18 changes: 12 additions & 6 deletions lib/wasix/src/bin_factory/exec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,16 +29,22 @@ pub async fn spawn_exec(
env: WasiEnv,
runtime: &Arc<dyn Runtime + Send + Sync + 'static>,
) -> Result<TaskJoinHandle, SpawnError> {
// Load the WASM
spawn_union_fs(&env, &binary).await?;

let wasm = spawn_load_wasm(&env, &binary, name).await?;

// Load the module
let module = spawn_load_module(&env, name, wasm, runtime).await?;
spawn_exec_wasm(wasm, name, env, runtime).await
}

// Spawn union the file system
spawn_union_fs(&env, &binary).await?;
#[tracing::instrument(level = "trace", skip_all, fields(%name))]
pub async fn spawn_exec_wasm(
wasm: &[u8],
name: &str,
env: WasiEnv,
runtime: &Arc<dyn Runtime + Send + Sync + 'static>,
) -> Result<TaskJoinHandle, SpawnError> {
let module = spawn_load_module(&env, name, wasm, runtime).await?;

// Now run the module
spawn_exec_module(module, env, runtime)
}

Expand Down
139 changes: 87 additions & 52 deletions lib/wasix/src/bin_factory/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use std::{
};

use anyhow::Context;
use exec::spawn_exec_wasm;
use virtual_fs::{AsyncReadExt, FileSystem};
use wasmer::FunctionEnvMut;
use wasmer_wasix_types::wasi::Errno;
Expand Down Expand Up @@ -52,54 +53,18 @@ impl BinFactory {
cache.insert(name.to_string(), Some(binary));
}

// TODO: remove allow once BinFactory is refactored
// currently fine because a BinFactory is only used by a single process tree
#[allow(clippy::await_holding_lock)]
pub async fn get_binary(
&self,
name: &str,
fs: Option<&dyn FileSystem>,
) -> Option<BinaryPackage> {
let name = name.to_string();

// Fast path
{
let cache = self.local.read().unwrap();
if let Some(data) = cache.get(&name) {
return data.clone();
}
}

// Slow path
let mut cache = self.local.write().unwrap();

// Check the cache
if let Some(data) = cache.get(&name) {
return data.clone();
}

// Check the filesystem for the file
if name.starts_with('/') {
if let Some(fs) = fs {
match load_package_from_filesystem(fs, name.as_ref(), self.runtime()).await {
Ok(pkg) => {
cache.insert(name, Some(pkg.clone()));
return Some(pkg);
}
Err(e) => {
tracing::warn!(
path = name,
error = &*e,
"Unable to load the package from disk"
);
}
}
}
}

// NAK
cache.insert(name, None);
None
self.get_executable(name, fs)
.await
.and_then(|executable| match executable {
Executable::Wasm(_) => None,
Executable::BinaryPackage(pkg) => Some(pkg),
})
}

pub fn spawn<'a>(
Expand All @@ -111,18 +76,25 @@ impl BinFactory {
Box::pin(async move {
// Find the binary (or die trying) and make the spawn type
let res = self
.get_binary(name.as_str(), Some(env.fs_root()))
.get_executable(name.as_str(), Some(env.fs_root()))
.await
.ok_or_else(|| SpawnError::BinaryNotFound {
binary: name.clone(),
});
if res.is_err() {
env.on_exit(Some(Errno::Noent.into())).await;
}
let binary = res?;
let executable = res?;

// Execute
spawn_exec(binary, name.as_str(), store, env, &self.runtime).await
match executable {
Executable::Wasm(bytes) => {
spawn_exec_wasm(&bytes, name.as_str(), env, &self.runtime).await
}
Executable::BinaryPackage(pkg) => {
spawn_exec(pkg, name.as_str(), store, env, &self.runtime).await
}
}
})
}

Expand All @@ -145,13 +117,71 @@ impl BinFactory {
}
Err(SpawnError::BinaryNotFound { binary: name })
}

// TODO: remove allow once BinFactory is refactored
// currently fine because a BinFactory is only used by a single process tree
#[allow(clippy::await_holding_lock)]
pub async fn get_executable(
&self,
name: &str,
fs: Option<&dyn FileSystem>,
) -> Option<Executable> {
let name = name.to_string();

// Fast path
{
let cache = self.local.read().unwrap();
if let Some(data) = cache.get(&name) {
data.clone().map(Executable::BinaryPackage);
}
}

// Slow path
let mut cache = self.local.write().unwrap();

// Check the cache
if let Some(data) = cache.get(&name) {
return data.clone().map(Executable::BinaryPackage);
}

// Check the filesystem for the file
if name.starts_with('/') {
if let Some(fs) = fs {
match load_executable_from_filesystem(fs, name.as_ref(), self.runtime()).await {
Ok(executable) => {
if let Executable::BinaryPackage(pkg) = &executable {
cache.insert(name, Some(pkg.clone()));
}

return Some(executable);
}
Err(e) => {
tracing::warn!(
path = name,
error = &*e,
"Unable to load the package from disk"
);
}
}
}
}

// NAK
cache.insert(name, None);
None
}
}

pub enum Executable {
Wasm(bytes::Bytes),
BinaryPackage(BinaryPackage),
}

async fn load_package_from_filesystem(
async fn load_executable_from_filesystem(
fs: &dyn FileSystem,
path: &Path,
rt: &(dyn Runtime + Send + Sync),
) -> Result<BinaryPackage, anyhow::Error> {
) -> Result<Executable, anyhow::Error> {
let mut f = fs
.new_open_options()
.read(true)
Expand All @@ -161,10 +191,15 @@ async fn load_package_from_filesystem(
let mut data = Vec::with_capacity(f.size() as usize);
f.read_to_end(&mut data).await.context("Read failed")?;

let container = Container::from_bytes(data).context("Unable to parse the WEBC file")?;
let pkg = BinaryPackage::from_webc(&container, rt)
.await
.context("Unable to load the package")?;
let bytes: bytes::Bytes = data.into();

Ok(pkg)
if let Ok(container) = Container::from_bytes(bytes.clone()) {
let pkg = BinaryPackage::from_webc(&container, rt)
.await
.context("Unable to load the package")?;

Ok(Executable::BinaryPackage(pkg))
} else {
Ok(Executable::Wasm(bytes))
}
}
1 change: 0 additions & 1 deletion tests/wasix/cwd-to-home/expected

This file was deleted.

2 changes: 1 addition & 1 deletion tests/wasix/cwd-to-home/run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@

$WASMER -q run main.wasm --dir=. > output

diff -u output expected 1>/dev/null
printf "0" | diff -u output - 1>/dev/null
34 changes: 34 additions & 0 deletions tests/wasix/execv/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>

int main(int argc, char *argv[])
{
if (argc > 1 && argv[1] != NULL)
{
return 0;
}

pid_t pid = fork();
if (pid == -1)
{
exit(EXIT_FAILURE);
}
else if (pid == 0)
{
char *newargv[] = {argv[0], "child", NULL};

execv("/code/main.wasm", newargv);

exit(EXIT_FAILURE);
}
else
{
int status;
waitpid(pid, &status, 0);
printf("%d", status);
}

return 0;
}
5 changes: 5 additions & 0 deletions tests/wasix/execv/run.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/bin/bash

$WASMER -q run main.wasm --mapdir=/code:. > output

printf "0" | diff -u output - 1>/dev/null
3 changes: 2 additions & 1 deletion tests/wasix/test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ while read dir; do

cmd="cd $dir; \
$CC $CFLAGS $LDFLAGS -o main.wasm main.c; \
wasm-opt -O4 --asyncify -g main.wasm -o main.wasm; \
./run.sh"

if bash -c "$cmd"; then
Expand All @@ -85,6 +86,6 @@ while read dir; do
printf "\rTesting $dir ❌\n"
status=1
fi
done < <(find . -mindepth 1 -maxdepth 1 -type d)
done < <(find . -mindepth 1 -maxdepth 1 -type d | sort)

exit $status

0 comments on commit 80435ac

Please sign in to comment.