use super::traits::{TlsProvider, TlsSlotAccess};
use crate::ThreadAllocator;
use crate::tls_slot::LocalAllocatorSlot;
use mnemosyne_arena::HasSegmentPool;
use mnemosyne_core::os_tls::{get_or_init_key, read_value, write_value};
pub trait TlsSlotOps: 'static {
const IDENTIFIER: &'static str;
unsafe fn read(key: u32) -> *mut core::ffi::c_void;
unsafe fn write(key: u32, ptr: *mut core::ffi::c_void);
}
pub struct OsValueOps;
impl TlsSlotOps for OsValueOps {
const IDENTIFIER: &'static str = "NativeOsTls";
#[inline(always)]
unsafe fn read(key: u32) -> *mut core::ffi::c_void {
read_value(key)
}
#[inline(always)]
unsafe fn write(key: u32, ptr: *mut core::ffi::c_void) {
write_value(key, ptr);
}
}
#[cfg(all(windows, target_arch = "x86_64", not(miri)))]
pub struct TebSlotOps;
#[cfg(all(windows, target_arch = "x86_64", not(miri)))]
impl TlsSlotOps for TebSlotOps {
const IDENTIFIER: &'static str = "AsmTls";
#[inline(always)]
unsafe fn read(key: u32) -> *mut core::ffi::c_void {
unsafe { mnemosyne_core::os_tls::read_teb_slot(key) }
}
#[inline(always)]
unsafe fn write(key: u32, ptr: *mut core::ffi::c_void) {
unsafe { mnemosyne_core::os_tls::write_teb_slot(key, ptr) };
}
}
pub struct OsTlsProvider<B, S, O>(core::marker::PhantomData<(B, S, O)>);
pub type NativeOsTls<B, S> = OsTlsProvider<B, S, OsValueOps>;
#[cfg(all(windows, target_arch = "x86_64", not(miri)))]
pub type AsmTls<B, S> = OsTlsProvider<B, S, TebSlotOps>;
#[cfg(any(not(all(windows, target_arch = "x86_64")), miri))]
pub type AsmTls<B, S> = NativeOsTls<B, S>;
#[inline(always)]
fn init_slot<const ARM_THREAD_EXIT: bool, B: HasSegmentPool, S: TlsSlotAccess<B>, O: TlsSlotOps>(
key: u32,
) -> *mut core::ffi::c_void {
S::get_slot_standard(|slot| {
let alloc_ptr = slot.allocator_ptr();
unsafe { O::write(key, alloc_ptr) };
slot.os_key.set(key);
if ARM_THREAD_EXIT {
S::arm_thread_exit(slot);
}
alloc_ptr
})
}
#[inline(always)]
fn publish_ptr<B: HasSegmentPool, S: TlsSlotAccess<B>, O: TlsSlotOps>(ptr: *mut core::ffi::c_void) {
let Some(key) = get_or_init_key(S::get_os_tls_key()) else {
return;
};
unsafe { O::write(key, ptr) };
}
impl<B: HasSegmentPool, S: TlsSlotAccess<B>, O: TlsSlotOps> TlsProvider<B>
for OsTlsProvider<B, S, O>
{
const IDENTIFIER: &'static str = O::IDENTIFIER;
#[inline(always)]
fn register_current_allocator_ptr(ptr: *mut core::ffi::c_void) {
publish_ptr::<B, S, O>(ptr);
}
#[inline(always)]
fn with_allocator<R>(f: impl FnOnce(&mut ThreadAllocator<B>) -> R) -> Option<R> {
let Some(key) = get_or_init_key(S::get_os_tls_key()) else {
return S::slot_access_armed(f);
};
let ptr = unsafe { O::read(key) };
if !ptr.is_null() {
unsafe { LocalAllocatorSlot::<B>::with_allocator(ptr, f) }
} else {
let alloc_ptr = init_slot::<true, B, S, O>(key);
unsafe { LocalAllocatorSlot::<B>::with_allocator(alloc_ptr, f) }
}
}
#[inline(always)]
unsafe fn with_allocator_unguarded<R>(
f: impl FnOnce(&mut ThreadAllocator<B>) -> R,
) -> Option<R> {
let Some(key) = get_or_init_key(S::get_os_tls_key()) else {
return unsafe { S::slot_access_unguarded(f) };
};
let ptr = unsafe { O::read(key) };
if !ptr.is_null() {
unsafe { LocalAllocatorSlot::<B>::with_allocator_unguarded(ptr, f) }
} else {
let alloc_ptr = init_slot::<true, B, S, O>(key);
unsafe { LocalAllocatorSlot::<B>::with_allocator_unguarded(alloc_ptr, f) }
}
}
#[inline(always)]
fn get_allocator_ptr() -> *mut core::ffi::c_void {
let Some(key) = get_or_init_key(S::get_os_tls_key()) else {
return S::get_slot_standard(|slot| slot.allocator_ptr());
};
let ptr = unsafe { O::read(key) };
if !ptr.is_null() {
ptr
} else {
init_slot::<false, B, S, O>(key)
}
}
#[inline(always)]
fn get_allocator_ptr_raw() -> *mut core::ffi::c_void {
get_or_init_key(S::get_os_tls_key())
.map_or(core::ptr::null_mut(), |key| unsafe { O::read(key) })
}
}