Crates continued to grow in number, but most are still quite small and feature specific. This refactor consolidates them.
63 lines
2.3 KiB
Rust
63 lines
2.3 KiB
Rust
use swactor::actor::{ActorInterface, Ctx};
|
|
use wasmtime::{Memory, Store, TypedFunc};
|
|
|
|
use crate::ByteMessage;
|
|
|
|
/// State accessible to host functions during guest execution.
|
|
#[derive(Default)]
|
|
pub(crate) struct HostState {
|
|
pub outbox: Vec<(swactor::actor::ActorAddress, Vec<u8>)>,
|
|
}
|
|
|
|
/// An actor whose logic is defined by a WebAssembly guest module.
|
|
///
|
|
/// Messages arrive as [`ByteMessage`], are copied into Wasm linear memory,
|
|
/// and processed by the guest's `handle` export. The guest can send messages
|
|
/// back via the `swactor.send` host import.
|
|
pub struct WasmActor {
|
|
pub(crate) store: Store<HostState>,
|
|
pub(crate) memory: Memory,
|
|
pub(crate) alloc: TypedFunc<i32, i32>,
|
|
pub(crate) handle: TypedFunc<(i32, i32), ()>,
|
|
}
|
|
|
|
impl ActorInterface for WasmActor {
|
|
type Incoming = ByteMessage;
|
|
type Response = ();
|
|
|
|
fn handle(&mut self, ctx: &Ctx, msg: ByteMessage) {
|
|
let bytes = &msg.0;
|
|
let len: i32 = match i32::try_from(bytes.len()) {
|
|
Ok(n) => n,
|
|
Err(_) => return, // message too large for i32 ABI
|
|
};
|
|
|
|
// 1. Allocate space in guest memory
|
|
let ptr = match self.alloc.call(&mut self.store, len) {
|
|
Ok(ptr) if ptr < 0 => return, // invalid pointer
|
|
Ok(0) if len > 0 => return, // OOM — drop message
|
|
Ok(ptr) => ptr,
|
|
Err(_) => return, // alloc trapped — drop message
|
|
};
|
|
|
|
// 2. Write message bytes into guest memory
|
|
let mem = self.memory.data_mut(&mut self.store);
|
|
let end = (ptr as usize).saturating_add(bytes.len());
|
|
if end > mem.len() {
|
|
return; // alloc returned OOB pointer — drop message
|
|
}
|
|
mem[ptr as usize..end].copy_from_slice(bytes);
|
|
|
|
// 3. Call guest handle
|
|
if self.handle.call(&mut self.store, (ptr, len)).is_err() {
|
|
self.store.data_mut().outbox.clear(); // discard sends from incomplete operation
|
|
return; // handle trapped — drop message, keep actor alive
|
|
}
|
|
|
|
// 4. Drain outbox → send via ctx
|
|
let outbox: Vec<_> = self.store.data_mut().outbox.drain(..).collect();
|
|
for (dest, payload) in outbox {
|
|
let _ = ctx.send(dest, ByteMessage(payload));
|
|
}
|
|
}
|
|
}
|