use core::ffi::c_void;
use core::ptr;
use super::{Alloc, MemConfig, PrimError, TlsDtor, align_up};
fn errno() -> PrimError {
#[cfg(target_os = "linux")]
unsafe {
*libc::__errno_location() as PrimError
}
#[cfg(target_os = "macos")]
unsafe {
*libc::__error() as PrimError
}
#[cfg(not(any(target_os = "linux", target_os = "macos")))]
{
1 }
}
pub(super) fn mem_init() -> MemConfig {
let page = unsafe { libc::sysconf(libc::_SC_PAGESIZE) };
let page_size = if page > 0 { page as usize } else { 4096 };
MemConfig {
page_size,
alloc_granularity: page_size,
large_page_size: 2 * 1024 * 1024, has_overcommit: cfg!(target_os = "linux"),
has_partial_free: true,
}
}
unsafe fn mmap_anon(hint: *mut c_void, size: usize, commit: bool) -> Result<*mut u8, PrimError> {
let prot = if commit {
libc::PROT_READ | libc::PROT_WRITE
} else {
libc::PROT_NONE
};
#[allow(unused_mut)]
let mut flags = libc::MAP_PRIVATE | libc::MAP_ANON;
#[cfg(target_os = "linux")]
if !commit {
flags |= libc::MAP_NORESERVE;
}
let p = unsafe { libc::mmap(hint, size, prot, flags, -1, 0) };
if p == libc::MAP_FAILED {
Err(errno())
} else {
Ok(p.cast())
}
}
pub(super) unsafe fn alloc(
size: usize,
try_alignment: usize,
commit: bool,
_allow_large: bool, ) -> Result<Alloc, PrimError> {
let cfg = mem_init();
if try_alignment <= cfg.page_size {
let p = unsafe { mmap_anon(ptr::null_mut(), size, commit)? };
return Ok(Alloc {
ptr: p,
is_large: false,
is_zero: true,
});
}
let over = size + try_alignment;
let raw = unsafe { mmap_anon(ptr::null_mut(), over, commit)? };
let base = raw as usize;
let aligned = align_up(base, try_alignment);
let pre = aligned - base;
let post = over - size - pre;
if pre > 0 {
unsafe { libc::munmap(raw.cast(), pre) };
}
if post > 0 {
unsafe { libc::munmap((aligned + size) as *mut c_void, post) };
}
Ok(Alloc {
ptr: aligned as *mut u8,
is_large: false,
is_zero: true,
})
}
pub(super) unsafe fn free(ptr_: *mut u8, size: usize) -> Result<(), PrimError> {
let r = unsafe { libc::munmap(ptr_.cast(), size) };
if r == 0 { Ok(()) } else { Err(errno()) }
}
pub(super) unsafe fn commit(ptr_: *mut u8, size: usize) -> Result<bool, PrimError> {
let r = unsafe { libc::mprotect(ptr_.cast(), size, libc::PROT_READ | libc::PROT_WRITE) };
if r != 0 {
return Err(errno());
}
Ok(false)
}
pub(super) unsafe fn decommit(ptr_: *mut u8, size: usize) -> Result<bool, PrimError> {
#[cfg(target_vendor = "apple")]
{
let p = unsafe {
libc::mmap(
ptr_.cast(),
size,
libc::PROT_READ | libc::PROT_WRITE,
libc::MAP_PRIVATE | libc::MAP_ANON | libc::MAP_FIXED,
-1,
0,
)
};
if p == libc::MAP_FAILED {
return Err(errno());
}
return Ok(false);
}
#[cfg(not(target_vendor = "apple"))]
{
let r = unsafe { libc::madvise(ptr_.cast(), size, libc::MADV_DONTNEED) };
if r == 0 { Ok(false) } else { Err(errno()) }
}
}
pub(super) unsafe fn reset(ptr_: *mut u8, size: usize) -> Result<(), PrimError> {
#[cfg(any(target_os = "linux", target_os = "macos"))]
{
let r = unsafe { libc::madvise(ptr_.cast(), size, libc::MADV_FREE) };
if r == 0 {
return Ok(());
}
}
let r = unsafe { libc::madvise(ptr_.cast(), size, libc::MADV_DONTNEED) };
if r == 0 { Ok(()) } else { Err(errno()) }
}
pub(super) unsafe fn protect(ptr_: *mut u8, size: usize, on: bool) -> Result<(), PrimError> {
let prot = if on {
libc::PROT_NONE
} else {
libc::PROT_READ | libc::PROT_WRITE
};
let r = unsafe { libc::mprotect(ptr_.cast(), size, prot) };
if r == 0 { Ok(()) } else { Err(errno()) }
}
pub(super) fn numa_node_count() -> usize {
1 }
#[inline]
pub(super) fn thread_id() -> usize {
(unsafe { libc::pthread_self() }) as usize
}
pub(super) fn clock_now() -> u64 {
let mut ts = libc::timespec {
tv_sec: 0,
tv_nsec: 0,
};
unsafe { libc::clock_gettime(libc::CLOCK_MONOTONIC, &mut ts) };
ts.tv_sec as u64 * 1_000_000_000 + ts.tv_nsec as u64
}
pub(super) struct TlsSlotImpl(libc::pthread_key_t);
pub(super) fn tls_new(dtor: Option<TlsDtor>) -> Option<TlsSlotImpl> {
let mut key: libc::pthread_key_t = 0;
let r = unsafe { libc::pthread_key_create(&mut key, dtor) };
if r == 0 { Some(TlsSlotImpl(key)) } else { None }
}
#[inline]
pub(super) fn tls_get(slot: &TlsSlotImpl) -> *mut c_void {
unsafe { libc::pthread_getspecific(slot.0) }
}
#[inline]
pub(super) fn tls_set(slot: &TlsSlotImpl, value: *mut c_void) {
unsafe { libc::pthread_setspecific(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 libc::pthread_key_t)
}