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 all commits
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
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
maminrayej marked this conversation as resolved.
Show resolved Hide resolved
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
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
}
}

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
Loading