use std::alloc::{alloc, dealloc, Layout};
use std::sync::atomic::{AtomicBool, AtomicU64, AtomicUsize, Ordering};
use std::sync::Mutex;
use crate::config::MkConfig;
pub struct GlobalState {
config: MkConfig,
frame_counter: AtomicU64,
total_allocated: AtomicUsize,
peak_allocated: AtomicUsize,
allocation_count: AtomicU64,
deallocation_count: AtomicU64,
deferred_queue: crate::sync::deferred::MkDeferredQueue,
handle_allocator: crate::allocator::handle::MkHandleAllocator,
global_pool: Mutex<crate::core::slab::SlabAllocator>,
pool_enabled: AtomicBool,
}
unsafe impl Send for GlobalState {}
unsafe impl Sync for GlobalState {}
impl GlobalState {
pub fn new(config: MkConfig) -> Self {
Self {
config,
frame_counter: AtomicU64::new(0),
total_allocated: AtomicUsize::new(0),
peak_allocated: AtomicUsize::new(0),
allocation_count: AtomicU64::new(0),
deallocation_count: AtomicU64::new(0),
deferred_queue: crate::sync::deferred::MkDeferredQueue::new(),
handle_allocator: crate::allocator::handle::MkHandleAllocator::new(),
global_pool: Mutex::new(crate::core::slab::SlabAllocator::new()),
pool_enabled: AtomicBool::new(false), }
}
pub fn config(&self) -> &MkConfig {
&self.config
}
pub fn next_frame(&self) -> u64 {
self.frame_counter.fetch_add(1, Ordering::SeqCst) + 1
}
pub fn frame(&self) -> u64 {
self.frame_counter.load(Ordering::SeqCst)
}
pub fn record_alloc(&self, size: usize) {
self.allocation_count.fetch_add(1, Ordering::Relaxed);
let new_total = self.total_allocated.fetch_add(size, Ordering::Relaxed) + size;
let mut peak = self.peak_allocated.load(Ordering::Relaxed);
while new_total > peak {
match self.peak_allocated.compare_exchange_weak(
peak,
new_total,
Ordering::Relaxed,
Ordering::Relaxed,
) {
Ok(_) => break,
Err(current) => peak = current,
}
}
}
pub fn record_dealloc(&self, size: usize) {
self.deallocation_count.fetch_add(1, Ordering::Relaxed);
self.total_allocated.fetch_sub(size, Ordering::Relaxed);
}
pub fn heap_alloc(&self, layout: Layout) -> *mut u8 {
let ptr = unsafe { alloc(layout) };
if !ptr.is_null() {
self.record_alloc(layout.size());
unsafe { crate::core::sentinel::MkSentinel::poison_alloc(ptr, layout.size()) };
}
ptr
}
pub fn heap_free(&self, ptr: *mut u8, layout: Layout) {
if !ptr.is_null() {
unsafe { crate::core::sentinel::MkSentinel::poison_free(ptr, layout.size()) };
self.record_dealloc(layout.size());
unsafe { dealloc(ptr, layout) };
}
}
pub fn deferred_queue(&self) -> &crate::sync::deferred::MkDeferredQueue {
&self.deferred_queue
}
pub fn handle_allocator(&self) -> &crate::allocator::handle::MkHandleAllocator {
&self.handle_allocator
}
pub fn pool_alloc<T>(&self, value: T) -> *mut T {
let layout = Layout::new::<T>();
if !self.pool_enabled.load(Ordering::Relaxed) {
let ptr = self.heap_alloc(layout) as *mut T;
if !ptr.is_null() {
unsafe {
ptr.write(value);
}
}
return ptr;
}
if let Ok(mut pool) = self.global_pool.lock() {
let ptr = pool.alloc::<T>();
if !ptr.is_null() {
unsafe {
ptr.write(value);
return ptr;
}
}
}
let ptr = self.heap_alloc(layout) as *mut T;
if !ptr.is_null() {
unsafe {
ptr.write(value);
}
}
ptr
}
pub fn pool_free<T>(&self, ptr: *mut T) {
if ptr.is_null() {
return;
}
unsafe {
std::ptr::drop_in_place(ptr);
let layout = Layout::new::<T>();
self.heap_free(ptr as *mut u8, layout);
}
}
pub fn stats(&self) -> GlobalStats {
GlobalStats {
total_allocated: self.total_allocated.load(Ordering::Relaxed),
peak_allocated: self.peak_allocated.load(Ordering::Relaxed),
allocation_count: self.allocation_count.load(Ordering::Relaxed),
deallocation_count: self.deallocation_count.load(Ordering::Relaxed),
frame: self.frame_counter.load(Ordering::Relaxed),
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct GlobalStats {
pub total_allocated: usize,
pub peak_allocated: usize,
pub allocation_count: u64,
pub deallocation_count: u64,
pub frame: u64,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_global_stats() {
let global = GlobalState::new(MkConfig::default());
global.record_alloc(100);
global.record_alloc(200);
let stats = global.stats();
assert_eq!(stats.total_allocated, 300);
assert_eq!(stats.peak_allocated, 300);
assert_eq!(stats.allocation_count, 2);
global.record_dealloc(100);
let stats = global.stats();
assert_eq!(stats.total_allocated, 200);
assert_eq!(stats.peak_allocated, 300); }
#[test]
fn test_frame_counter() {
let global = GlobalState::new(MkConfig::default());
assert_eq!(global.frame(), 0);
assert_eq!(global.next_frame(), 1);
assert_eq!(global.next_frame(), 2);
assert_eq!(global.frame(), 2);
}
}
impl Drop for GlobalState {
fn drop(&mut self) {
crate::core::sentinel::MkSentinel::verify_leaks();
}
}