use bumpalo::Bump;
use parking_lot::RwLock;
use std::collections::VecDeque;
use std::sync::Arc;
#[allow(clippy::arc_with_non_send_sync)]
pub struct SafeArenaAllocator {
current_arena: Arc<RwLock<Bump>>,
backup_arena: Arc<RwLock<Bump>>,
arena_size: usize,
}
impl SafeArenaAllocator {
#[allow(clippy::arc_with_non_send_sync)]
pub fn new(arena_size: usize) -> Self {
Self {
current_arena: Arc::new(RwLock::new(Bump::with_capacity(arena_size))),
backup_arena: Arc::new(RwLock::new(Bump::with_capacity(arena_size))),
arena_size,
}
}
pub fn reset_and_swap(&self) {
let mut current = self.current_arena.write();
let mut backup = self.backup_arena.write();
current.reset();
std::mem::swap(&mut *current, &mut *backup);
}
pub fn memory_usage(&self) -> usize {
let current = self.current_arena.read();
current.allocated_bytes()
}
pub fn needs_reset(&self) -> bool {
let current = self.current_arena.read();
current.allocated_bytes() > (self.arena_size * 3) / 4
}
pub fn scoped<F, R>(&self, f: F) -> R
where
F: FnOnce() -> R,
{
let result = f();
self.reset_and_swap();
result
}
}
pub struct SafePoolAllocator<T> {
pool: Arc<RwLock<VecDeque<Box<T>>>>,
factory: Arc<dyn Fn() -> T + Send + Sync>,
max_pool_size: usize,
}
impl<T> SafePoolAllocator<T>
where
T: Default,
{
pub fn new(initial_size: usize, max_size: usize) -> Self {
let pool = Arc::new(RwLock::new(VecDeque::new()));
{
let mut pool_guard = pool.write();
for _ in 0..initial_size {
pool_guard.push_back(Box::new(T::default()));
}
}
Self {
pool,
factory: Arc::new(|| T::default()),
max_pool_size: max_size,
}
}
}
impl<T> SafePoolAllocator<T> {
pub fn with_factory<F>(factory: F, initial_size: usize, max_size: usize) -> Self
where
F: Fn() -> T + Send + Sync + 'static,
{
let pool = Arc::new(RwLock::new(VecDeque::new()));
let factory = Arc::new(factory);
{
let mut pool_guard = pool.write();
for _ in 0..initial_size {
pool_guard.push_back(Box::new(factory()));
}
}
Self {
pool,
factory,
max_pool_size: max_size,
}
}
pub fn acquire(&self) -> PooledObject<T> {
let obj = {
let mut pool = self.pool.write();
pool.pop_front()
.unwrap_or_else(|| Box::new((self.factory)()))
};
PooledObject {
object: Some(obj),
pool: Arc::clone(&self.pool),
max_pool_size: self.max_pool_size,
}
}
pub fn stats(&self) -> PoolStats {
let pool = self.pool.read();
PoolStats {
available_objects: pool.len(),
max_pool_size: self.max_pool_size,
}
}
}
#[derive(Debug, Clone)]
pub struct PoolStats {
pub available_objects: usize,
pub max_pool_size: usize,
}
pub struct PooledObject<T> {
object: Option<Box<T>>,
pool: Arc<RwLock<VecDeque<Box<T>>>>,
max_pool_size: usize,
}
impl<T> PooledObject<T> {
pub fn get(&self) -> &T {
self.object.as_ref().unwrap()
}
pub fn get_mut(&mut self) -> &mut T {
self.object.as_mut().unwrap()
}
}
impl<T> Drop for PooledObject<T> {
fn drop(&mut self) {
if let Some(obj) = self.object.take() {
let mut pool = self.pool.write();
if pool.len() < self.max_pool_size {
pool.push_back(obj);
}
}
}
}
pub struct SafeMemoryManager {
arena: SafeArenaAllocator,
node_pool: SafePoolAllocator<Vec<u8>>,
relationship_pool: SafePoolAllocator<Vec<u8>>,
}
impl SafeMemoryManager {
pub fn new(arena_size: usize) -> Self {
Self {
arena: SafeArenaAllocator::new(arena_size),
node_pool: SafePoolAllocator::new(1000, 10000),
relationship_pool: SafePoolAllocator::new(2000, 20000),
}
}
pub fn arena(&self) -> &SafeArenaAllocator {
&self.arena
}
pub fn acquire_node_buffer(&self) -> PooledObject<Vec<u8>> {
self.node_pool.acquire()
}
pub fn acquire_relationship_buffer(&self) -> PooledObject<Vec<u8>> {
self.relationship_pool.acquire()
}
pub fn stats(&self) -> SafeMemoryStats {
SafeMemoryStats {
arena_usage: self.arena.memory_usage(),
node_pool: self.node_pool.stats(),
relationship_pool: self.relationship_pool.stats(),
}
}
pub fn cleanup(&self) {
self.arena.reset_and_swap();
}
}
#[derive(Debug)]
pub struct SafeMemoryStats {
pub arena_usage: usize,
pub node_pool: PoolStats,
pub relationship_pool: PoolStats,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_safe_arena_allocator() {
let arena = SafeArenaAllocator::new(1024);
let result = arena.scoped(|| {
42
});
assert_eq!(result, 42);
}
#[test]
fn test_safe_pool_allocator() {
let pool: SafePoolAllocator<Vec<u8>> = SafePoolAllocator::new(5, 20);
let mut obj1 = pool.acquire();
let mut obj2 = pool.acquire();
obj1.get_mut().push(1);
obj2.get_mut().push(2);
assert_eq!(obj1.get()[0], 1);
assert_eq!(obj2.get()[0], 2);
drop(obj1);
drop(obj2);
let stats = pool.stats();
assert!(stats.available_objects > 0);
}
#[test]
fn test_pooled_object_return() {
let pool: SafePoolAllocator<String> = SafePoolAllocator::new(1, 5);
{
let mut obj = pool.acquire();
obj.get_mut().push_str("test");
assert_eq!(obj.get(), "test");
}
let stats = pool.stats();
assert_eq!(stats.available_objects, 1);
}
#[test]
fn test_safe_memory_manager() {
let manager = SafeMemoryManager::new(4096);
let mut node_buf = manager.acquire_node_buffer();
node_buf.get_mut().extend_from_slice(&[1, 2, 3, 4]);
let mut rel_buf = manager.acquire_relationship_buffer();
rel_buf.get_mut().extend_from_slice(&[5, 6, 7, 8]);
let stats = manager.stats();
assert!(stats.node_pool.available_objects < 1000); assert!(stats.relationship_pool.available_objects < 2000);
manager.cleanup();
}
#[test]
fn test_arena_memory_tracking() {
let arena = SafeArenaAllocator::new(1024);
let initial_usage = arena.memory_usage();
arena.reset_and_swap();
let after_reset = arena.memory_usage();
assert!(after_reset <= initial_usage || after_reset > 0);
}
}