parity_util_mem/
allocators.rs

1// Copyright 2020 Parity Technologies
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
9//! default allocator management
10//! Features are:
11//! - windows:
12//! 	 - no features: default implementation from servo `heapsize` crate
13//! 	 - dlmalloc: default to `estimate_size`
14//! 	 - jemalloc: default windows allocator is used instead
15//! 	 - mimalloc: use mimallocator crate
16//! - arch x86:
17//! 	 - no features: use default alloc
18//! 	 - jemalloc: use tikv-jemallocator crate
19//! 	 - dlmalloc: default to `estimate_size`
20//! 	 - mimalloc: use mimallocator crate
21//! - arch x86/macos:
22//! 	 - no features: use default alloc, requires using `estimate_size`
23//! 	 - jemalloc: use tikv-jemallocator crate
24//! 	 - dlmalloc: default to `estimate_size`
25//! 	 - mimalloc: use mimallocator crate
26//! - arch wasm32:
27//! 	 - no features: default to `estimate_size`
28//! 	 - dlmalloc: default to `estimate_size`
29//! 	 - jemalloc: compile error
30//! 	 - mimalloc: compile error (until https://github.com/microsoft/mimalloc/pull/32 is merged)
31
32#[cfg(feature = "std")]
33use crate::malloc_size::MallocUnconditionalSizeOf;
34use crate::malloc_size::{MallocSizeOf, MallocSizeOfOps, VoidPtrToSizeFn};
35#[cfg(not(feature = "std"))]
36use core::ffi::c_void;
37#[cfg(feature = "std")]
38use std::os::raw::c_void;
39
40mod usable_size {
41
42	use super::*;
43
44	cfg_if::cfg_if! {
45
46		if #[cfg(any(
47			target_arch = "wasm32",
48			feature = "estimate-heapsize",
49			feature = "dlmalloc-global",
50		))] {
51
52			// do not try system allocator
53
54			/// Warning this is for compatibility only.
55			/// This function does panic: `estimate-heapsize` feature needs to be activated
56			/// to avoid this function call.
57			pub unsafe extern "C" fn malloc_usable_size(_ptr: *const c_void) -> usize {
58				unreachable!("estimate heapsize only")
59			}
60
61		} else if #[cfg(target_os = "windows")] {
62
63			use winapi::um::heapapi::{GetProcessHeap, HeapSize, HeapValidate};
64			use winapi::ctypes::c_void as winapi_c_void;
65
66			/// Get the size of a heap block.
67			/// Call windows allocator through `winapi` crate
68			pub unsafe extern "C" fn malloc_usable_size(mut ptr: *const c_void) -> usize {
69
70				let heap = GetProcessHeap();
71
72				if HeapValidate(heap, 0, ptr as *const winapi_c_void) == 0 {
73					ptr = *(ptr as *const *const c_void).offset(-1);
74				}
75
76				HeapSize(heap, 0, ptr as *const winapi_c_void) as usize
77			}
78
79		} else if #[cfg(feature = "jemalloc-global")] {
80
81			/// Use of jemalloc usable size C function through jemallocator crate call.
82			pub unsafe extern "C" fn malloc_usable_size(ptr: *const c_void) -> usize {
83				tikv_jemallocator::usable_size(ptr)
84			}
85
86		} else if #[cfg(feature = "mimalloc-global")] {
87
88			/// Use of mimalloc usable size C function through mimalloc_sys crate call.
89			pub unsafe extern "C" fn malloc_usable_size(ptr: *const c_void) -> usize {
90				// mimalloc doesn't actually mutate the value ptr points to,
91				// but requires a mut pointer in the API
92				libmimalloc_sys::mi_usable_size(ptr as *mut _)
93			}
94
95		} else if #[cfg(any(
96			target_os = "linux",
97			target_os = "android",
98			target_os = "freebsd",
99		))] {
100			// Linux/BSD call system allocator (currently malloc).
101			extern "C" {
102				pub fn malloc_usable_size(ptr: *const c_void) -> usize;
103			}
104
105		} else {
106			// default allocator for non linux or windows system use estimate
107			pub unsafe extern "C" fn malloc_usable_size(_ptr: *const c_void) -> usize {
108				unreachable!("estimate heapsize or feature allocator needed")
109			}
110
111		}
112
113	}
114
115	/// No enclosing function defined.
116	#[inline]
117	pub fn new_enclosing_size_fn() -> Option<VoidPtrToSizeFn> {
118		None
119	}
120}
121
122/// Get a new instance of a MallocSizeOfOps
123pub fn new_malloc_size_ops() -> MallocSizeOfOps {
124	MallocSizeOfOps::new(usable_size::malloc_usable_size, usable_size::new_enclosing_size_fn(), None)
125}
126
127/// Extension methods for `MallocSizeOf` trait, do not implement
128/// directly.
129/// It allows getting heapsize without exposing `MallocSizeOfOps`
130/// (a single default `MallocSizeOfOps` is used for each call).
131pub trait MallocSizeOfExt: MallocSizeOf {
132	/// Method to launch a heapsize measurement with a
133	/// fresh state.
134	fn malloc_size_of(&self) -> usize {
135		let mut ops = new_malloc_size_ops();
136		<Self as MallocSizeOf>::size_of(self, &mut ops)
137	}
138}
139
140impl<T: MallocSizeOf> MallocSizeOfExt for T {}
141
142#[cfg(feature = "std")]
143impl<T: MallocSizeOf> MallocSizeOf for std::sync::Arc<T> {
144	fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
145		self.unconditional_size_of(ops)
146	}
147}