use std::cell::RefCell;
use ocas_core::arena::Arena;
const MAX_POOLED: usize = 4;
thread_local! {
#[allow(clippy::missing_const_for_thread_local)]
static ARENA_POOL: RefCell<Vec<Arena>> = RefCell::new(Vec::new());
}
pub struct WorkspaceArena {
arena: Option<Arena>,
_not_send: std::marker::PhantomData<*const ()>,
}
impl WorkspaceArena {
pub fn acquire() -> Self {
let arena = ARENA_POOL
.with(|pool| pool.borrow_mut().pop())
.unwrap_or_default();
Self {
arena: Some(arena),
_not_send: std::marker::PhantomData,
}
}
pub fn arena(&self) -> &Arena {
self.arena.as_ref().expect("arena present until drop")
}
pub fn reset(&mut self) {
self.arena
.as_ref()
.expect("arena present until drop")
.reset();
}
}
impl Drop for WorkspaceArena {
fn drop(&mut self) {
if let Some(arena) = self.arena.take() {
arena.reset();
ARENA_POOL.with(|pool| {
let mut pool = pool.borrow_mut();
if pool.len() < MAX_POOLED {
pool.push(arena);
}
});
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::AtomArena;
#[test]
fn acquire_and_use() {
let ws = WorkspaceArena::acquire();
let ctx = AtomArena::new(ws.arena());
let x = ctx.var("x");
let expr = ctx.add(&[x, ctx.num(1)]);
assert_eq!(expr.to_string(), "x + 1");
}
#[test]
fn pool_reuses_arena_memory() {
{
let ws = WorkspaceArena::acquire();
let ctx = AtomArena::new(ws.arena());
let x = ctx.var("x");
let _ = ctx.add(&[x, ctx.num(1)]);
drop(ctx);
}
ARENA_POOL.with(|pool| assert!(!pool.borrow().is_empty()));
let ws2 = WorkspaceArena::acquire();
let ctx2 = AtomArena::new(ws2.arena());
let y = ctx2.var("y");
let expr = ctx2.add(&[y, ctx2.num(2)]);
assert_eq!(expr.to_string(), "y + 2");
}
#[test]
fn reset_generations_are_independent() {
let mut ws = WorkspaceArena::acquire();
for round in 0..100 {
{
let ctx = AtomArena::new(ws.arena());
let x = ctx.var("x");
let expr = ctx.add(&[x, ctx.num(round)]);
assert_eq!(expr.to_string(), format!("x + {round}"));
}
ws.reset();
}
}
#[test]
fn pool_is_bounded() {
let handles: Vec<WorkspaceArena> = (0..16).map(|_| WorkspaceArena::acquire()).collect();
drop(handles);
ARENA_POOL.with(|pool| assert!(pool.borrow().len() <= MAX_POOLED));
}
#[test]
fn stress_many_generations() {
for i in 0..10_000u64 {
let ws = WorkspaceArena::acquire();
{
let ctx = AtomArena::new(ws.arena());
let x = ctx.var("x");
let mut acc = ctx.num(0);
for j in 0..(i % 50) {
acc = ctx.add(&[acc, ctx.mul(&[x, ctx.num(j as i64)])]);
}
let _ = acc.to_string();
}
}
ARENA_POOL.with(|pool| assert!(pool.borrow().len() <= MAX_POOLED));
}
}