Actor-served files: /actor/<id>/fs/
ci / check (push) Successful in 10s

File operations under /actor/<id>/fs/ become messages from `fs` to <id>
(fs_read, fs_write, fs_mkdir, fs_remove, fs_rename) with a 10 s timeout.
vfs.rs wraps the wasi:filesystem implementation of wasmtime-wasi.
This commit is contained in:
Sausage Agent
2026-10-16 01:25:11 +00:00
parent e1409b93f2
commit 2ff30ecf70
8 changed files with 866 additions and 4 deletions
+32 -1
View File
@@ -20,6 +20,7 @@ use wasmtime::component::Component;
use crate::candidate;
use crate::runtime::{InstanceConfig, Mount, Runtime};
use crate::supervisor::{Control, Reply, Supervisor};
use crate::vfs;
use crate::world::{self, ActorMeta, NATIVE, VersionMeta, World};
pub const MAILBOX_SIZE: usize = 1000;
@@ -60,12 +61,15 @@ pub struct Broker {
seq: AtomicU64,
/// Set by the `exit` request: the host process should stop.
exit: tokio::sync::Notify,
/// Requests of the host from `fs` (see `vfs.rs`) that wait for a reply, by message ID.
asks: Mutex<HashMap<String, oneshot::Sender<Value>>>,
me: std::sync::Weak<Broker>,
}
impl Broker {
pub fn new(world: World, runtime: Arc<Runtime>) -> Result<Arc<Self>> {
let metas = world.actor_ids()?.into_iter().map(|id| (id.clone(), world.read_meta(&id))).collect();
Ok(Arc::new(Self {
Ok(Arc::new_cyclic(|me| Self {
world,
runtime,
mailboxes: Mutex::new(HashMap::new()),
@@ -74,9 +78,27 @@ impl Broker {
op_locks: Mutex::new(HashMap::new()),
seq: AtomicU64::new(1),
exit: tokio::sync::Notify::new(),
asks: Mutex::new(HashMap::new()),
me: me.clone(),
}))
}
/// Send `body` from `fs` to `to` now. The future resolves to the reply
/// body, or `None` after `vfs::ASK_TIMEOUT`.
pub fn ask(self: &Arc<Self>, to: &str, body: Value) -> impl Future<Output = Option<Value>> + Send + 'static {
let own = format!("q{}", self.seq.fetch_add(1, Ordering::Relaxed));
let id = format!("{}/{own}", vfs::ID);
let (tx, rx) = oneshot::channel();
self.asks.lock().unwrap().insert(id.clone(), tx);
self.send(vfs::ID, Some(to), Some(&own), None, body);
let this = self.clone();
async move {
let reply = tokio::time::timeout(vfs::ASK_TIMEOUT, rx).await;
this.asks.lock().unwrap().remove(&id);
reply.ok()?.ok()
}
}
/// Wait until an actor sends `{"type": "exit"}` to `broker`. The `sausage`
/// binary then stops all actors and exits, so that a wrapper such as
/// `nix run .#loop` can start a fresh (updated) host.
@@ -155,6 +177,13 @@ impl Broker {
}
fn deliver(&self, env: Envelope) {
if env.to == vfs::ID {
let waiting = env.reply_to.as_ref().and_then(|r| self.asks.lock().unwrap().remove(r));
if let Some(tx) = waiting {
let _ = tx.send(env.body);
}
return;
}
let tx = self.mailboxes.lock().unwrap().get(&env.to).cloned();
let error = match tx {
None => format!("no actor with ID {}", env.to),
@@ -201,6 +230,8 @@ impl Broker {
network: scratch.is_none(),
stderr_log: own.join("stderr.log"),
env,
actor: id.to_string(),
broker: self.me.clone(),
}
}