diff --git a/Cargo.toml b/Cargo.toml index 56fe4a8..4169f9a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "dlmalloc" -version = "0.2.1" +version = "0.2.2" authors = ["Alex Crichton "] license = "MIT/Apache-2.0" readme = "README.md" diff --git a/src/dlmalloc.rs b/src/dlmalloc.rs index d16f523..a4b4b71 100644 --- a/src/dlmalloc.rs +++ b/src/dlmalloc.rs @@ -193,11 +193,15 @@ impl Dlmalloc { } fn align_offset(&self, addr: *mut u8) -> usize { - align_up(addr as usize, self.malloc_alignment()) - (addr as usize) + self.align_offset_usize(addr as usize) + } + + fn align_offset_usize(&self, addr: usize) -> usize { + align_up(addr, self.malloc_alignment()) - (addr as usize) } fn top_foot_size(&self) -> usize { - self.align_offset(unsafe { Chunk::to_mem(ptr::null_mut()) }) + self.align_offset_usize(Chunk::mem_offset() as usize) + self.pad_request(mem::size_of::()) + self.min_chunk_size() } @@ -1723,7 +1727,11 @@ impl Chunk { } unsafe fn to_mem(me: *mut Chunk) -> *mut u8 { - (me as *mut u8).offset(2 * (mem::size_of::() as isize)) + (me as *mut u8).offset(Chunk::mem_offset()) + } + + fn mem_offset() -> isize { + 2 * (mem::size_of::() as isize) } unsafe fn from_mem(mem: *mut u8) -> *mut Chunk { diff --git a/src/global.rs b/src/global.rs index fbac315..c761fde 100644 --- a/src/global.rs +++ b/src/global.rs @@ -3,6 +3,8 @@ use core::ops::{Deref, DerefMut}; use Dlmalloc; +pub use sys::enable_alloc_after_fork; + /// An instance of a "global allocator" backed by `Dlmalloc` /// /// This API requires the `global` feature is activated, and this type diff --git a/src/lib.rs b/src/lib.rs index 22a43c6..92e8af7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -20,7 +20,7 @@ use core::ptr; use sys::System; #[cfg(feature = "global")] -pub use self::global::GlobalDlmalloc; +pub use self::global::{enable_alloc_after_fork, GlobalDlmalloc}; mod dlmalloc; #[cfg(feature = "global")] @@ -72,19 +72,15 @@ pub unsafe trait Allocator: Send { /// lingering memory back to the OS. That may happen eventually though! pub struct Dlmalloc(dlmalloc::Dlmalloc); -#[cfg(target_arch = "wasm32")] +#[cfg(target_family = "wasm")] #[path = "wasm.rs"] mod sys; -#[cfg(target_os = "macos")] -#[path = "macos.rs"] +#[cfg(any(target_os = "linux", target_os = "macos"))] +#[path = "unix.rs"] mod sys; -#[cfg(target_os = "linux")] -#[path = "linux.rs"] -mod sys; - -#[cfg(not(any(target_os = "linux", target_os = "macos", target_arch = "wasm32")))] +#[cfg(not(any(target_os = "linux", target_os = "macos", target_family = "wasm")))] #[path = "dummy.rs"] mod sys; diff --git a/src/macos.rs b/src/macos.rs deleted file mode 100644 index d131271..0000000 --- a/src/macos.rs +++ /dev/null @@ -1,72 +0,0 @@ -extern crate libc; - -use core::ptr; -use Allocator; - -/// System setting for MacOS -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( - 0 as *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 as *mut u8, size, 0) - } - } - - fn remap(&self, _ptr: *mut u8, _oldsize: usize, _newsize: usize, _can_move: bool) -> *mut u8 { - ptr::null_mut() - } - - fn free_part(&self, ptr: *mut u8, oldsize: usize, newsize: usize) -> bool { - unsafe { libc::munmap(ptr.offset(newsize as isize) as *mut _, oldsize - newsize) == 0 } - } - - fn free(&self, ptr: *mut u8, size: usize) -> bool { - unsafe { libc::munmap(ptr as *mut _, 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(&mut LOCK), 0) } -} - -#[cfg(feature = "global")] -pub fn release_global_lock() { - unsafe { assert_eq!(libc::pthread_mutex_unlock(&mut LOCK), 0) } -} diff --git a/src/linux.rs b/src/unix.rs similarity index 56% rename from src/linux.rs rename to src/unix.rs index 42e6549..a4f9914 100644 --- a/src/linux.rs +++ b/src/unix.rs @@ -24,7 +24,7 @@ unsafe impl Allocator for System { 0 as *mut _, size, libc::PROT_WRITE | libc::PROT_READ, - libc::MAP_ANONYMOUS | libc::MAP_PRIVATE, + libc::MAP_ANON | libc::MAP_PRIVATE, -1, 0, ) @@ -36,6 +36,7 @@ unsafe impl Allocator for System { } } + #[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 as *mut _, oldsize, newsize, flags) }; @@ -46,6 +47,12 @@ unsafe impl Allocator for System { } } + #[cfg(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 as *mut _, oldsize, newsize, 0); @@ -56,6 +63,11 @@ unsafe impl Allocator for System { } } + #[cfg(target_os = "macos")] + fn free_part(&self, ptr: *mut u8, oldsize: usize, newsize: usize) -> bool { + unsafe { libc::munmap(ptr.offset(newsize as isize) as *mut _, oldsize - newsize) == 0 } + } + fn free(&self, ptr: *mut u8, size: usize) -> bool { unsafe { libc::munmap(ptr as *mut _, size) == 0 } } @@ -82,3 +94,40 @@ pub fn acquire_global_lock() { pub fn release_global_lock() { unsafe { assert_eq!(libc::pthread_mutex_unlock(&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(); +} diff --git a/src/wasm.rs b/src/wasm.rs index 381fb02..216fe43 100644 --- a/src/wasm.rs +++ b/src/wasm.rs @@ -1,4 +1,7 @@ -use core::arch::wasm32; +#[cfg(target_arch = "wasm32")] +use core::arch::wasm32 as wasm; +#[cfg(target_arch = "wasm64")] +use core::arch::wasm64 as wasm; use core::ptr; use Allocator; @@ -16,7 +19,7 @@ impl System { unsafe impl Allocator for System { fn alloc(&self, size: usize) -> (*mut u8, usize, u32) { let pages = size / self.page_size(); - let prev = wasm32::memory_grow(0, pages); + let prev = wasm::memory_grow(0, pages); if prev == usize::max_value() { return (ptr::null_mut(), 0, 0); } @@ -62,3 +65,8 @@ pub fn acquire_global_lock() { pub fn release_global_lock() { // single threaded, no need! } + +#[cfg(feature = "global")] +pub unsafe fn enable_alloc_after_fork() { + // single threaded, no need! +}