#[derive(Clone, Copy)]
pub(crate) struct ThreadState {
pub(crate) bytes_until_sample: isize,
pub(crate) in_hook: bool,
}
#[inline(always)]
pub(crate) fn sample_debit(size: usize) -> isize {
match isize::try_from(size) {
Ok(size) => size,
Err(_) => isize::MAX,
}
}
#[cfg(nightly_tls_active)]
#[thread_local]
static mut THREAD_STATE: ThreadState = ThreadState {
bytes_until_sample: 0,
in_hook: false,
};
#[cfg(not(nightly_tls_active))]
std::thread_local! {
static THREAD_STATE: core::cell::UnsafeCell<ThreadState> = const {
core::cell::UnsafeCell::new(ThreadState {
bytes_until_sample: 0,
in_hook: false,
})
};
}
#[cfg(all(not(nightly_tls_active), not(feature = "std_tls"), not(miri)))]
static PROFILER_TLS_KEY: core::sync::atomic::AtomicU32 =
core::sync::atomic::AtomicU32::new(u32::MAX);
#[cfg(not(nightly_tls_active))]
#[inline(always)]
pub(crate) fn get_profiler_state() -> *mut ThreadState {
#[cfg(any(feature = "std_tls", miri))]
{
THREAD_STATE.with(|cell| cell.get())
}
#[cfg(all(not(feature = "std_tls"), not(miri)))]
{
#[cfg(all(windows, target_arch = "x86_64"))]
{
let Some(key) = mnemosyne_core::os_tls::get_or_init_key(&PROFILER_TLS_KEY) else {
return THREAD_STATE.with(|cell| cell.get());
};
let ptr = unsafe { mnemosyne_core::os_tls::read_teb_slot(key) } as *mut ThreadState;
if !ptr.is_null() {
ptr
} else {
THREAD_STATE.with(|cell| {
let p = cell.get();
unsafe {
mnemosyne_core::os_tls::write_teb_slot(key, p as *mut core::ffi::c_void);
};
p
})
}
}
#[cfg(not(all(windows, target_arch = "x86_64")))]
{
let Some(key) = mnemosyne_core::os_tls::get_or_init_key(&PROFILER_TLS_KEY) else {
return THREAD_STATE.with(|cell| cell.get());
};
let ptr = mnemosyne_core::os_tls::read_value(key) as *mut ThreadState;
if !ptr.is_null() {
ptr
} else {
THREAD_STATE.with(|cell| {
let p = cell.get();
mnemosyne_core::os_tls::write_value(key, p as *mut core::ffi::c_void);
p
})
}
}
}
}
#[inline(always)]
pub(crate) fn should_skip_alloc_fast_path(
size: usize,
hook_absent: bool,
leak_inactive: bool,
) -> bool {
#[cfg(nightly_tls_active)]
unsafe {
should_skip_alloc_fast_path_state(
&mut *(&raw mut THREAD_STATE),
size,
hook_absent,
leak_inactive,
)
}
#[cfg(not(nightly_tls_active))]
unsafe {
should_skip_alloc_fast_path_state(
&mut *get_profiler_state(),
size,
hook_absent,
leak_inactive,
)
}
}
#[inline(always)]
fn should_skip_alloc_fast_path_state(
state: &mut ThreadState,
size: usize,
hook_absent: bool,
leak_inactive: bool,
) -> bool {
if state.in_hook {
return true;
}
if hook_absent && leak_inactive {
let debit = sample_debit(size);
if state.bytes_until_sample > debit {
state.bytes_until_sample -= debit;
return true;
}
}
false
}
#[inline(always)]
pub(crate) fn enter_hook() -> bool {
#[cfg(nightly_tls_active)]
unsafe {
if THREAD_STATE.in_hook {
true
} else {
THREAD_STATE.in_hook = true;
false
}
}
#[cfg(not(nightly_tls_active))]
unsafe {
let state = &mut *get_profiler_state();
if state.in_hook {
true
} else {
state.in_hook = true;
false
}
}
}
#[inline(always)]
pub(crate) fn exit_hook() {
#[cfg(nightly_tls_active)]
unsafe {
THREAD_STATE.in_hook = false;
}
#[cfg(not(nightly_tls_active))]
unsafe {
(*get_profiler_state()).in_hook = false;
}
}
#[cfg(nightly_tls_active)]
#[inline(always)]
pub(crate) fn get_bytes_until_sample() -> isize {
unsafe { THREAD_STATE.bytes_until_sample }
}
#[cfg(nightly_tls_active)]
#[inline(always)]
pub(crate) fn set_bytes_until_sample(val: isize) {
unsafe {
THREAD_STATE.bytes_until_sample = val;
}
}