0.3.0: converge relibc to upstream 0.6.0 + Red Bear patches
This commit is contained in:
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,42 @@
|
||||
use crate::Allocator;
|
||||
use core::ptr;
|
||||
|
||||
pub struct System {
|
||||
_priv: (),
|
||||
}
|
||||
|
||||
impl System {
|
||||
pub const fn new() -> System {
|
||||
System { _priv: () }
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Allocator for System {
|
||||
fn alloc(&self, _size: usize) -> (*mut u8, usize, u32) {
|
||||
(ptr::null_mut(), 0, 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 {
|
||||
false
|
||||
}
|
||||
|
||||
fn free(&self, _ptr: *mut u8, _size: usize) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn can_release_part(&self, _flags: u32) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn allocates_zeros(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn page_size(&self) -> usize {
|
||||
1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
use crate::Dlmalloc;
|
||||
use core::alloc::{GlobalAlloc, Layout};
|
||||
use core::ptr;
|
||||
|
||||
pub use crate::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
|
||||
/// implements the `GlobalAlloc` trait in the standard library.
|
||||
pub struct GlobalDlmalloc;
|
||||
|
||||
static mut DLMALLOC: Dlmalloc = Dlmalloc::new();
|
||||
|
||||
unsafe impl GlobalAlloc for GlobalDlmalloc {
|
||||
#[inline]
|
||||
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
|
||||
let _guard = lock();
|
||||
let dlmalloc = ptr::addr_of_mut!(DLMALLOC);
|
||||
(*dlmalloc).malloc(layout.size(), layout.align())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
|
||||
let _guard = lock();
|
||||
let dlmalloc = ptr::addr_of_mut!(DLMALLOC);
|
||||
(*dlmalloc).free(ptr, layout.size(), layout.align())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
|
||||
let _guard = lock();
|
||||
let dlmalloc = ptr::addr_of_mut!(DLMALLOC);
|
||||
(*dlmalloc).calloc(layout.size(), layout.align())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
|
||||
let _guard = lock();
|
||||
let dlmalloc = ptr::addr_of_mut!(DLMALLOC);
|
||||
(*dlmalloc).realloc(ptr, layout.size(), layout.align(), new_size)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn lock() -> impl Drop {
|
||||
crate::sys::acquire_global_lock();
|
||||
|
||||
struct Guard;
|
||||
impl Drop for Guard {
|
||||
fn drop(&mut self) {
|
||||
crate::sys::release_global_lock()
|
||||
}
|
||||
}
|
||||
|
||||
Guard
|
||||
}
|
||||
@@ -0,0 +1,230 @@
|
||||
//! A Rust port of the `dlmalloc` allocator.
|
||||
//!
|
||||
//! The `dlmalloc` allocator is described at
|
||||
//! <https://gee.cs.oswego.edu/dl/html/malloc.html> and this Rust crate is a straight
|
||||
//! port of the C code for the allocator into Rust. The implementation is
|
||||
//! wrapped up in a `Dlmalloc` type and has support for Linux, OSX, and Wasm
|
||||
//! currently.
|
||||
//!
|
||||
//! The primary purpose of this crate is that it serves as the default memory
|
||||
//! allocator for the `wasm32-unknown-unknown` target in the standard library.
|
||||
//! Support for other platforms is largely untested and unused, but is used when
|
||||
//! testing this crate.
|
||||
|
||||
#![allow(dead_code)]
|
||||
#![no_std]
|
||||
#![deny(missing_docs)]
|
||||
|
||||
#[cfg(feature = "rust_api")]
|
||||
use core::{cmp, ptr};
|
||||
|
||||
#[cfg(feature = "system")]
|
||||
use sys::System;
|
||||
|
||||
#[cfg(feature = "global")]
|
||||
pub use self::global::{enable_alloc_after_fork, GlobalDlmalloc};
|
||||
|
||||
mod dlmalloc;
|
||||
|
||||
#[cfg(feature = "c_api")]
|
||||
pub use dlmalloc::Dlmalloc as DlmallocCApi;
|
||||
|
||||
#[cfg(feature = "global")]
|
||||
mod global;
|
||||
|
||||
/// In order for this crate to efficiently manage memory, it needs a way to communicate with the
|
||||
/// underlying platform. This `Allocator` trait provides an interface for this communication.
|
||||
pub unsafe trait Allocator: Send {
|
||||
/// Allocates system memory region of at least `size` bytes
|
||||
/// Returns a triple of `(base, size, flags)` where `base` is a pointer to the beginning of the
|
||||
/// allocated memory region. `size` is the actual size of the region while `flags` specifies
|
||||
/// properties of the allocated region. If `EXTERN_BIT` (bit 0) set in flags, then we did not
|
||||
/// allocate this segment and so should not try to deallocate or merge with others.
|
||||
/// This function can return a `std::ptr::null_mut()` when allocation fails (other values of
|
||||
/// the triple will be ignored).
|
||||
fn alloc(&self, size: usize) -> (*mut u8, usize, u32);
|
||||
|
||||
/// Remaps system memory region at `ptr` with size `oldsize` to a potential new location with
|
||||
/// size `newsize`. `can_move` indicates if the location is allowed to move to a completely new
|
||||
/// location, or that it is only allowed to change in size. Returns a pointer to the new
|
||||
/// location in memory.
|
||||
/// This function can return a `std::ptr::null_mut()` to signal an error.
|
||||
fn remap(&self, ptr: *mut u8, oldsize: usize, newsize: usize, can_move: bool) -> *mut u8;
|
||||
|
||||
/// Frees a part of a memory chunk. The original memory chunk starts at `ptr` with size `oldsize`
|
||||
/// and is turned into a memory region starting at the same address but with `newsize` bytes.
|
||||
/// Returns `true` iff the access memory region could be freed.
|
||||
fn free_part(&self, ptr: *mut u8, oldsize: usize, newsize: usize) -> bool;
|
||||
|
||||
/// Frees an entire memory region. Returns `true` iff the operation succeeded. When `false` is
|
||||
/// returned, the `dlmalloc` may re-use the location on future allocation requests
|
||||
fn free(&self, ptr: *mut u8, size: usize) -> bool;
|
||||
|
||||
/// Indicates if the system can release a part of memory. For the `flags` argument, see
|
||||
/// `Allocator::alloc`
|
||||
fn can_release_part(&self, flags: u32) -> bool;
|
||||
|
||||
/// Indicates whether newly allocated regions contain zeros.
|
||||
fn allocates_zeros(&self) -> bool;
|
||||
|
||||
/// Returns the page size. Must be a power of two
|
||||
fn page_size(&self) -> usize;
|
||||
}
|
||||
|
||||
/// An allocator instance
|
||||
///
|
||||
/// Instances of this type are used to allocate blocks of memory. For best
|
||||
/// results only use one of these. Currently doesn't implement `Drop` to release
|
||||
/// lingering memory back to the OS. That may happen eventually though!
|
||||
#[cfg(feature = "rust_api")]
|
||||
pub struct Dlmalloc<
|
||||
#[cfg(feature = "system")]
|
||||
A = System,
|
||||
#[cfg(not(feature = "system"))]
|
||||
A,
|
||||
>(dlmalloc::Dlmalloc<A>);
|
||||
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(all(feature = "system", target_family = "wasm"))] {
|
||||
#[path = "wasm.rs"]
|
||||
mod sys;
|
||||
} else if #[cfg(all(feature = "system", target_os = "windows"))] {
|
||||
#[path = "windows.rs"]
|
||||
mod sys;
|
||||
} else if #[cfg(all(feature = "system", target_os = "xous"))] {
|
||||
#[path = "xous.rs"]
|
||||
mod sys;
|
||||
} else if #[cfg(all(feature = "system", any(target_os = "linux", target_os = "macos", target_os = "redox")))] {
|
||||
#[path = "unix.rs"]
|
||||
mod sys;
|
||||
} else {
|
||||
#[path = "dummy.rs"]
|
||||
mod sys;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "system")]
|
||||
#[cfg(feature = "rust_api")]
|
||||
impl Dlmalloc<System> {
|
||||
/// Creates a new instance of an allocator
|
||||
pub const fn new() -> Dlmalloc<System> {
|
||||
Dlmalloc(dlmalloc::Dlmalloc::new(System::new()))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rust_api")]
|
||||
impl<A> Dlmalloc<A> {
|
||||
/// Creates a new instance of an allocator
|
||||
pub const fn new_with_allocator(sys_allocator: A) -> Dlmalloc<A> {
|
||||
Dlmalloc(dlmalloc::Dlmalloc::new(sys_allocator))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rust_api")]
|
||||
impl<A: Allocator> Dlmalloc<A> {
|
||||
/// Allocates `size` bytes with `align` align.
|
||||
///
|
||||
/// Returns a null pointer if allocation fails. Returns a valid pointer
|
||||
/// otherwise.
|
||||
///
|
||||
/// Safety and contracts are largely governed by the `GlobalAlloc::alloc`
|
||||
/// method contracts.
|
||||
#[inline]
|
||||
pub unsafe fn malloc(&mut self, size: usize, align: usize) -> *mut u8 {
|
||||
if align <= self.0.malloc_alignment() {
|
||||
self.0.malloc(size)
|
||||
} else {
|
||||
self.0.memalign(align, size)
|
||||
}
|
||||
}
|
||||
|
||||
/// Same as `malloc`, except if the allocation succeeds it's guaranteed to
|
||||
/// point to `size` bytes of zeros.
|
||||
#[inline]
|
||||
pub unsafe fn calloc(&mut self, size: usize, align: usize) -> *mut u8 {
|
||||
let ptr = self.malloc(size, align);
|
||||
if !ptr.is_null() && self.0.calloc_must_clear(ptr) {
|
||||
ptr::write_bytes(ptr, 0, size);
|
||||
}
|
||||
ptr
|
||||
}
|
||||
|
||||
/// Deallocates a `ptr` with `size` and `align` as the previous request used
|
||||
/// to allocate it.
|
||||
///
|
||||
/// Safety and contracts are largely governed by the `GlobalAlloc::dealloc`
|
||||
/// method contracts.
|
||||
#[inline]
|
||||
pub unsafe fn free(&mut self, ptr: *mut u8, size: usize, align: usize) {
|
||||
let _ = align;
|
||||
self.0.validate_size(ptr, size);
|
||||
self.0.free(ptr)
|
||||
}
|
||||
|
||||
/// Reallocates `ptr`, a previous allocation with `old_size` and
|
||||
/// `old_align`, to have `new_size` and the same alignment as before.
|
||||
///
|
||||
/// Returns a null pointer if the memory couldn't be reallocated, but `ptr`
|
||||
/// is still valid. Returns a valid pointer and frees `ptr` if the request
|
||||
/// is satisfied.
|
||||
///
|
||||
/// Safety and contracts are largely governed by the `GlobalAlloc::realloc`
|
||||
/// method contracts.
|
||||
#[inline]
|
||||
pub unsafe fn realloc(
|
||||
&mut self,
|
||||
ptr: *mut u8,
|
||||
old_size: usize,
|
||||
old_align: usize,
|
||||
new_size: usize,
|
||||
) -> *mut u8 {
|
||||
self.0.validate_size(ptr, old_size);
|
||||
|
||||
if old_align <= self.0.malloc_alignment() {
|
||||
self.0.realloc(ptr, new_size)
|
||||
} else {
|
||||
let res = self.malloc(new_size, old_align);
|
||||
if !res.is_null() {
|
||||
let size = cmp::min(old_size, new_size);
|
||||
ptr::copy_nonoverlapping(ptr, res, size);
|
||||
self.free(ptr, old_size, old_align);
|
||||
}
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
/// If possible, gives memory back to the system if there is unused memory
|
||||
/// at the high end of the malloc pool or in unused segments.
|
||||
///
|
||||
/// You can call this after freeing large blocks of memory to potentially
|
||||
/// reduce the system-level memory requirements of a program. However, it
|
||||
/// cannot guarantee to reduce memory. Under some allocation patterns, some
|
||||
/// large free blocks of memory will be locked between two used chunks, so
|
||||
/// they cannot be given back to the system.
|
||||
///
|
||||
/// The `pad` argument represents the amount of free trailing space to
|
||||
/// leave untrimmed. If this argument is zero, only the minimum amount of
|
||||
/// memory to maintain internal data structures will be left. Non-zero
|
||||
/// arguments can be supplied to maintain enough trailing space to service
|
||||
/// future expected allocations without having to re-obtain memory from the
|
||||
/// system.
|
||||
///
|
||||
/// Returns `true` if it actually released any memory, else `false`.
|
||||
pub unsafe fn trim(&mut self, pad: usize) -> bool {
|
||||
self.0.trim(pad)
|
||||
}
|
||||
|
||||
/// Releases all allocations in this allocator back to the system,
|
||||
/// consuming self and preventing further use.
|
||||
///
|
||||
/// Returns the number of bytes released to the system.
|
||||
pub unsafe fn destroy(self) -> usize {
|
||||
self.0.destroy()
|
||||
}
|
||||
|
||||
/// Get a reference the underlying [`Allocator`] that this `Dlmalloc` was
|
||||
/// constructed with.
|
||||
pub fn allocator(&self) -> &A {
|
||||
self.0.allocator()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
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();
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
use crate::Allocator;
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
use core::arch::wasm32 as wasm;
|
||||
#[cfg(target_arch = "wasm64")]
|
||||
use core::arch::wasm64 as wasm;
|
||||
use core::ptr;
|
||||
|
||||
/// System setting for Wasm
|
||||
pub struct System {
|
||||
_priv: (),
|
||||
}
|
||||
|
||||
impl System {
|
||||
pub const fn new() -> System {
|
||||
System { _priv: () }
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Allocator for System {
|
||||
fn alloc(&self, size: usize) -> (*mut u8, usize, u32) {
|
||||
let pages = size / self.page_size();
|
||||
let prev = wasm::memory_grow(0, pages);
|
||||
if prev == usize::max_value() {
|
||||
return (ptr::null_mut(), 0, 0);
|
||||
}
|
||||
(
|
||||
(prev * self.page_size()) as *mut u8,
|
||||
pages * self.page_size(),
|
||||
0,
|
||||
)
|
||||
}
|
||||
|
||||
fn remap(&self, _ptr: *mut u8, _oldsize: usize, _newsize: usize, _can_move: bool) -> *mut u8 {
|
||||
// TODO: I think this can be implemented near the end?
|
||||
ptr::null_mut()
|
||||
}
|
||||
|
||||
fn free_part(&self, _ptr: *mut u8, _oldsize: usize, _newsize: usize) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn free(&self, _ptr: *mut u8, _size: usize) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn can_release_part(&self, _flags: u32) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn allocates_zeros(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn page_size(&self) -> usize {
|
||||
64 * 1024
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "global")]
|
||||
pub fn acquire_global_lock() {
|
||||
// single threaded, no need!
|
||||
assert!(!cfg!(target_feature = "atomics"));
|
||||
}
|
||||
|
||||
#[cfg(feature = "global")]
|
||||
pub fn release_global_lock() {
|
||||
// single threaded, no need!
|
||||
assert!(!cfg!(target_feature = "atomics"));
|
||||
}
|
||||
|
||||
#[allow(missing_docs)]
|
||||
#[cfg(feature = "global")]
|
||||
pub unsafe fn enable_alloc_after_fork() {
|
||||
// single threaded, no need!
|
||||
assert!(!cfg!(target_feature = "atomics"));
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
use crate::Allocator;
|
||||
use core::mem::MaybeUninit;
|
||||
use core::ptr;
|
||||
use windows_sys::Win32::System::Memory::*;
|
||||
use windows_sys::Win32::System::SystemInformation::*;
|
||||
#[cfg(feature = "global")]
|
||||
use windows_sys::Win32::System::Threading::*;
|
||||
|
||||
pub struct System {
|
||||
_priv: (),
|
||||
}
|
||||
|
||||
impl System {
|
||||
pub const fn new() -> System {
|
||||
System { _priv: () }
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Allocator for System {
|
||||
fn alloc(&self, size: usize) -> (*mut u8, usize, u32) {
|
||||
let addr = unsafe {
|
||||
VirtualAlloc(
|
||||
ptr::null_mut(),
|
||||
size,
|
||||
MEM_RESERVE | MEM_COMMIT,
|
||||
PAGE_READWRITE,
|
||||
)
|
||||
};
|
||||
|
||||
if addr.is_null() {
|
||||
(ptr::null_mut(), 0, 0)
|
||||
} else {
|
||||
(addr.cast(), 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 { VirtualFree(ptr.add(newsize).cast(), oldsize - newsize, MEM_DECOMMIT) != 0 }
|
||||
}
|
||||
|
||||
fn free(&self, ptr: *mut u8, _size: usize) -> bool {
|
||||
unsafe { VirtualFree(ptr.cast(), 0, MEM_DECOMMIT) != 0 }
|
||||
}
|
||||
|
||||
fn can_release_part(&self, _flags: u32) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn allocates_zeros(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn page_size(&self) -> usize {
|
||||
unsafe {
|
||||
let mut info = MaybeUninit::uninit();
|
||||
GetSystemInfo(info.as_mut_ptr());
|
||||
info.assume_init_ref().dwPageSize as usize
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NB: `SRWLOCK_INIT` doesn't appear to be in `windows-sys`
|
||||
#[cfg(feature = "global")]
|
||||
static mut LOCK: SRWLOCK = SRWLOCK {
|
||||
Ptr: ptr::null_mut(),
|
||||
};
|
||||
|
||||
#[cfg(feature = "global")]
|
||||
pub fn acquire_global_lock() {
|
||||
unsafe {
|
||||
AcquireSRWLockExclusive(ptr::addr_of_mut!(LOCK));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "global")]
|
||||
pub fn release_global_lock() {
|
||||
unsafe {
|
||||
ReleaseSRWLockExclusive(ptr::addr_of_mut!(LOCK));
|
||||
}
|
||||
}
|
||||
|
||||
/// Not needed on Windows
|
||||
#[cfg(feature = "global")]
|
||||
pub unsafe fn enable_alloc_after_fork() {}
|
||||
@@ -0,0 +1,117 @@
|
||||
use crate::Allocator;
|
||||
use core::ptr;
|
||||
|
||||
pub struct System {
|
||||
_priv: (),
|
||||
}
|
||||
|
||||
impl System {
|
||||
pub const fn new() -> System {
|
||||
System { _priv: () }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "riscv32")]
|
||||
mod sys {
|
||||
use core::arch::asm;
|
||||
|
||||
pub fn increase_heap(length: usize) -> Result<(usize, usize), ()> {
|
||||
let syscall_no_increase_heap = 10usize;
|
||||
let memory_flags_read_write = 2usize | 4usize;
|
||||
|
||||
let mut a0 = syscall_no_increase_heap;
|
||||
let mut a1 = length;
|
||||
let mut a2 = memory_flags_read_write;
|
||||
|
||||
unsafe {
|
||||
asm!(
|
||||
"ecall",
|
||||
inlateout("a0") a0,
|
||||
inlateout("a1") a1,
|
||||
inlateout("a2") a2,
|
||||
out("a3") _,
|
||||
out("a4") _,
|
||||
out("a5") _,
|
||||
out("a6") _,
|
||||
out("a7") _,
|
||||
)
|
||||
};
|
||||
|
||||
let result = a0;
|
||||
let address = a1;
|
||||
let length = a2;
|
||||
|
||||
// 3 is the "MemoryRange" type, and the result is only valid
|
||||
// if we get nonzero address and length.
|
||||
if result == 3 && address != 0 && length != 0 {
|
||||
Ok((address, length))
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Allocator for System {
|
||||
/// Allocate an additional `size` bytes on the heap, and return a new
|
||||
/// chunk of memory, as well as the size of the allocation and some
|
||||
/// flags. Since flags are unused on this platform, they will always
|
||||
/// be `0`.
|
||||
fn alloc(&self, size: usize) -> (*mut u8, usize, u32) {
|
||||
let size = if size == 0 {
|
||||
4096
|
||||
} else if size & 4095 == 0 {
|
||||
size
|
||||
} else {
|
||||
size + (4096 - (size & 4095))
|
||||
};
|
||||
|
||||
if let Ok((address, length)) = sys::increase_heap(size) {
|
||||
let start = address - size + length;
|
||||
(start as *mut u8, size, 0)
|
||||
} else {
|
||||
(ptr::null_mut(), 0, 0)
|
||||
}
|
||||
}
|
||||
|
||||
fn remap(&self, _ptr: *mut u8, _oldsize: usize, _newsize: usize, _can_move: bool) -> *mut u8 {
|
||||
// TODO
|
||||
ptr::null_mut()
|
||||
}
|
||||
|
||||
fn free_part(&self, _ptr: *mut u8, _oldsize: usize, _newsize: usize) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn free(&self, _ptr: *mut u8, _size: usize) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn can_release_part(&self, _flags: u32) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn allocates_zeros(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn page_size(&self) -> usize {
|
||||
4 * 1024
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "global")]
|
||||
pub fn acquire_global_lock() {
|
||||
// global feature should not be enabled
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
#[cfg(feature = "global")]
|
||||
pub fn release_global_lock() {
|
||||
// global feature should not be enabled
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
#[cfg(feature = "global")]
|
||||
pub unsafe fn enable_alloc_after_fork() {
|
||||
// platform does not support `fork()` call
|
||||
}
|
||||
Reference in New Issue
Block a user