132 lines
3.6 KiB
Rust
132 lines
3.6 KiB
Rust
use crate::Allocator;
|
|
use core::ptr;
|
|
|
|
/// System setting for Linux
|
|
pub struct System {
|
|
_priv: (),
|
|
}
|
|
|
|
impl System {
|
|
pub const fn new() -> System {
|
|
System { _priv: () }
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "global")]
|
|
static mut LOCK: libc::pthread_mutex_t = libc::PTHREAD_MUTEX_INITIALIZER;
|
|
|
|
unsafe impl Allocator for System {
|
|
fn alloc(&self, size: usize) -> (*mut u8, usize, u32) {
|
|
let addr = unsafe {
|
|
libc::mmap(
|
|
ptr::null_mut(),
|
|
size,
|
|
libc::PROT_WRITE | libc::PROT_READ,
|
|
libc::MAP_ANON | libc::MAP_PRIVATE,
|
|
-1,
|
|
0,
|
|
)
|
|
};
|
|
if addr == libc::MAP_FAILED {
|
|
(ptr::null_mut(), 0, 0)
|
|
} else {
|
|
(addr.cast(), size, 0)
|
|
}
|
|
}
|
|
|
|
#[cfg(target_os = "linux")]
|
|
fn remap(&self, ptr: *mut u8, oldsize: usize, newsize: usize, can_move: bool) -> *mut u8 {
|
|
let flags = if can_move { libc::MREMAP_MAYMOVE } else { 0 };
|
|
let ptr = unsafe { libc::mremap(ptr.cast(), oldsize, newsize, flags) };
|
|
if ptr == libc::MAP_FAILED {
|
|
ptr::null_mut()
|
|
} else {
|
|
ptr.cast()
|
|
}
|
|
}
|
|
|
|
#[cfg(any(target_os = "redox", target_os = "macos"))]
|
|
fn remap(&self, _ptr: *mut u8, _oldsize: usize, _newsize: usize, _can_move: bool) -> *mut u8 {
|
|
ptr::null_mut()
|
|
}
|
|
|
|
#[cfg(target_os = "linux")]
|
|
fn free_part(&self, ptr: *mut u8, oldsize: usize, newsize: usize) -> bool {
|
|
unsafe {
|
|
let rc = libc::mremap(ptr.cast(), oldsize, newsize, 0);
|
|
if rc != libc::MAP_FAILED {
|
|
return true;
|
|
}
|
|
libc::munmap(ptr.add(newsize).cast(), oldsize - newsize) == 0
|
|
}
|
|
}
|
|
|
|
#[cfg(any(target_os = "redox", target_os = "macos"))]
|
|
fn free_part(&self, ptr: *mut u8, oldsize: usize, newsize: usize) -> bool {
|
|
unsafe { libc::munmap(ptr.add(newsize).cast(), oldsize - newsize) == 0 }
|
|
}
|
|
|
|
fn free(&self, ptr: *mut u8, size: usize) -> bool {
|
|
unsafe { libc::munmap(ptr.cast(), size) == 0 }
|
|
}
|
|
|
|
fn can_release_part(&self, _flags: u32) -> bool {
|
|
true
|
|
}
|
|
|
|
fn allocates_zeros(&self) -> bool {
|
|
true
|
|
}
|
|
|
|
fn page_size(&self) -> usize {
|
|
4096
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "global")]
|
|
pub fn acquire_global_lock() {
|
|
unsafe { assert_eq!(libc::pthread_mutex_lock(ptr::addr_of_mut!(LOCK)), 0) }
|
|
}
|
|
|
|
#[cfg(feature = "global")]
|
|
pub fn release_global_lock() {
|
|
unsafe { assert_eq!(libc::pthread_mutex_unlock(ptr::addr_of_mut!(LOCK)), 0) }
|
|
}
|
|
|
|
#[cfg(feature = "global")]
|
|
/// allows the allocator to remain unsable in the child process,
|
|
/// after a call to `fork(2)`
|
|
///
|
|
/// #Safety
|
|
///
|
|
/// if used, this function must be called,
|
|
/// before any allocations are made with the global allocator.
|
|
pub unsafe fn enable_alloc_after_fork() {
|
|
// atfork must only be called once, to avoid a deadlock,
|
|
// where the handler attempts to acquire the global lock twice
|
|
static mut FORK_PROTECTED: bool = false;
|
|
|
|
unsafe extern "C" fn _acquire_global_lock() {
|
|
acquire_global_lock()
|
|
}
|
|
|
|
unsafe extern "C" fn _release_global_lock() {
|
|
release_global_lock()
|
|
}
|
|
|
|
acquire_global_lock();
|
|
// if a process forks,
|
|
// it will acquire the lock before any other thread,
|
|
// protecting it from deadlock,
|
|
// due to the child being created with only the calling thread.
|
|
if !FORK_PROTECTED {
|
|
libc::pthread_atfork(
|
|
Some(_acquire_global_lock),
|
|
Some(_release_global_lock),
|
|
Some(_release_global_lock),
|
|
);
|
|
FORK_PROTECTED = true;
|
|
}
|
|
release_global_lock();
|
|
}
|