use std::ptr::NonNull;
use std::sync::atomic::{AtomicUsize, Ordering};
use crate::node_heap::PerNodeHeap;
use crate::platform;
pub const MAX_NODES: usize = 8;
pub const DEFAULT_REGION_SIZE: usize = 128 * 1024 * 1024;
pub struct NodeRegion {
base: NonNull<u8>,
size: usize,
bump: AtomicUsize,
pub node_heap: PerNodeHeap,
}
impl NodeRegion {
fn new(base: NonNull<u8>, size: usize) -> Self {
Self {
base,
size,
bump: AtomicUsize::new(0),
node_heap: PerNodeHeap::new(),
}
}
fn empty() -> Self {
Self::new(NonNull::dangling(), 0)
}
pub fn allocate_bag(&self, bag_size: usize) -> Option<NonNull<u8>> {
debug_assert!(bag_size.is_power_of_two());
let align_mask = bag_size - 1;
loop {
let offset = self.bump.load(Ordering::Relaxed);
let aligned = (offset + align_mask) & !align_mask;
let new_offset = aligned + bag_size;
if new_offset > self.size {
return None;
}
if self
.bump
.compare_exchange_weak(offset, new_offset, Ordering::Relaxed, Ordering::Relaxed)
.is_ok()
{
return Some(unsafe { NonNull::new_unchecked(self.base.as_ptr().add(aligned)) });
}
}
}
}
unsafe impl Send for NodeRegion {}
unsafe impl Sync for NodeRegion {}
pub struct GlobalHeap {
base: NonNull<u8>,
total_size: usize,
region_size: usize,
num_nodes: usize,
nodes: [NodeRegion; MAX_NODES],
}
impl GlobalHeap {
pub fn new(num_nodes: usize) -> Option<Self> {
let num_nodes = num_nodes.clamp(1, MAX_NODES);
let region_size = DEFAULT_REGION_SIZE;
let total_size = region_size * num_nodes;
let base = unsafe { platform::mmap_anonymous(total_size)? };
let nodes: [NodeRegion; MAX_NODES] = std::array::from_fn(|i| {
if i < num_nodes {
let node_base =
unsafe { NonNull::new_unchecked(base.as_ptr().add(i * region_size)) };
unsafe {
platform::bind_to_node(node_base, region_size, i);
}
NodeRegion::new(node_base, region_size)
} else {
NodeRegion::empty()
}
});
Some(Self {
base,
total_size,
region_size,
num_nodes,
nodes,
})
}
#[inline]
pub fn node_for_ptr(&self, ptr: NonNull<u8>) -> Option<usize> {
let offset = (ptr.as_ptr() as usize).wrapping_sub(self.base.as_ptr() as usize);
if offset >= self.total_size {
return None;
}
Some(offset / self.region_size)
}
#[inline]
pub fn is_owned(&self, ptr: NonNull<u8>) -> bool {
self.node_for_ptr(ptr).is_some()
}
#[inline]
pub fn node_region(&self, node: usize) -> &NodeRegion {
&self.nodes[node]
}
#[inline]
pub fn num_nodes(&self) -> usize {
self.num_nodes
}
}
unsafe impl Send for GlobalHeap {}
unsafe impl Sync for GlobalHeap {}
impl Drop for GlobalHeap {
fn drop(&mut self) {
unsafe {
platform::munmap(self.base, self.total_size);
}
}
}