1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
//! Per-thread pool of recyclable fusevm `VM`s — a Rust-only optimization
//! that stops zshrs from rebuilding a VM (and re-registering its entire
//! builtin table) on every function call, command substitution, pipeline
//! stage, and subshell.
//!
//! # Why
//!
//! Handing a compiled `Chunk` to a [`fusevm::VM`] used to mean
//! `VM::new(chunk)` + `register_builtins(&mut vm)` at each of ~15 execution
//! sites. `register_builtins` installs the ~hundreds of `fn`-pointer
//! handlers that make up the shell builtin table — identical for every VM,
//! so redoing it per run was pure waste (the #2 hot spot after option
//! lookups in a function-call profile). Worse, throwing the VM away each
//! call meant the tracing JIT never stayed warm, so hot numeric loops never
//! got compiled.
//!
//! [`fusevm::VM::reset`] clears execution state (stack, frames, globals, ip,
//! status) but PRESERVES the `builtin_table`, shell host, and JIT wiring. So
//! a VM built once can be recycled for any later chunk with only a `reset` —
//! no re-registration — and its JIT trace state accumulates across runs.
//!
//! # Usage
//!
//! [`acquire`] returns a [`PooledVm`] RAII guard that derefs to the VM. It
//! pops a recycled VM (already registered — just `reset`) or builds and
//! registers a fresh one. On drop the VM returns to the pool. Runs nest
//! (a function body spawns a command substitution, …), so multiple guards
//! can be live at once; the pool grows to the maximum nesting depth.
//! Thread-local because a fusevm `VM` is not `Sync`.
use std::cell::RefCell;
use std::ops::{Deref, DerefMut};
thread_local! {
/// Released, ready-to-reuse VMs for this thread. Each retains its
/// registered builtin table and host from first construction.
static POOL: RefCell<Vec<fusevm::VM>> = const { RefCell::new(Vec::new()) };
}
/// Cap on retained VMs per thread. Deep recursion can hold many guards at
/// once; we only bound what we *keep*, so a pathological one-off recursion
/// doesn't pin a large fleet forever. Excess returns are dropped.
const MAX_POOLED: usize = 64;
/// RAII handle to a pooled VM. Derefs to [`fusevm::VM`]; returns the VM to
/// the pool on drop.
pub struct PooledVm {
vm: Option<fusevm::VM>,
}
/// Acquire a call-ready VM for `chunk`. Recycles a pooled VM (builtins
/// already registered — just `reset`) or builds and registers a fresh one.
pub fn acquire(chunk: fusevm::Chunk) -> PooledVm {
let vm = match POOL.with(|p| p.borrow_mut().pop()) {
Some(mut recycled) => {
recycled.reset(chunk);
recycled
}
None => {
let mut fresh = fusevm::VM::new(chunk);
crate::fusevm_bridge::register_builtins(&mut fresh);
fresh
}
};
PooledVm { vm: Some(vm) }
}
impl Deref for PooledVm {
type Target = fusevm::VM;
#[inline]
fn deref(&self) -> &fusevm::VM {
// Present for the whole lifetime; only `take`n in Drop.
self.vm.as_ref().expect("PooledVm used after drop")
}
}
impl DerefMut for PooledVm {
#[inline]
fn deref_mut(&mut self) -> &mut fusevm::VM {
self.vm.as_mut().expect("PooledVm used after drop")
}
}
impl Drop for PooledVm {
fn drop(&mut self) {
if let Some(vm) = self.vm.take() {
POOL.with(|p| {
let mut pool = p.borrow_mut();
if pool.len() < MAX_POOLED {
pool.push(vm);
}
});
}
}
}