use std::cell::RefCell;
use std::sync::Arc;
use crate::allocators::deferred::DeferredFreeQueue;
use crate::allocators::frame::FrameArena;
use crate::allocators::slab::LocalPools;
use crate::api::stats::ThreadStats;
use crate::core::global::GlobalState;
use crate::util::size::mb;
pub struct ThreadLocalState {
frame: FrameArena,
pools: LocalPools,
deferred: DeferredFreeQueue,
stats: ThreadStats,
frame_active: bool,
}
thread_local! {
static TLS: RefCell<Option<ThreadLocalState>> = const { RefCell::new(None) };
}
impl ThreadLocalState {
fn new() -> Self {
Self {
frame: FrameArena::new(mb(16)), pools: LocalPools::new(),
deferred: DeferredFreeQueue::new(),
stats: ThreadStats::new(),
frame_active: false,
}
}
pub fn begin_frame(&mut self) {
self.deferred.drain(&mut self.pools);
self.frame_active = true;
}
pub fn end_frame(&mut self) {
self.frame.reset();
self.frame_active = false;
}
pub fn is_frame_active(&self) -> bool {
self.frame_active
}
pub fn frame_head(&self) -> usize {
self.frame.head()
}
pub fn reset_frame_to(&mut self, head: usize) {
self.frame.reset_to(head);
}
pub fn frame_alloc<T>(&mut self) -> *mut T {
let ptr = self.frame.alloc::<T>();
#[cfg(not(feature = "minimal"))]
if !ptr.is_null() {
self.stats.record_alloc(std::mem::size_of::<T>());
}
#[cfg(all(target_arch = "x86_64", feature = "prefetch"))]
if !ptr.is_null() {
unsafe { std::arch::x86_64::_mm_prefetch(ptr as *const i8, std::arch::x86_64::_MM_HINT_T0); }
}
ptr
}
pub fn try_frame_alloc<T>(&mut self) -> Option<*mut T> {
let ptr = self.frame.alloc::<T>();
#[cfg(not(feature = "minimal"))]
if !ptr.is_null() {
self.stats.record_alloc(std::mem::size_of::<T>());
}
#[cfg(all(target_arch = "x86_64", feature = "prefetch"))]
if !ptr.is_null() {
unsafe { std::arch::x86_64::_mm_prefetch(ptr as *const i8, std::arch::x86_64::_MM_HINT_T0); }
}
if ptr.is_null() {
None
} else {
Some(ptr)
}
}
pub fn frame_alloc_slice<T>(&mut self, count: usize) -> *mut T {
let ptr = self.frame.alloc_slice::<T>(count);
#[cfg(not(feature = "minimal"))]
if !ptr.is_null() {
self.stats.record_alloc(std::mem::size_of::<T>() * count);
}
#[cfg(all(target_arch = "x86_64", feature = "prefetch"))]
if !ptr.is_null() && count > 0 {
unsafe { std::arch::x86_64::_mm_prefetch(ptr as *const i8, std::arch::x86_64::_MM_HINT_T0); }
}
ptr
}
pub fn frame_alloc_batch<T>(&mut self, count: usize) -> *mut T {
let ptr = self.frame.alloc_slice::<T>(count);
#[cfg(not(feature = "minimal"))]
if !ptr.is_null() {
self.stats.record_alloc(std::mem::size_of::<T>() * count);
}
#[cfg(all(target_arch = "x86_64", feature = "prefetch"))]
if !ptr.is_null() && count > 0 {
unsafe { std::arch::x86_64::_mm_prefetch(ptr as *const i8, std::arch::x86_64::_MM_HINT_T0); }
}
ptr
}
#[inline(always)]
pub fn frame_alloc_2<T>(&mut self) -> *mut [T; 2] {
let ptr = self.frame.alloc_slice::<T>(2) as *mut [T; 2];
#[cfg(not(feature = "minimal"))]
if !ptr.is_null() {
self.stats.record_alloc(std::mem::size_of::<T>() * 2);
}
#[cfg(all(target_arch = "x86_64", feature = "prefetch"))]
if !ptr.is_null() {
unsafe { std::arch::x86_64::_mm_prefetch(ptr as *const i8, std::arch::x86_64::_MM_HINT_T0); }
}
ptr
}
#[inline(always)]
pub fn frame_alloc_4<T>(&mut self) -> *mut [T; 4] {
let ptr = self.frame.alloc_slice::<T>(4) as *mut [T; 4];
#[cfg(not(feature = "minimal"))]
if !ptr.is_null() {
self.stats.record_alloc(std::mem::size_of::<T>() * 4);
}
#[cfg(all(target_arch = "x86_64", feature = "prefetch"))]
if !ptr.is_null() {
unsafe { std::arch::x86_64::_mm_prefetch(ptr as *const i8, std::arch::x86_64::_MM_HINT_T0); }
}
ptr
}
#[inline(always)]
pub fn frame_alloc_8<T>(&mut self) -> *mut [T; 8] {
let ptr = self.frame.alloc_slice::<T>(8) as *mut [T; 8];
#[cfg(not(feature = "minimal"))]
if !ptr.is_null() {
self.stats.record_alloc(std::mem::size_of::<T>() * 8);
}
#[cfg(all(target_arch = "x86_64", feature = "prefetch"))]
if !ptr.is_null() {
unsafe { std::arch::x86_64::_mm_prefetch(ptr as *const i8, std::arch::x86_64::_MM_HINT_T0); }
}
ptr
}
pub fn pool_alloc<T>(&mut self, global: &Arc<GlobalState>) -> *mut T {
let size = std::mem::size_of::<T>();
let ptr = self.pools.alloc(size, global.slabs());
if !ptr.is_null() {
self.stats.record_alloc(size);
}
ptr as *mut T
}
pub fn pool_free<T>(&mut self, ptr: *mut T, _global: &Arc<GlobalState>) {
let size = std::mem::size_of::<T>();
self.pools.free(ptr as *mut u8, size);
self.stats.record_dealloc(size);
}
#[allow(dead_code)]
pub fn queue_deferred_free(&self, ptr: *mut u8, size: usize) {
self.deferred.push(ptr, size);
}
pub fn frame_alloc_layout(&mut self, layout: std::alloc::Layout) -> *mut u8 {
let ptr = self.frame.alloc_layout(layout);
#[cfg(not(feature = "minimal"))]
if !ptr.is_null() {
self.stats.record_alloc(layout.size());
}
ptr
}
pub fn pool_alloc_layout(&mut self, layout: std::alloc::Layout, global: &Arc<GlobalState>) -> *mut u8 {
let ptr = self.pools.alloc(layout.size(), global.slabs());
if !ptr.is_null() {
self.stats.record_alloc(layout.size());
}
ptr
}
pub fn pool_free_layout(&mut self, ptr: *mut u8, layout: std::alloc::Layout, _global: &Arc<GlobalState>) {
self.pools.free(ptr, layout.size());
self.stats.record_dealloc(layout.size());
}
}
pub fn with_tls<F, R>(f: F) -> R
where
F: FnOnce(&mut ThreadLocalState) -> R,
{
TLS.with(|cell| {
let mut borrow = cell.borrow_mut();
let tls = borrow.get_or_insert_with(ThreadLocalState::new);
f(tls)
})
}
pub fn is_tls_initialized() -> bool {
TLS.with(|cell| cell.borrow().is_some())
}