Skip to main content

uni_plugin_wasm/
buffer.rs

1//! `WasmIpcBuffer` — RAII handle for a buffer allocated inside a WASM
2//! plugin's linear memory.
3//!
4//! Specific to the Component Model loader's alloc/copy/free dance for
5//! Arrow IPC payloads — kept in this crate (rather than the shared
6//! `uni-plugin-wasm-rt`) because the free closure crosses the
7//! wasmtime linear-memory boundary.
8
9use std::sync::Arc;
10
11/// RAII handle to a buffer allocated inside a WASM plugin's linear memory.
12///
13/// Holds the `(ptr, len)` returned by the plugin's `alloc` export; on
14/// drop, invokes `free`. Used by the M6 cutover commits to ensure plugin
15/// memory is always reclaimed.
16pub struct WasmIpcBuffer {
17    /// Pointer in the plugin's linear memory.
18    pub ptr: u32,
19    /// Length in bytes.
20    pub len: u32,
21    /// Free closure invoked on drop. Wrapped in `Arc<dyn Fn>` so a
22    /// pool's instance can hand out buffers without lifetime infection.
23    free: Arc<dyn Fn(u32, u32) + Send + Sync>,
24}
25
26impl std::fmt::Debug for WasmIpcBuffer {
27    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
28        f.debug_struct("WasmIpcBuffer")
29            .field("ptr", &self.ptr)
30            .field("len", &self.len)
31            .finish_non_exhaustive()
32    }
33}
34
35impl WasmIpcBuffer {
36    /// Construct a buffer with the given free closure.
37    #[must_use]
38    pub fn new(ptr: u32, len: u32, free: Arc<dyn Fn(u32, u32) + Send + Sync>) -> Self {
39        Self { ptr, len, free }
40    }
41}
42
43impl Drop for WasmIpcBuffer {
44    fn drop(&mut self) {
45        (self.free)(self.ptr, self.len);
46    }
47}
48
49#[cfg(test)]
50mod tests {
51    use super::*;
52
53    #[test]
54    fn wasm_ipc_buffer_calls_free_on_drop() {
55        let counter = Arc::new(std::sync::atomic::AtomicU32::new(0));
56        let c2 = Arc::clone(&counter);
57        let free: Arc<dyn Fn(u32, u32) + Send + Sync> = Arc::new(move |_p, _l| {
58            c2.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
59        });
60        {
61            let _b = WasmIpcBuffer::new(0x1000, 64, free);
62        }
63        assert_eq!(counter.load(std::sync::atomic::Ordering::SeqCst), 1);
64    }
65}