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

Add support for spawning local wasm files #4819

Merged
merged 3 commits into from
Jun 7, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 13 additions & 3 deletions lib/wasix/src/bin_factory/exec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,25 @@ pub async fn spawn_exec(
env: WasiEnv,
runtime: &Arc<dyn Runtime + Send + Sync + 'static>,
) -> Result<TaskJoinHandle, SpawnError> {
// Spawn union the file system
spawn_union_fs(&env, &binary).await?;

// Load the WASM
let wasm = spawn_load_wasm(&env, &binary, name).await?;

spawn_exec_wasm(wasm, name, env, runtime).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> {
// Load the module
let module = spawn_load_module(&env, name, wasm, runtime).await?;

// Spawn union the file system
maminrayej marked this conversation as resolved.
Show resolved Hide resolved
spawn_union_fs(&env, &binary).await?;

// Now run the module
spawn_exec_module(module, env, runtime)
}
Expand Down
119 changes: 117 additions & 2 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,8 +53,6 @@ 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,
Expand Down Expand Up @@ -145,6 +144,90 @@ impl BinFactory {
}
Err(SpawnError::BinaryNotFound { binary: name })
}

pub fn spawn_executable<'a>(
&'a self,
name: String,
store: wasmer::Store,
env: WasiEnv,
) -> Pin<Box<dyn Future<Output = Result<TaskJoinHandle, SpawnError>> + 'a>> {
Box::pin(async move {
// Find the binary (or die trying) and make the spawn type
let res = self
.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 executable = res?;

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

// TODO: remove allow once BinFactory is refactored
maminrayej marked this conversation as resolved.
Show resolved Hide resolved
// 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
}
}

async fn load_package_from_filesystem(
Expand All @@ -168,3 +251,35 @@ async fn load_package_from_filesystem(

Ok(pkg)
}

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

async fn load_executable_from_filesystem(
fs: &dyn FileSystem,
path: &Path,
rt: &(dyn Runtime + Send + Sync),
) -> Result<Executable, anyhow::Error> {
let mut f = fs
.new_open_options()
.read(true)
.open(path)
.context("Unable to open the file")?;

let mut data = Vec::with_capacity(f.size() as usize);
f.read_to_end(&mut data).await.context("Read failed")?;

let bytes: bytes::Bytes = data.into();

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))
}
}
6 changes: 4 additions & 2 deletions lib/wasix/src/syscalls/wasix/proc_exec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,9 @@ pub fn proc_exec<M: MemorySize>(

let name_inner = name.clone();
__asyncify_light(ctx.data(), None, async {
let ret = bin_factory.spawn(name_inner, new_store, env).await;
let ret = bin_factory
.spawn_executable(name_inner, new_store, env)
.await;
match ret {
Ok(ret) => {
trace!(%child_pid, "spawned sub-process");
Expand Down Expand Up @@ -213,7 +215,7 @@ pub fn proc_exec<M: MemorySize>(
let env = builder.take().unwrap();

// Spawn a new process with this current execution environment
InlineWaker::block_on(bin_factory.spawn(name, new_store, env))
InlineWaker::block_on(bin_factory.spawn_executable(name, new_store, env))
}
};

Expand Down
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
Loading