use core::ffi::c_void;
use core::ptr;
use core::sync::atomic::{AtomicU64, Ordering};
use windows_sys::Win32::Foundation::GetLastError;
use windows_sys::Win32::System::Environment::GetEnvironmentVariableA;
use windows_sys::Win32::System::Memory::{
GetLargePageMinimum, MEM_COMMIT, MEM_DECOMMIT, MEM_FREE, MEM_LARGE_PAGES, MEM_RELEASE,
MEM_RESERVE, MEM_RESET, MEMORY_BASIC_INFORMATION, PAGE_NOACCESS, PAGE_READWRITE, VirtualAlloc,
VirtualFree, VirtualProtect, VirtualQuery,
};
use windows_sys::Win32::System::Performance::{QueryPerformanceCounter, QueryPerformanceFrequency};
use windows_sys::Win32::System::SystemInformation::{GetSystemInfo, SYSTEM_INFO};
use windows_sys::Win32::System::Threading::{
FlsAlloc, FlsGetValue, FlsSetValue, GetCurrentThreadId, GetNumaHighestNodeNumber,
};
use super::{Alloc, MemConfig, PrimError, TlsDtor, align_up};
pub(super) fn mem_init() -> MemConfig {
let si = unsafe {
let mut si: SYSTEM_INFO = core::mem::zeroed();
GetSystemInfo(&mut si);
si
};
let large = unsafe { GetLargePageMinimum() };
MemConfig {
page_size: si.dwPageSize as usize,
alloc_granularity: si.dwAllocationGranularity as usize,
large_page_size: large,
has_overcommit: false,
has_partial_free: false,
}
}
fn last_error() -> PrimError {
unsafe { GetLastError() }
}
pub(super) unsafe fn alloc(
size: usize,
try_alignment: usize,
commit: bool,
allow_large: bool,
) -> Result<Alloc, PrimError> {
let flags = MEM_RESERVE | if commit { MEM_COMMIT } else { 0 };
if allow_large && commit {
let large_min = unsafe { GetLargePageMinimum() };
if large_min > 0 && try_alignment <= large_min && size >= large_min {
if let Some(lsize) = super::align_up_checked(size, large_min) {
let p = unsafe {
VirtualAlloc(ptr::null(), lsize, flags | MEM_LARGE_PAGES, PAGE_READWRITE)
};
if !p.is_null() && crate::bins::is_aligned_to(p as usize, try_alignment) {
return Ok(Alloc {
ptr: p.cast(),
is_large: true,
is_zero: true,
});
}
if !p.is_null() {
unsafe { VirtualFree(p, 0, MEM_RELEASE) };
}
}
}
}
let p = unsafe { VirtualAlloc(ptr::null(), size, flags, PAGE_READWRITE) };
if !p.is_null() && crate::bins::is_aligned_to(p as usize, try_alignment) {
return Ok(Alloc {
ptr: p.cast(),
is_large: false,
is_zero: true,
});
}
if !p.is_null() {
unsafe { VirtualFree(p, 0, MEM_RELEASE) };
}
for _ in 0..3 {
let Some(over) = size.checked_add(try_alignment) else {
return Err(12); };
let probe = unsafe { VirtualAlloc(ptr::null(), over, MEM_RESERVE, PAGE_NOACCESS) };
if probe.is_null() {
return Err(last_error());
}
let aligned = align_up(probe as usize, try_alignment) as *mut c_void;
unsafe { VirtualFree(probe, 0, MEM_RELEASE) };
let p = unsafe { VirtualAlloc(aligned, size, flags, PAGE_READWRITE) };
if core::ptr::eq(p, aligned) {
return Ok(Alloc {
ptr: p.cast(),
is_large: false,
is_zero: true,
});
}
if !p.is_null() {
unsafe { VirtualFree(p, 0, MEM_RELEASE) };
}
}
Err(last_error())
}
pub(super) unsafe fn free(ptr_: *mut u8, _size: usize) -> Result<(), PrimError> {
let ok = unsafe { VirtualFree(ptr_.cast(), 0, MEM_RELEASE) };
if ok != 0 { Ok(()) } else { Err(last_error()) }
}
pub(super) unsafe fn commit(ptr_: *mut u8, size: usize) -> Result<bool, PrimError> {
let p = unsafe { VirtualAlloc(ptr_.cast(), size, MEM_COMMIT, PAGE_READWRITE) };
if p.is_null() {
return Err(last_error());
}
if p != ptr_.cast() {
let _ = unsafe { VirtualFree(p, 0, MEM_RELEASE) };
return Err(22);
}
Ok(false)
}
pub(super) unsafe fn decommit(ptr_: *mut u8, size: usize) -> Result<bool, PrimError> {
let ok = unsafe { VirtualFree(ptr_.cast(), size, MEM_DECOMMIT) };
if ok != 0 { Ok(true) } else { Err(last_error()) }
}
pub(super) unsafe fn reset(ptr_: *mut u8, size: usize) -> Result<(), PrimError> {
let p = unsafe { VirtualAlloc(ptr_.cast(), size, MEM_RESET, PAGE_READWRITE) };
if p.is_null() {
Err(last_error())
} else {
Ok(())
}
}
pub(super) unsafe fn protect(ptr_: *mut u8, size: usize, on: bool) -> Result<(), PrimError> {
let new = if on { PAGE_NOACCESS } else { PAGE_READWRITE };
let mut old = 0u32;
let ok = unsafe { VirtualProtect(ptr_.cast(), size, new, &mut old) };
if ok != 0 { Ok(()) } else { Err(last_error()) }
}
pub(super) fn range_is_reserved(ptr: *const u8, size: usize) -> bool {
if ptr.is_null() || size == 0 {
return false;
}
let mut addr = ptr as usize;
let Some(end) = addr.checked_add(size) else {
return false;
};
while addr < end {
let mut mbi = unsafe { core::mem::zeroed::<MEMORY_BASIC_INFORMATION>() };
let n = unsafe {
VirtualQuery(
addr as *const c_void,
&mut mbi,
core::mem::size_of::<MEMORY_BASIC_INFORMATION>(),
)
};
if n == 0 || mbi.State == MEM_FREE || mbi.RegionSize == 0 {
return false;
}
let region_end = (mbi.BaseAddress as usize).saturating_add(mbi.RegionSize);
if region_end <= addr {
return false;
}
addr = region_end;
}
true
}
pub(super) fn numa_node_count() -> usize {
let mut highest = 0u32;
let ok = unsafe { GetNumaHighestNodeNumber(&mut highest) };
if ok != 0 { highest as usize + 1 } else { 1 }
}
pub(super) fn getenv(name: &[u8], out: &mut [u8]) -> Option<usize> {
let cap = u32::try_from(out.len()).ok()?;
let n = unsafe { GetEnvironmentVariableA(name.as_ptr(), out.as_mut_ptr(), cap) };
if n == 0 || n >= cap {
None
} else {
Some(n as usize)
}
}
#[inline]
pub(super) fn thread_id() -> usize {
(unsafe { GetCurrentThreadId() }) as usize
}
#[inline]
fn ticks_to_ns(count: u64, freq: u64, scale: u64) -> u64 {
if scale != 0 {
count.wrapping_mul(scale)
} else {
((u128::from(count) * 1_000_000_000u128) / u128::from(freq.max(1))) as u64
}
}
pub(super) fn clock_now() -> u64 {
static FREQ: AtomicU64 = AtomicU64::new(0);
static SCALE: AtomicU64 = AtomicU64::new(0);
let mut freq = FREQ.load(Ordering::Acquire);
if freq == 0 {
let mut f = 0i64;
unsafe { QueryPerformanceFrequency(&mut f) };
freq = f.max(1) as u64;
let q = 1_000_000_000u64 / freq;
let scale = if q * freq == 1_000_000_000 { q } else { 0 };
SCALE.store(scale, Ordering::Relaxed);
FREQ.store(freq, Ordering::Release);
}
let mut count = 0i64;
unsafe { QueryPerformanceCounter(&mut count) };
ticks_to_ns(count.max(0) as u64, freq, SCALE.load(Ordering::Relaxed))
}
pub(super) struct TlsSlotImpl(u32);
pub(super) fn tls_new(dtor: Option<TlsDtor>) -> Option<TlsSlotImpl> {
let idx = unsafe { FlsAlloc(dtor) };
if idx == u32::MAX {
None
} else {
Some(TlsSlotImpl(idx))
}
}
#[inline]
pub(super) fn tls_get(slot: &TlsSlotImpl) -> *mut c_void {
unsafe { FlsGetValue(slot.0) }
}
#[inline]
pub(super) fn tls_set(slot: &TlsSlotImpl, value: *mut c_void) {
unsafe { FlsSetValue(slot.0, value) };
}
pub(super) fn tls_raw(slot: &TlsSlotImpl) -> usize {
slot.0 as usize
}
pub(super) fn tls_from_raw(raw: usize) -> TlsSlotImpl {
TlsSlotImpl(raw as u32)
}
#[cfg(test)]
mod clock_tests {
use super::ticks_to_ns;
fn reference(count: u64, freq: u64) -> u64 {
((u128::from(count) * 1_000_000_000u128) / u128::from(freq)) as u64
}
fn scale_for(freq: u64) -> u64 {
if 1_000_000_000u64.is_multiple_of(freq) {
1_000_000_000u64 / freq
} else {
0
}
}
#[test]
fn exact_scale_matches_the_u128_reference() {
for freq in [10_000_000u64, 1_000_000, 100_000, 1_000, 1] {
assert_eq!(scale_for(freq), 1_000_000_000 / freq, "freq {freq}");
for count in [0u64, 1, 7, 12_345, 1 << 20, 1 << 32, 86_400 * freq] {
assert_eq!(
ticks_to_ns(count, freq, scale_for(freq)),
reference(count, freq),
"freq {freq} count {count}"
);
}
}
}
#[test]
fn inexact_frequency_falls_back_and_stays_correct() {
let freq = 3_579_545u64;
assert_eq!(scale_for(freq), 0, "3.579545 MHz must not get a scale");
for count in [0u64, 1, 3_579_545, 1 << 32] {
assert_eq!(ticks_to_ns(count, freq, 0), reference(count, freq));
}
}
#[test]
fn a_day_of_ticks_does_not_wrap() {
let freq = 10_000_000u64;
let day = 86_400 * freq;
assert_eq!(
ticks_to_ns(day, freq, scale_for(freq)),
86_400 * 1_000_000_000,
"one day must convert exactly"
);
}
}