123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362 |
- use prelude::*;
- use core::mem;
- use {brk, sync};
- use bookkeeper::{self, Bookkeeper, Allocator};
- use shim::config;
- #[cfg(feature = "tls")]
- use tls;
- #[cfg(feature = "tls")]
- type ThreadLocalAllocator = MoveCell<Option<LazyInit<fn() -> LocalAllocator, LocalAllocator>>>;
- static GLOBAL_ALLOCATOR: sync::Mutex<LazyInit<fn() -> GlobalAllocator, GlobalAllocator>> =
- sync::Mutex::new(LazyInit::new(GlobalAllocator::init));
- #[cfg(feature = "tls")]
- tls! {
-
- static THREAD_ALLOCATOR: ThreadLocalAllocator = MoveCell::new(Some(LazyInit::new(LocalAllocator::init)));
- }
- macro_rules! get_allocator {
- (|$v:ident| $b:expr) => {{
-
- #[cfg(feature = "tls")]
- {
- THREAD_ALLOCATOR.with(|thread_alloc| {
- if let Some(mut thread_alloc_original) = thread_alloc.replace(None) {
- let res = {
-
- let $v = thread_alloc_original.get();
- $b
- };
-
- thread_alloc.replace(Some(thread_alloc_original));
- res
- } else {
-
-
- log!(WARNING, "Accessing the allocator after deinitialization of the local allocator.");
-
- log!(DEBUG, "Locking global allocator.");
- let mut guard = GLOBAL_ALLOCATOR.lock();
-
- let $v = guard.get();
- $b
- }
- })
- }
-
- #[cfg(not(feature = "tls"))]
- {
-
- log!(DEBUG, "Locking global allocator.");
- let mut guard = GLOBAL_ALLOCATOR.lock();
-
- let $v = guard.get();
- $b
- }
- }}
- }
- struct GlobalAllocator {
-
- inner: Bookkeeper,
- }
- impl GlobalAllocator {
-
- fn init() -> GlobalAllocator {
- log!(NOTE, "Initializing the global allocator.");
-
- let (aligner, initial_segment, excessive) =
- brk::lock().canonical_brk(4 * bookkeeper::EXTRA_ELEMENTS * mem::size_of::<Block>(), mem::align_of::<Block>());
-
- let mut res = GlobalAllocator {
- inner: Bookkeeper::new(unsafe {
-
- Vec::from_raw_parts(initial_segment, 0)
- }),
- };
-
- res.push(aligner);
- res.push(excessive);
- res
- }
- }
- derive_deref!(GlobalAllocator, Bookkeeper);
- impl Allocator for GlobalAllocator {
- #[inline]
- fn alloc_fresh(&mut self, size: usize, align: usize) -> Block {
-
- let (alignment_block, res, excessive) = brk::lock().canonical_brk(size, align);
-
-
-
- self.push(alignment_block);
- self.push(excessive);
- res
- }
- fn on_new_memory(&mut self) {
- if self.total_bytes() > config::OS_MEMTRIM_LIMIT {
-
-
- let block = self.pop().expect("The byte count on the global allocator is invalid.");
-
- if block.size() >= config::OS_MEMTRIM_WORTHY {
- log!(NOTE, "Memtrimming the global allocator.");
-
- if let Err(block) = brk::lock().release(block) {
-
-
- self.push(block);
- }
-
-
-
- } else {
- log!(WARNING, "Memtrimming for the global allocator failed.");
-
-
- self.push(block);
- }
- }
- }
- }
- #[cfg(feature = "tls")]
- pub struct LocalAllocator {
-
- inner: Bookkeeper,
- }
- #[cfg(feature = "tls")]
- impl LocalAllocator {
-
- fn init() -> LocalAllocator {
-
-
-
- extern fn dtor(alloc: &ThreadLocalAllocator) {
- log!(NOTE, "Deinitializing and freeing the local allocator to the global allocator.");
-
-
-
-
-
-
- let alloc = alloc.replace(None).expect("Thread-local allocator is already freed.");
-
- let mut global_alloc = GLOBAL_ALLOCATOR.lock();
- let global_alloc = global_alloc.get();
-
-
- alloc.into_inner().inner.for_each(move |block| global_alloc.free(block));
- }
- log!(NOTE, "Initializing the local allocator.");
-
- let initial_segment = GLOBAL_ALLOCATOR
- .lock()
- .get()
- .alloc(4 * bookkeeper::EXTRA_ELEMENTS * mem::size_of::<Block>(), mem::align_of::<Block>());
- unsafe {
-
-
- THREAD_ALLOCATOR.register_thread_destructor(dtor);
- LocalAllocator {
- inner: Bookkeeper::new(Vec::from_raw_parts(initial_segment, 0)),
- }
- }
- }
- }
- #[cfg(feature = "tls")]
- derive_deref!(LocalAllocator, Bookkeeper);
- #[cfg(feature = "tls")]
- impl Allocator for LocalAllocator {
- #[inline]
- fn alloc_fresh(&mut self, size: usize, align: usize) -> Block {
-
-
- GLOBAL_ALLOCATOR.lock().get().alloc(size, align)
- }
- #[inline]
- fn on_new_memory(&mut self) {
-
-
- if self.total_bytes() < config::FRAGMENTATION_SCALE * self.len()
- || self.total_bytes() > config::LOCAL_MEMTRIM_LIMIT {
-
- log!(NOTE, "Memtrimming the local allocator.");
-
- let mut global_alloc = GLOBAL_ALLOCATOR.lock();
- let global_alloc = global_alloc.get();
- while let Some(block) = self.pop() {
-
- global_alloc.free(block);
-
- if self.total_bytes() < config::LOCAL_MEMTRIM_STOP { break; }
- }
- }
- }
- }
- #[inline]
- pub fn alloc(size: usize, align: usize) -> *mut u8 {
- log!(CALL, "Allocating buffer of size {} (align {}).", size, align);
- get_allocator!(|alloc| *Pointer::from(alloc.alloc(size, align)))
- }
- #[inline]
- pub unsafe fn free(ptr: *mut u8, size: usize) {
- log!(CALL, "Freeing buffer of size {}.", size);
- get_allocator!(|alloc| alloc.free(Block::from_raw_parts(Pointer::new(ptr), size)))
- }
- #[inline]
- pub unsafe fn realloc(ptr: *mut u8, old_size: usize, size: usize, align: usize) -> *mut u8 {
- log!(CALL, "Reallocating buffer of size {} to new size {}.", old_size, size);
- get_allocator!(|alloc| {
- *Pointer::from(alloc.realloc(
- Block::from_raw_parts(Pointer::new(ptr), old_size),
- size,
- align
- ))
- })
- }
- #[inline]
- pub unsafe fn realloc_inplace(ptr: *mut u8, old_size: usize, size: usize) -> Result<(), ()> {
- log!(CALL, "Inplace reallocating buffer of size {} to new size {}.", old_size, size);
- get_allocator!(|alloc| {
- if alloc.realloc_inplace(
- Block::from_raw_parts(Pointer::new(ptr), old_size),
- size
- ).is_ok() {
- Ok(())
- } else {
- Err(())
- }
- })
- }
|