separate safe/unsafe ctl apis, test everything

This commit is contained in:
gnzlbg 2018-11-09 10:25:44 +01:00 committed by gnzlbg
parent 7fb8e425e5
commit 1abc6d7ef8
17 changed files with 1056 additions and 1724 deletions

View File

@ -24,6 +24,7 @@ maintenance = { status = "actively-developed" }
[dependencies]
jemalloc-sys = { path = "../jemalloc-sys", version = "0.3.0" }
libc = { version = "0.2", default-features = false }
paste = { version = "0.1" }
[dev-dependencies]
jemallocator = { path = "..", version = "0.3.0" }

View File

@ -1,64 +1,27 @@
//! Arena operations.
use error::Result;
use keys::{Access, AsName, Mib};
use libc::c_uint;
const NARENAS: &[u8] = b"arenas.narenas\0";
/// Returns the current limit on the number of arenas.
///
/// # Examples
///
/// ```
/// extern crate jemallocator;
/// extern crate jemalloc_ctl;
///
/// #[global_allocator]
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
///
/// fn main() {
/// println!(
/// "number of arenas: {}",
/// jemalloc_ctl::arenas::narenas().unwrap()
/// );
/// }
/// ```
pub fn narenas() -> Result<c_uint> {
NARENAS.name().read()
}
/// A type providing access to the current limit on the number of arenas.
///
/// # Examples
///
/// ```
/// extern crate jemallocator;
/// extern crate jemalloc_ctl;
///
/// use jemalloc_ctl::arenas::NArenas;
///
/// #[global_allocator]
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
///
/// fn main() {
/// let narenas = NArenas::new().unwrap();
///
/// println!("number of arenas: {}", narenas.get().unwrap());
/// }
/// ```
#[derive(Copy, Clone)]
pub struct NArenas(Mib<[usize; 2]>);
impl NArenas {
/// Returns a new `NArenas`.
pub fn new() -> Result<Self> {
let mib = NARENAS.name().mib()?;
Ok(NArenas(mib))
}
/// Returns the maximum number of arenas.
pub fn get(self) -> Result<c_uint> {
self.0.read()
}
option! {
narenas[ str: b"arenas.narenas\0", non_str: 2 ] => libc::c_uint |
ops: r |
docs:
/// Current limit on the number of arenas.
///
/// # Examples
///
/// ```
/// # extern crate jemallocator;
/// # extern crate jemalloc_ctl;
/// #
/// # #[global_allocator]
/// # static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
/// #
/// # fn main() {
/// use jemalloc_ctl::arenas;
/// println!("number of arenas: {}", arenas::narenas::read().unwrap());
///
/// let arenas_mib = arenas::narenas::mib().unwrap();
/// println!("number of arenas: {}", arenas_mib.read().unwrap());
/// # }
/// ```
mib_docs: /// See [`narenas`].
}

View File

@ -1,181 +0,0 @@
//! Background thread operations.
use error::Result;
use keys::{Access, AsName, Mib};
const BACKGROUND_THREAD: &[u8] = b"background_thread\0";
/// Returns the state of internal background worker threads.
///
/// When enabled, background threads are created on demand (the number of
/// background threads will be no more than the number of CPUs or active
/// arenas). Threads run periodically and handle purging asynchronously.
///
/// ```
/// extern crate jemallocator;
/// extern crate jemalloc_ctl;
///
/// #[global_allocator]
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
///
/// fn main() {
/// # #[cfg(not(target_os = "macos"))]
/// println!(
/// "background_thread: {}",
/// jemalloc_ctl::background_thread().unwrap()
/// );
/// }
/// ```
pub fn background_thread() -> Result<bool> {
BACKGROUND_THREAD.name().read()
}
/// Enables or disables internal background worker threads.
///
/// When enabled, background threads are created on demand (the number of
/// background threads will be no more than the number of CPUs or active
/// arenas). Threads run periodically and handle purging asynchronously.
///
/// ```
/// extern crate jemallocator;
/// extern crate jemalloc_ctl;
///
/// #[global_allocator]
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
///
/// fn main() {
/// # #[cfg(not(target_os = "macos"))] {
/// jemalloc_ctl::set_background_thread(true).unwrap();
/// assert!(jemalloc_ctl::background_thread().unwrap());
/// # }
/// }
/// ```
pub fn set_background_thread(background_thread: bool) -> Result<()> {
BACKGROUND_THREAD.name().write(background_thread)
}
/// A type providing access to the state of internal background worker threads.
///
/// When enabled, background threads are created on demand (the number of
/// background threads will be no more than the number of CPUs or active
/// arenas). Threads run periodically and handle purging asynchronously.
///
/// ```
/// extern crate jemallocator;
/// extern crate jemalloc_ctl;
///
/// #[global_allocator]
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
///
/// fn main() {
/// # #[cfg(not(target_os = "macos"))] {
/// let mut background_thread
/// = jemalloc_ctl::BackgroundThread::new().unwrap();
/// background_thread.set(true).unwrap();
/// assert!(background_thread.get().unwrap());
/// # }
/// }
/// ```
#[derive(Copy, Clone)]
pub struct BackgroundThread(Mib<[usize; 1]>);
impl BackgroundThread {
/// Returns a new `BackgroundThread`.
pub fn new() -> Result<Self> {
let mib = BACKGROUND_THREAD.name().mib()?;
Ok(BackgroundThread(mib))
}
/// Returns the current background thread state.
pub fn get(self) -> Result<bool> {
self.0.read()
}
/// Sets the background thread state.
pub fn set(self, background_thread: bool) -> Result<()> {
self.0.write(background_thread)
}
}
const MAX_BACKGROUND_THREADS: &[u8] = b"max_background_threads\0";
/// Returns the maximum number of background threads that will be created.
///
/// ```
/// extern crate jemallocator;
/// extern crate jemalloc_ctl;
///
/// #[global_allocator]
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
///
/// fn main() {
/// # #[cfg(not(target_os = "macos"))]
/// println!(
/// "max_background_threads: {}",
/// jemalloc_ctl::max_background_threads().unwrap()
/// );
/// }
/// ```
pub fn max_background_threads() -> Result<usize> {
MAX_BACKGROUND_THREADS.name().read()
}
/// Sets the maximum number of background threads that will be created.
///
/// ```
/// extern crate jemallocator;
/// extern crate jemalloc_ctl;
///
/// #[global_allocator]
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
///
/// fn main() {
/// # #[cfg(not(target_os = "macos"))] {
/// jemalloc_ctl::set_max_background_threads(1).unwrap();
/// assert_eq!(jemalloc_ctl::max_background_threads().unwrap(), 1);
/// # }
/// }
/// ```
pub fn set_max_background_threads(max_background_threads: usize) -> Result<()> {
MAX_BACKGROUND_THREADS.name().write(max_background_threads)
}
/// A type providing access to the maximum number of background threads that
/// will be created.
///
/// ```
/// extern crate jemallocator;
/// extern crate jemalloc_ctl;
///
/// #[global_allocator]
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
///
/// fn main() {
/// # #[cfg(not(target_os = "macos"))] {
/// let mut max_background_threads
/// = jemalloc_ctl::MaxBackgroundThreads::new().unwrap();
/// max_background_threads.set(1).unwrap();
/// assert_eq!(max_background_threads.get().unwrap(), 1);
/// # }
/// }
/// ```
#[derive(Copy, Clone)]
pub struct MaxBackgroundThreads(Mib<[usize; 1]>);
impl MaxBackgroundThreads {
/// Returns a new `MaxBackgroundThreads`.
pub fn new() -> Result<Self> {
let mib = MAX_BACKGROUND_THREADS.name().mib()?;
Ok(MaxBackgroundThreads(mib))
}
/// Returns the current background thread limit.
pub fn get(self) -> Result<usize> {
self.0.read()
}
/// Sets the background thread limit.
pub fn set(self, max_background_threads: usize) -> Result<()> {
self.0.write(max_background_threads)
}
}

View File

@ -1,69 +1,28 @@
//! Information about the jemalloc compile-time configuration
use error::Result;
use keys::{Access, AsName, MibStr};
//! `jemalloc`'s build-time configuration.
const MALLOC_CONF: &[u8] = b"config.malloc_conf\0";
/// Returns the embeddec configure-time-specified run-time options config.
///
/// The string will be empty unless `--with-malloc-conf` was specified during
/// build configuration.
///
/// # Examples
///
/// ```
/// extern crate jemallocator;
/// extern crate jemalloc_ctl;
///
/// #[global_allocator]
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
///
/// fn main() {
/// println!(
/// "default malloc conf: {}",
/// jemalloc_ctl::config::malloc_conf().unwrap()
/// );
/// }
/// ```
pub fn malloc_conf() -> Result<&'static str> {
MALLOC_CONF.name().read()
}
/// A type providing access to the embedded configure-time-specified run-time
/// options config.
///
/// The string will be empty unless `--with-malloc-conf` was specified during
/// build configuration.
///
/// # Examples
///
/// ```
/// extern crate jemallocator;
/// extern crate jemalloc_ctl;
///
/// use jemalloc_ctl::config::MallocConf;
///
/// #[global_allocator]
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
///
/// fn main() {
/// let malloc_conf = MallocConf::new().unwrap();
///
/// println!("default malloc conf: {}", malloc_conf.get().unwrap());
/// }
/// ```
#[derive(Copy, Clone)]
pub struct MallocConf(MibStr<[usize; 2]>);
impl MallocConf {
/// Returns a new `MallocConf`.
pub fn new() -> Result<Self> {
let mib = MALLOC_CONF.name().mib_str()?;
Ok(MallocConf(mib))
}
/// Returns the embedded configure-time-specified run-time options config.
pub fn get(self) -> Result<&'static str> {
self.0.read()
}
option! {
malloc_conf[ str: b"config.malloc_conf\0", str: 2 ] => &'static str |
ops: r |
docs:
/// Default run-time options specified during `jemalloc`'s build configuration.
///
/// The string will be empty unless `--with-malloc-conf` was specified
/// during build configuration.
///
/// # Examples
///
/// ```
/// # extern crate jemallocator;
/// # extern crate jemalloc_ctl;
/// #
/// # #[global_allocator]
/// # static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
/// #
/// # fn main() {
/// use jemalloc_ctl::config;
/// let malloc_conf = config::malloc_conf::mib().unwrap();
/// println!("default malloc conf: {}", malloc_conf.read().unwrap());
/// # }
/// ```
mib_docs: /// See [`malloc_conf`].
}

View File

@ -1,76 +0,0 @@
//! Epoch access.
use error::Result;
use keys::{Access, AsName, Mib};
const EPOCH: &[u8] = b"epoch\0";
/// Advances the jemalloc epoch, returning it.
///
/// Many of the statistics tracked by jemalloc are cached. The epoch controls
/// when they are refreshed.
///
/// # Example
///
/// Advancing the epoch:
///
/// ```
/// extern crate jemallocator;
/// extern crate jemalloc_ctl;
///
/// #[global_allocator]
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
///
/// fn main() {
/// let a = jemalloc_ctl::epoch().unwrap();
/// let b = jemalloc_ctl::epoch().unwrap();
/// assert_eq!(a + 1, b);
/// }
/// ```
pub fn epoch() -> Result<u64> {
EPOCH.name().read_write(1)
}
/// A type providing access to the jemalloc epoch.
///
/// Many of the statistics tracked by jemalloc are cached. The epoch controls
/// when they are refreshed.
///
/// # Example
///
/// Advancing the epoch:
///
/// ```
/// extern crate jemallocator;
/// extern crate jemalloc_ctl;
///
/// use jemalloc_ctl::Epoch;
///
/// #[global_allocator]
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
///
/// fn main() {
/// let epoch = Epoch::new().unwrap();
///
/// let a = epoch.advance().unwrap();
/// let b = epoch.advance().unwrap();
/// assert_eq!(a + 1, b);
/// }
#[derive(Copy, Clone)]
pub struct Epoch(Mib<[usize; 1]>);
impl Epoch {
/// Returns a new `Epoch`.
pub fn new() -> Result<Self> {
let mib = EPOCH.name().mib()?;
Ok(Epoch(mib))
}
/// Advances the epoch, returning it.
///
/// The epoch advances by 1 every time it is advanced, so the value can be
/// used to determine if another thread triggered a referesh.
pub fn advance(self) -> Result<u64> {
self.0.read_write(1)
}
}

View File

@ -19,7 +19,7 @@ impl NonZeroT for i64 {
pub type NonZeroCInt = <c_int as NonZeroT>::T;
/// Error of the `jemalloc_sys::mallct`-family of functions.
/// Errors of the `jemalloc_sys::mallct`-family of functions.
///
/// The `jemalloc-sys` crate: `mallctl`, `mallctlnametomib`, and `mallctlbymib``
/// functions return `0` on success; otherwise they return an error value.

View File

@ -1,8 +1,8 @@
//! Key types to index the _MALLCTL NAMESPACE_.
//!
//! The [`Name`] and [`Mib`] types are provided as safe indices into the
//! _MALLCTL NAMESPACE_. These are constructed from slices via the [`AsName`]
//! and [`IntoMib`] traits. The [`Access`] trait provides provides safe access
//! The [`Name`] and [`Mib`]/[`MibStr`] types are provided as safe indices into
//! the _MALLCTL NAMESPACE_. These are constructed from null-terminated strings
//! via the [`AsName`] trait. The [`Access`] trait provides provides safe access
//! into the `_MALLCTL NAMESPACE_`.
//!
//! # Example
@ -57,7 +57,13 @@ impl AsName for [u8] {
"cannot create Name from non-null-terminated byte-string \"{}\"",
str::from_utf8(self).unwrap()
);
unsafe { &*(self as *const [u8] as *const Name) }
unsafe { &*(self as *const Self as *const Name) }
}
}
impl AsName for str {
fn name(&self) -> &Name {
self.as_bytes().name()
}
}
@ -108,6 +114,11 @@ impl Name {
_ => false,
}
}
/// Returns the name as null-terminated byte-string.
pub fn as_bytes(&self) -> &'static [u8] {
unsafe { &*(self as *const Self as *const [u8]) }
}
}
impl fmt::Debug for Name {
@ -124,16 +135,28 @@ impl fmt::Display for Name {
}
}
/// Management Information Base.
/// Management Information Base of a non-string value.
#[repr(transparent)]
#[derive(Copy, Clone, PartialEq, Debug, Default)]
pub struct Mib<T: MibArg>(T);
/// Management Information Base refering to a string value.
/// Management Information Base of a string value.
#[repr(transparent)]
#[derive(Copy, Clone, PartialEq, Debug, Default)]
pub struct MibStr<T: MibArg>(T);
impl<T: MibArg> AsRef<[usize]> for Mib<T> {
fn as_ref(&self) -> &[usize] {
self.0.as_ref()
}
}
impl<T: MibArg> AsMut<[usize]> for Mib<T> {
fn as_mut(&mut self) -> &mut [usize] {
self.0.as_mut()
}
}
impl<T: MibArg> ops::Index<usize> for Mib<T> {
type Output = usize;
fn index(&self, idx: usize) -> &Self::Output {
@ -167,31 +190,31 @@ pub trait Access<T> {
/// Write `value` at the key `self`.
fn write(&self, value: T) -> Result<()>;
/// Write `value` at the key `self` returning its previous value.
fn read_write(&self, value: T) -> Result<T>;
fn update(&self, value: T) -> Result<T>;
}
macro_rules! impl_access {
($id:ty) => {
impl<T: MibArg> Access<$id> for Mib<T> {
fn read(&self) -> Result<$id> {
unsafe { raw::get_mib(self.0.as_ref()) }
unsafe { raw::read_mib(self.0.as_ref()) }
}
fn write(&self, value: $id) -> Result<()> {
unsafe { raw::set_mib(self.0.as_ref(), value) }
unsafe { raw::write_mib(self.0.as_ref(), value) }
}
fn read_write(&self, value: $id) -> Result<$id> {
unsafe { raw::get_set_mib(self.0.as_ref(), value) }
fn update(&self, value: $id) -> Result<$id> {
unsafe { raw::update_mib(self.0.as_ref(), value) }
}
}
impl Access<$id> for Name {
fn read(&self) -> Result<$id> {
unsafe { raw::get(&self.0) }
unsafe { raw::read(&self.0) }
}
fn write(&self, value: $id) -> Result<()> {
unsafe { raw::set(&self.0, value) }
unsafe { raw::write(&self.0, value) }
}
fn read_write(&self, value: $id) -> Result<$id> {
unsafe { raw::get_set(&self.0, value) }
fn update(&self, value: $id) -> Result<$id> {
unsafe { raw::update(&self.0, value) }
}
}
};
@ -205,17 +228,17 @@ impl_access!(usize);
impl<T: MibArg> Access<bool> for Mib<T> {
fn read(&self) -> Result<bool> {
unsafe {
let v: u8 = raw::get_mib(self.0.as_ref())?;
let v: u8 = raw::read_mib(self.0.as_ref())?;
assert!(v == 0 || v == 1);
Ok(v == 1)
}
}
fn write(&self, value: bool) -> Result<()> {
unsafe { raw::set_mib(self.0.as_ref(), value) }
unsafe { raw::write_mib(self.0.as_ref(), value) }
}
fn read_write(&self, value: bool) -> Result<bool> {
fn update(&self, value: bool) -> Result<bool> {
unsafe {
let v: u8 = raw::get_set_mib(self.0.as_ref(), value as u8)?;
let v: u8 = raw::update_mib(self.0.as_ref(), value as u8)?;
Ok(v == 1)
}
}
@ -224,17 +247,17 @@ impl<T: MibArg> Access<bool> for Mib<T> {
impl Access<bool> for Name {
fn read(&self) -> Result<bool> {
unsafe {
let v: u8 = raw::get(&self.0)?;
let v: u8 = raw::read(&self.0)?;
assert!(v == 0 || v == 1);
Ok(v == 1)
}
}
fn write(&self, value: bool) -> Result<()> {
unsafe { raw::set(&self.0, value) }
unsafe { raw::write(&self.0, value) }
}
fn read_write(&self, value: bool) -> Result<bool> {
fn update(&self, value: bool) -> Result<bool> {
unsafe {
let v: u8 = raw::get_set(&self.0, value as u8)?;
let v: u8 = raw::update(&self.0, value as u8)?;
Ok(v == 1)
}
}
@ -244,15 +267,15 @@ impl<T: MibArg> Access<&'static [u8]> for MibStr<T> {
fn read(&self) -> Result<&'static [u8]> {
// this is safe because the only safe way to construct a `MibStr` is by
// validating that the key refers to a byte-string value
unsafe { raw::get_str_mib(self.0.as_ref()) }
unsafe { raw::read_str_mib(self.0.as_ref()) }
}
fn write(&self, value: &'static [u8]) -> Result<()> {
raw::set_str_mib(self.0.as_ref(), value)
raw::write_str_mib(self.0.as_ref(), value)
}
fn read_write(&self, value: &'static [u8]) -> Result<&'static [u8]> {
fn update(&self, value: &'static [u8]) -> Result<&'static [u8]> {
// this is safe because the only safe way to construct a `MibStr` is by
// validating that the key refers to a byte-string value
unsafe { raw::get_set_str_mib(self.0.as_ref(), value) }
unsafe { raw::update_str_mib(self.0.as_ref(), value) }
}
}
@ -264,7 +287,7 @@ impl Access<&'static [u8]> for Name {
self
);
// this is safe because the key refers to a byte string:
unsafe { raw::get_str(&self.0) }
unsafe { raw::read_str(&self.0) }
}
fn write(&self, value: &'static [u8]) -> Result<()> {
assert!(
@ -272,16 +295,16 @@ impl Access<&'static [u8]> for Name {
"the name \"{:?}\" does not refer to a byte string",
self
);
raw::set_str(&self.0, value)
raw::write_str(&self.0, value)
}
fn read_write(&self, value: &'static [u8]) -> Result<&'static [u8]> {
fn update(&self, value: &'static [u8]) -> Result<&'static [u8]> {
assert!(
self.value_type_str(),
"the name \"{:?}\" does not refer to a byte string",
self
);
// this is safe because the key refers to a byte string:
unsafe { raw::get_set_str(&self.0, value) }
unsafe { raw::update_str(&self.0, value) }
}
}
@ -289,16 +312,16 @@ impl<T: MibArg> Access<&'static str> for MibStr<T> {
fn read(&self) -> Result<&'static str> {
// this is safe because the only safe way to construct a `MibStr` is by
// validating that the key refers to a byte-string value
let s = unsafe { raw::get_str_mib(self.0.as_ref())? };
let s = unsafe { raw::read_str_mib(self.0.as_ref())? };
Ok(str::from_utf8(s).unwrap())
}
fn write(&self, value: &'static str) -> Result<()> {
raw::set_str_mib(self.0.as_ref(), value.as_bytes())
raw::write_str_mib(self.0.as_ref(), value.as_bytes())
}
fn read_write(&self, value: &'static str) -> Result<&'static str> {
fn update(&self, value: &'static str) -> Result<&'static str> {
// this is safe because the only safe way to construct a `MibStr` is by
// validating that the key refers to a byte-string value
let s = unsafe { raw::get_set_str_mib(self.0.as_ref(), value.as_bytes())? };
let s = unsafe { raw::update_str_mib(self.0.as_ref(), value.as_bytes())? };
Ok(str::from_utf8(s).unwrap())
}
}
@ -311,7 +334,7 @@ impl Access<&'static str> for Name {
self
);
// this is safe because the key refers to a byte string:
let s = unsafe { raw::get_str(&self.0)? };
let s = unsafe { raw::read_str(&self.0)? };
Ok(str::from_utf8(s).unwrap())
}
fn write(&self, value: &'static str) -> Result<()> {
@ -320,16 +343,16 @@ impl Access<&'static str> for Name {
"the name \"{:?}\" does not refer to a byte string",
self
);
raw::set_str(&self.0, value.as_bytes())
raw::write_str(&self.0, value.as_bytes())
}
fn read_write(&self, value: &'static str) -> Result<&'static str> {
fn update(&self, value: &'static str) -> Result<&'static str> {
assert!(
self.value_type_str(),
"the name \"{:?}\" does not refer to a byte string",
self
);
// this is safe because the key refers to a byte string:
let s = unsafe { raw::get_set_str(&self.0, value.as_bytes())? };
let s = unsafe { raw::update_str(&self.0, value.as_bytes())? };
Ok(str::from_utf8(s).unwrap())
}
}

View File

@ -9,10 +9,10 @@
//! ideal. Fortunately, `jemalloc` offers the ability to translate the string ahead of time into a
//! "Management Information Base" (MIB) to speed up future lookups.
//!
//! This crate provides both a function and a type for each `mallctl` operation. While the
//! function is more convenient, the type will be more efficient if the operation will be repeatedly
//! performed. Its constructor performs the MIB lookup, so the struct should be saved if the same
//! operation is going to be repeatedly performed.
//! This crate provides a type for each `mallctl` operation. Calling
//! `$op::{read(), write(x), update(x)}` on the type calls `mallctl` with the
//! string-based API. If the operation will be repeatedly performed, a MIB for
//! the operation can be obtained using `$op.mib()`.
//!
//! # Examples
//!
@ -24,6 +24,7 @@
//!
//! use std::thread;
//! use std::time::Duration;
//! use jemalloc_ctl::{stats, epoch};
//!
//! #[global_allocator]
//! static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
@ -31,10 +32,10 @@
//! fn main() {
//! loop {
//! // many statistics are cached and only updated when the epoch is advanced.
//! jemalloc_ctl::epoch().unwrap();
//! epoch::advance().unwrap();
//!
//! let allocated = jemalloc_ctl::stats::allocated().unwrap();
//! let resident = jemalloc_ctl::stats::resident().unwrap();
//! let allocated = stats::allocated::read().unwrap();
//! let resident = stats::resident::read().unwrap();
//! println!("{} bytes allocated/{} bytes resident", allocated, resident);
//! thread::sleep(Duration::from_secs(10));
//! }
@ -49,20 +50,21 @@
//!
//! use std::thread;
//! use std::time::Duration;
//! use jemalloc_ctl::{stats, epoch};
//!
//! #[global_allocator]
//! static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
//!
//! fn main() {
//! let epoch = jemalloc_ctl::Epoch::new().unwrap();
//! let allocated = jemalloc_ctl::stats::Allocated::new().unwrap();
//! let resident = jemalloc_ctl::stats::Resident::new().unwrap();
//! let e = epoch::mib().unwrap();
//! let allocated = stats::allocated::mib().unwrap();
//! let resident = stats::resident::mib().unwrap();
//! loop {
//! // many statistics are cached and only updated when the epoch is advanced.
//! epoch.advance().unwrap();
//! e.advance().unwrap();
//!
//! let allocated = allocated.get().unwrap();
//! let resident = resident.get().unwrap();
//! let allocated = allocated.read().unwrap();
//! let resident = resident.read().unwrap();
//! println!("{} bytes allocated/{} bytes resident", allocated, resident);
//! thread::sleep(Duration::from_secs(10));
//! }
@ -75,6 +77,7 @@
extern crate jemalloc_sys;
extern crate libc;
extern crate paste;
#[cfg(test)]
extern crate jemallocator;
@ -87,10 +90,11 @@ static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
use core as std;
use std::{fmt, mem, num, ops, ptr, result, slice, str};
#[macro_use]
mod macros;
pub mod arenas;
mod background_threads;
pub mod config;
mod epoch;
mod error;
mod keys;
pub mod opt;
@ -99,10 +103,143 @@ pub mod stats;
#[cfg(feature = "use_std")]
pub mod stats_print;
pub mod thread;
mod version;
pub use background_threads::*;
pub use epoch::*;
pub use error::{Error, Result};
pub use keys::{Access, AsName, Mib, MibStr, Name};
pub use version::*;
option! {
version[ str: b"version\0", str: 1 ] => &'static str |
ops: r |
docs:
/// `jemalloc` version string.
///
/// # Example
///
/// ```
/// # extern crate jemallocator;
/// # extern crate jemalloc_ctl;
/// #
/// # #[global_allocator]
/// # static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
/// #
/// # fn main() {
/// use jemalloc_ctl::version;
/// println!("jemalloc version {}", version::read().unwrap());
/// let version_mib = version::mib().unwrap();
/// println!("jemalloc version {}", version_mib.read().unwrap());
/// # }
/// ```
mib_docs: /// See [`version`].
}
option! {
background_thread[ str: b"background_thread\0", non_str: 1 ] => bool |
ops: r,w,u |
docs:
/// State of internal background worker threads.
///
/// When enabled, background threads are created on demand (the number of
/// background threads will be no more than the number of CPUs or active
/// arenas). Threads run periodically and handle purging asynchronously.
///
/// ```
/// # extern crate jemallocator;
/// # extern crate jemalloc_ctl;
/// #
/// # #[global_allocator]
/// # static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
/// #
/// # fn main() {
/// # #[cfg(not(target_os = "macos"))] {
/// #
/// use jemalloc_ctl::background_thread;
/// let s = background_thread::read().unwrap();
/// println!("background_threads enabled: {}", s);
/// let p = background_thread::update(!s).unwrap();
/// assert_eq!(p, s);
/// let s = background_thread::read().unwrap();
/// assert_ne!(p, s);
/// background_thread::write(!s).unwrap();
/// assert_eq!(p, s);
/// #
/// # } // #[cfg(..)]
/// # }
/// ```
mib_docs: /// See [`background_thread`].
}
option! {
max_background_threads[ str: b"max_background_threads\0", non_str: 1 ] => libc::size_t |
ops: r, w, u |
docs:
/// Maximum number of background threads that will be created.
///
/// ```
/// # extern crate jemallocator;
/// # extern crate jemalloc_ctl;
/// #
/// # #[global_allocator]
/// # static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
/// #
/// # fn main() {
/// # #[cfg(not(target_os = "macos"))] {
/// #
/// use jemalloc_ctl::max_background_threads;
/// let n = max_background_threads::read().unwrap();
/// println!("max_background_threads: {}", n);
/// max_background_threads::write(n + 1).unwrap();
/// assert_eq!(max_background_threads::read().unwrap(), n + 1);
/// #
/// # } // #[cfg(..)]
/// # }
/// ```
mib_docs: /// See [`max_background_threads`].
}
option! {
epoch[ str: b"epoch\0", non_str: 1 ] => u64 |
ops: r, w, u |
docs:
/// `jemalloc` epoch.
///
/// Many of the statistics tracked by `jemalloc` are cached. The epoch
/// controls when they are refreshed.
///
/// # Example
///
/// Advancing the epoch:
///
/// ```
/// # extern crate jemallocator;
/// # extern crate jemalloc_ctl;
/// #
/// # #[global_allocator]
/// # static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
/// #
/// # fn main() {
/// #
/// use jemalloc_ctl::epoch;
/// let e = epoch::mib().unwrap();
/// let a = e.advance().unwrap();
/// let b = e.advance().unwrap();
/// assert_eq!(a + 1, b);
///
/// let o = e.update(0).unwrap();
/// assert_eq!(o, e.read().unwrap());
/// # }
mib_docs: /// See [`epoch`].
}
impl epoch {
/// Advances the epoch returning its old value - see [`epoch`].
pub fn advance() -> ::error::Result<u64> {
Self::update(1)
}
}
impl epoch_mib {
/// Advances the epoch returning its old value - see [`epoch`].
pub fn advance(self) -> ::error::Result<u64> {
self.0.update(1)
}
}

210
jemalloc-ctl/src/macros.rs Normal file
View File

@ -0,0 +1,210 @@
//! Utility macros
macro_rules! types {
($id:ident[ str: $byte_string:expr, $mib:ty, $name_to_mib:ident ] |
docs: $(#[$doc:meta])*
mib_docs: $(#[$doc_mib:meta])*
) => {
paste::item! {
$(#[$doc])*
#[allow(non_camel_case_types)]
pub struct $id;
impl $id {
const NAME: &'static ::keys::Name = {
union U<'a> {
bytes: &'a [u8],
name: &'a ::keys::Name
}
unsafe { U { bytes: $byte_string }.name }
};
/// Returns Management Information Base (MIB)
///
/// This value can be used to access the key without doing string lookup.
pub fn mib() -> ::error::Result<[<$id _mib>]> {
Ok([<$id _mib>](Self::NAME.$name_to_mib()?))
}
/// Key [`Name`].
pub fn name() -> &'static ::keys::Name {
Self::NAME
}
}
$(#[$doc_mib])*
#[doc(hidden)]
#[repr(transparent)]
#[derive(Copy, Clone)]
#[allow(non_camel_case_types)]
pub struct [<$id _mib>](pub ::keys::$mib);
}
};
}
/// Read
macro_rules! r {
($id:ident => $ret_ty:ty) => {
paste::item! {
impl $id {
/// Reads value using string API.
pub fn read() -> ::error::Result<$ret_ty> {
use ::keys::Access;
Self::NAME.read()
}
}
impl [<$id _mib>] {
/// Reads value using MIB API.
pub fn read(self) -> ::error::Result<$ret_ty> {
use ::keys::Access;
self.0.read()
}
}
#[cfg(test)]
#[test]
fn [<$id _read_test>]() {
match stringify!($id) {
"background_thread" |
"max_background_threads"
if cfg!(target_os = "macos") => return,
_ => (),
}
let _ = $id::read().unwrap();
let mib = $id::mib().unwrap();
let _ = mib.read().unwrap();
}
}
};
}
/// Write
macro_rules! w {
($id:ident => $ret_ty:ty) => {
paste::item! {
impl $id {
/// Writes `value` using string API.
pub fn write(value: $ret_ty) -> ::error::Result<()> {
use ::keys::Access;
Self::NAME.write(value)
}
}
impl [<$id _mib>] {
/// Writes `value` using MIB API.
pub fn write(self, value: $ret_ty) -> ::error::Result<()> {
use ::keys::Access;
self.0.write(value)
}
}
#[cfg(test)]
#[test]
fn [<$id _write_test>]() {
match stringify!($id) {
"background_thread" |
"max_background_threads"
if cfg!(target_os = "macos") => return,
_ => (),
}
let _ = $id::write($ret_ty::default()).unwrap();
let mib = $id::mib().unwrap();
let _ = mib.write($ret_ty::default()).unwrap();
}
}
};
}
/// Update
macro_rules! u {
($id:ident => $ret_ty:ty) => {
paste::item! {
impl $id {
/// Updates key to `value` returning its old value using string API.
pub fn update(value: $ret_ty) -> ::error::Result<$ret_ty> {
use ::keys::Access;
Self::NAME.update(value)
}
}
impl [<$id _mib>] {
/// Updates key to `value` returning its old value using MIB API.
pub fn update(self, value: $ret_ty) -> ::error::Result<$ret_ty> {
use ::keys::Access;
self.0.update(value)
}
}
#[cfg(test)]
#[test]
fn [<$id _update_test>]() {
match stringify!($id) {
"background_thread" |
"max_background_threads"
if cfg!(target_os = "macos") => return,
_ => (),
}
let _ = $id::update($ret_ty::default()).unwrap();
let mib = $id::mib().unwrap();
let _ = mib.update($ret_ty::default()).unwrap();
}
}
};
}
/// Creates a new option
macro_rules! option {
($id:ident[ str: $byte_string:expr, $mib:ty, $name_to_mib:ident ] => $ret_ty:ty |
ops: $($ops:ident),* |
docs:
$(#[$doc:meta])*
mib_docs:
$(#[$doc_mib:meta])*
) => {
types! {
$id[ str: $byte_string, $mib, $name_to_mib ] |
docs: $(#[$doc])*
mib_docs: $(#[$doc_mib])*
}
$(
$ops!($id => $ret_ty);
)*
};
// Non-string option:
($id:ident[ str: $byte_string:expr, non_str: $mib_len:expr ] => $ret_ty:ty |
ops: $($ops:ident),* |
docs:
$(#[$doc:meta])*
mib_docs:
$(#[$doc_mib:meta])*
) => {
option! {
$id[ str: $byte_string, Mib<[usize; $mib_len]>, mib ] => $ret_ty |
ops: $($ops),* |
docs: $(#[$doc])*
mib_docs: $(#[$doc_mib])*
}
};
// String option:
($id:ident[ str: $byte_string:expr, str: $mib_len:expr ] => $ret_ty:ty |
ops: $($ops:ident),* |
docs:
$(#[$doc:meta])*
mib_docs:
$(#[$doc_mib:meta])*
) => {
option! {
$id[ str: $byte_string, MibStr<[usize; $mib_len]>, mib_str ] => $ret_ty |
ops: $($ops),* |
docs: $(#[$doc])*
mib_docs: $(#[$doc_mib])*
}
};
}

View File

@ -1,523 +1,241 @@
//! Information about the run-time `jemalloc` configuration.
//! `jemalloc`'s run-time configuration.
//!
//! These settings are controlled by the `MALLOC_CONF` environment variable.
use error::Result;
use keys::{Access, AsName, Mib, MibStr};
use libc::c_uint;
const ABORT: &[u8] = b"opt.abort\0";
/// Determines if `jemalloc` will call `abort(3)` on most warnings.
///
/// This is disabled by default unless `--enable-debug` was specified during build configuration.
///
/// # Examples
///
/// ```
/// extern crate jemallocator;
/// extern crate jemalloc_ctl;
///
/// #[global_allocator]
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
///
/// fn main() {
/// println!("abort on warning: {}", jemalloc_ctl::opt::abort().unwrap());
/// }
/// ```
pub fn abort() -> Result<bool> {
ABORT.name().read()
option! {
abort[ str: b"opt.abort\0", non_str: 2 ] => bool |
ops: r |
docs:
/// Whether `jemalloc` calls `abort(3)` on most warnings.
///
/// This is disabled by default unless `--enable-debug` was specified during
/// build configuration.
///
/// # Examples
///
/// ```
/// # extern crate jemallocator;
/// # extern crate jemalloc_ctl;
/// #
/// # #[global_allocator]
/// # static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
/// #
/// # fn main() {
/// use jemalloc_ctl::opt;
/// let abort = opt::abort::mib().unwrap();
/// println!("abort on warning: {}", abort.read().unwrap());
/// # }
/// ```
mib_docs: /// See [`abort`].
}
/// A type determining if `jemalloc` will call `abort(3)` on most warnings.
///
/// This is disabled by default unless `--enable-debug` was specified during build configuration.
///
/// # Examples
///
/// ```
/// extern crate jemallocator;
/// extern crate jemalloc_ctl;
///
/// use jemalloc_ctl::opt::Abort;
///
/// #[global_allocator]
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
///
/// fn main() {
/// let abort = Abort::new().unwrap();
///
/// println!("abort on warning: {}", abort.get().unwrap());
/// }
/// ```
#[derive(Copy, Clone)]
pub struct Abort(Mib<[usize; 2]>);
impl Abort {
/// Returns a new `Abort`.
pub fn new() -> Result<Self> {
let mib = ABORT.name().mib()?;
Ok(Abort(mib))
}
/// Returns the abort-on-warning behavior.
pub fn get(self) -> Result<bool> {
self.0.read()
}
option! {
dss[ str: b"opt.dss\0", str: 2 ] => &'static str |
ops: r |
docs:
/// The `dss` (`sbrk(2)`) allocation precedence as related to `mmap(2)`
/// allocation.
///
/// The following settings are supported if `sbrk(2)` is supported by the
/// operating system: "disabled", "primary", and "secondary"; otherwise only
/// "disabled" is supported. The default is "secondary" if `sbrk(2)` is
/// supported by the operating system; "disabled" otherwise.
///
/// # Examples
///
/// ```
/// # extern crate jemallocator;
/// # extern crate jemalloc_ctl;
/// #
/// # #[global_allocator]
/// # static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
/// #
/// # fn main() {
/// use jemalloc_ctl::opt;
/// let dss = opt::dss::read().unwrap();
/// println!("dss priority: {}", dss);
/// # }
/// ```
mib_docs: /// See [`dss`].
}
const DSS: &[u8] = b"opt.dss\0";
/// Returns the dss (`sbrk(2)`) allocation precedence as related to `mmap(2)` allocation.
///
/// The following settings are supported if `sbrk(2)` is supported by the operating system:
/// "disabled", "primary", and "secondary"; otherwise only "disabled" is supported. The default is
/// "secondary" if `sbrk(2)` is supported by the operating system; "disabled" otherwise.
///
/// # Examples
///
/// ```
/// extern crate jemallocator;
/// extern crate jemalloc_ctl;
///
/// #[global_allocator]
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
///
/// fn main() {
/// println!("dss priority: {}", jemalloc_ctl::opt::dss().unwrap());
/// }
/// ```
pub fn dss() -> Result<&'static str> {
DSS.name().read()
option! {
narenas[ str: b"opt.narenas\0", non_str: 2 ] => libc::c_uint |
ops: r |
docs:
/// Maximum number of arenas to use for automatic multiplexing of threads
/// and arenas.
///
/// The default is four times the number of CPUs, or one if there is a
/// single CPU.
///
/// # Examples
///
/// ```
/// # extern crate jemallocator;
/// # extern crate jemalloc_ctl;
/// #
/// # #[global_allocator]
/// # static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
/// #
/// # fn main() {
/// use jemalloc_ctl::opt;
/// let narenas = opt::narenas::read().unwrap();
/// println!("number of arenas: {}", narenas);
/// # }
/// ```
mib_docs: /// See [`narenas`].
}
/// A type providing access to the dss (`sbrk(2)`) allocation precedence as related to `mmap(2)`
/// allocation.
///
/// The following settings are supported if `sbrk(2)` is supported by the operating system:
/// "disabled", "primary", and "secondary"; otherwise only "disabled" is supported. The default is
/// "secondary" if `sbrk(2)` is supported by the operating system; "disabled" otherwise.
///
/// # Examples
///
/// ```
/// extern crate jemallocator;
/// extern crate jemalloc_ctl;
///
/// use jemalloc_ctl::opt::Dss;
///
/// #[global_allocator]
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
///
/// fn main() {
/// let dss = Dss::new().unwrap();
///
/// println!("dss priority: {}", dss.get().unwrap());
/// }
/// ```
#[derive(Copy, Clone)]
pub struct Dss(MibStr<[usize; 2]>);
impl Dss {
/// Returns a new `Dss`.
pub fn new() -> Result<Self> {
let mib = DSS.name().mib_str()?;
Ok(Dss(mib))
}
/// Returns the dss allocation precedence.
pub fn get(self) -> Result<&'static str> {
self.0.read()
}
option! {
junk[ str: b"opt.junk\0", str: 2 ] => &'static str |
ops: r |
docs:
/// `jemalloc`'s junk filling mode.
///
/// Requires `--enable-fill` to have been specified during build
/// configuration.
///
/// If set to "alloc", each byte of uninitialized allocated memory will be
/// set to `0x5a`. If set to "free", each byte of deallocated memory will be set
/// to `0x5a`. If set to "true", both allocated and deallocated memory will be
/// initialized, and if set to "false" junk filling will be disabled. This is
/// intended for debugging and will impact performance negatively.
///
/// The default is "false", unless `--enable-debug` was specified during
/// build configuration, in
/// which case the default is "true".
///
/// # Examples
///
/// ```
/// # extern crate jemallocator;
/// # extern crate jemalloc_ctl;
/// #
/// # #[global_allocator]
/// # static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
/// #
/// # fn main() {
/// use jemalloc_ctl::opt;
/// let junk = opt::junk::read().unwrap();
/// println!("junk filling: {}", junk);
/// # }
/// ```
mib_docs: /// See [`junk`].
}
const NARENAS: &[u8] = b"opt.narenas\0";
/// Returns the maximum number of arenas to use for automatic multiplexing of threads and arenas.
///
/// The default is four times the number of CPUs, or one if there is a single CPU.
///
/// # Examples
///
/// ```
/// extern crate jemallocator;
/// extern crate jemalloc_ctl;
///
/// #[global_allocator]
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
///
/// fn main() {
/// println!("number of arenas: {}", jemalloc_ctl::opt::narenas().unwrap());
/// }
/// ```
pub fn narenas() -> Result<c_uint> {
NARENAS.name().read()
option! {
zero[ str: b"opt.zero\0", non_str: 2 ] => bool |
ops: r |
docs:
/// `jemalloc`'s zeroing behavior.
///
/// Requires `--enable-fill` to have been specified during build
/// configuration.
///
/// If enabled, `jemalloc` will initialize each byte of uninitialized
/// allocated memory to 0. This is intended for debugging and will impact
/// performance negatively. It is disabled by default.
///
/// # Examples
///
/// ```
/// # extern crate jemallocator;
/// # extern crate jemalloc_ctl;
/// #
/// # #[global_allocator]
/// # static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
/// #
/// # fn main() {
/// use jemalloc_ctl::opt;
/// let zero = opt::zero::read().unwrap();
/// println!("zeroing: {}", zero);
/// # }
/// ```
mib_docs: /// See [`zero`].
}
/// A type providing access to the maximum number of arenas to use for automatic multiplexing of
/// threads and arenas.
///
/// The default is four times the number of CPUs, or one if there is a single CPU.
///
/// # Examples
///
/// ```
/// extern crate jemallocator;
/// extern crate jemalloc_ctl;
///
/// use jemalloc_ctl::opt::NArenas;
///
/// #[global_allocator]
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
///
/// fn main() {
/// let narenas = NArenas::new().unwrap();
///
/// println!("number of arenas: {}", narenas.get().unwrap());
/// }
/// ```
#[derive(Copy, Clone)]
pub struct NArenas(Mib<[usize; 2]>);
impl NArenas {
/// Returns a new `NArenas`.
pub fn new() -> Result<Self> {
let mib = NARENAS.name().mib()?;
Ok(NArenas(mib))
}
/// Returns the maximum number of arenas.
pub fn get(self) -> Result<c_uint> {
self.0.read()
}
option! {
tcache[ str: b"opt.tcache\0", non_str: 2 ] => bool |
ops: r |
docs:
/// Thread-local allocation caching behavior.
///
/// Thread-specific caching allows many allocations to be satisfied without
/// performing any thread synchronization, at the cost of increased memory
/// use. This is enabled by default.
///
/// # Examples
///
/// ```
/// # extern crate jemallocator;
/// # extern crate jemalloc_ctl;
/// #
/// # #[global_allocator]
/// # static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
/// #
/// # fn main() {
/// use jemalloc_ctl::opt;
/// let tcache = opt::tcache::read().unwrap();
/// println!("thread-local caching: {}", tcache);
/// # }
/// ```
mib_docs: /// See [`tcache`].
}
const JUNK: &[u8] = b"opt.junk\0";
/// Returns `jemalloc`'s junk filling mode.
///
/// Requires `--enable-fill` to have been specified during build configuration.
///
/// If set to "alloc", each byte of uninitialized allocated memory will be set to `0x5a`. If set to
/// "free", each byte of deallocated memory will be set to `0x5a`. If set to "true", both allocated
/// and deallocated memory will be initialized, and if set to "false" junk filling will be disabled.
/// This is intended for debugging and will impact performance negatively.
///
/// The default is "false", unless `--enable-debug` was specified during build configuration, in
/// which case the default is "true".
///
/// # Examples
///
/// ```
/// extern crate jemallocator;
/// extern crate jemalloc_ctl;
///
/// #[global_allocator]
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
///
/// fn main() {
/// println!("junk filling: {}", jemalloc_ctl::opt::junk().unwrap());
/// }
/// ```
pub fn junk() -> Result<&'static str> {
JUNK.name().read()
option! {
lg_tcache_max[ str: b"opt.lg_tcache_max\0", non_str: 2 ] => libc::size_t |
ops: r |
docs:
/// Maximum size class (log base 2) to cache in the thread-specific cache
/// (`tcache`).
///
/// At a minimum, all small size classes are cached, and at a maximum all
/// large size classes are cached. The default maximum is 32 KiB (2^15).
///
/// # Examples
///
/// ```
/// # extern crate jemallocator;
/// # extern crate jemalloc_ctl;
/// #
/// # #[global_allocator]
/// # static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
/// #
/// # fn main() {
/// use jemalloc_ctl::opt;
/// let lg_tcache_max = opt::lg_tcache_max::read().unwrap();
/// println!("max cached allocation size: {}", 1 << lg_tcache_max);
/// # }
/// ```
mib_docs: /// See [`lg_tcache_max`].
}
/// A type providing access to `jemalloc`'s junk filling mode.
///
/// Requires `--enable-fill` to have been specified during build configuration.
///
/// If set to "alloc", each byte of uninitialized allocated memory will be set to `0x5a`. If set to
/// "free", each byte of deallocated memory will be set to `0x5a`. If set to "true", both allocated
/// and deallocated memory will be initialized, and if set to "false" junk filling will be disabled.
/// This is intended for debugging and will impact performance negatively.
///
/// The default is "false", unless `--enable-debug` was specified during build configuration, in
/// which case the default is "true".
///
/// # Examples
///
/// ```
/// extern crate jemallocator;
/// extern crate jemalloc_ctl;
///
/// use jemalloc_ctl::opt::Junk;
///
/// #[global_allocator]
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
///
/// fn main() {
/// let junk = Junk::new().unwrap();
///
/// println!("junk filling: {}", junk.get().unwrap());
/// }
/// ```
#[derive(Copy, Clone)]
pub struct Junk(MibStr<[usize; 2]>);
impl Junk {
/// Returns a new `Junk`.
pub fn new() -> Result<Self> {
let mib = JUNK.name().mib_str()?;
Ok(Junk(mib))
}
/// Returns jemalloc's junk filling mode.
pub fn get(self) -> Result<&'static str> {
self.0.read()
}
}
const ZERO: &[u8] = b"opt.zero\0";
/// Returns jemalloc's zeroing behavior.
///
/// Requires `--enable-fill` to have been specified during build configuration.
///
/// If enabled, `jemalloc` will initialize each byte of uninitialized allocated memory to 0. This is
/// intended for debugging and will impact performance negatively. It is disabled by default.
///
/// # Examples
///
/// ```
/// extern crate jemallocator;
/// extern crate jemalloc_ctl;
///
/// #[global_allocator]
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
///
/// fn main() {
/// println!("zeroing: {}", jemalloc_ctl::opt::zero().unwrap());
/// }
/// ```
pub fn zero() -> Result<bool> {
ZERO.name().read()
}
/// A type providing access to jemalloc's zeroing behavior.
///
/// Requires `--enable-fill` to have been specified during build configuration.
///
/// If enabled, `jemalloc` will initialize each byte of uninitialized allocated memory to 0. This is
/// intended for debugging and will impact performance negatively. It is disabled by default.
///
/// # Examples
///
/// ```
/// extern crate jemallocator;
/// extern crate jemalloc_ctl;
///
/// use jemalloc_ctl::opt::Zero;
///
/// #[global_allocator]
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
///
/// fn main() {
/// let zero = Zero::new().unwrap();
///
/// println!("zeroing: {}", zero.get().unwrap());
/// }
/// ```
#[derive(Copy, Clone)]
pub struct Zero(Mib<[usize; 2]>);
impl Zero {
/// Returns a new `Zero`.
pub fn new() -> Result<Self> {
let mib = ZERO.name().mib()?;
Ok(Zero(mib))
}
/// Returns the `jemalloc` zeroing behavior.
pub fn get(self) -> Result<bool> {
self.0.read()
}
}
const TCACHE: &[u8] = b"opt.tcache\0";
/// Determines if thread-local allocation caching is enabled.
///
/// Thread-specific caching allows many allocations to be satisfied without performing any thread
/// synchronization, at the cost of increased memory use. This is enabled by default.
///
/// # Examples
///
/// ```
/// extern crate jemallocator;
/// extern crate jemalloc_ctl;
///
/// #[global_allocator]
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
///
/// fn main() {
/// println!("thread-local caching: {}", jemalloc_ctl::opt::tcache().unwrap());
/// }
/// ```
pub fn tcache() -> Result<bool> {
TCACHE.name().read()
}
/// A type providing access to thread-local allocation caching behavior.
///
/// Thread-specific caching allows many allocations to be satisfied without performing any thread
/// synchronization, at the cost of increased memory use. This is enabled by default.
///
/// # Examples
///
/// ```
/// extern crate jemallocator;
/// extern crate jemalloc_ctl;
///
/// use jemalloc_ctl::opt::Tcache;
///
/// #[global_allocator]
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
///
/// fn main() {
/// let tcache = Tcache::new().unwrap();
///
/// println!("thread-local caching: {}", tcache.get().unwrap());
/// }
/// ```
#[derive(Copy, Clone)]
pub struct Tcache(Mib<[usize; 2]>);
impl Tcache {
/// Returns a new `Tcache`.
pub fn new() -> Result<Self> {
let mib = TCACHE.name().mib()?;
Ok(Tcache(mib))
}
/// Returns the thread-local caching behavior.
pub fn get(self) -> Result<bool> {
self.0.read()
}
}
const LG_TCACHE_MAX: &[u8] = b"opt.lg_tcache_max\0";
/// Returns the maximum size class (log base 2) to cache in the thread-specific cache (tcache).
///
/// At a minimum, all small size classes are cached, and at a maximum all large size classes are
/// cached. The default maximum is 32 KiB (2^15).
///
/// # Examples
///
/// ```
/// extern crate jemallocator;
/// extern crate jemalloc_ctl;
///
/// #[global_allocator]
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
///
/// fn main() {
/// println!("max cached allocation size: {}", 1 << jemalloc_ctl::opt::lg_tcache_max().unwrap());
/// }
/// ```
pub fn lg_tcache_max() -> Result<usize> {
LG_TCACHE_MAX.name().read()
}
/// A type providing access to the maximum size class (log base 2) to cache in the thread-specific
/// cache (tcache).
///
/// At a minimum, all small size classes are cached, and at a maximum all large size classes are
/// cached. The default maximum is 32 KiB (2^15).
///
/// # Examples
///
/// ```
/// extern crate jemallocator;
/// extern crate jemalloc_ctl;
///
/// use jemalloc_ctl::opt::LgTcacheMax;
///
/// #[global_allocator]
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
///
/// fn main() {
/// let lg_tcache_max = LgTcacheMax::new().unwrap();
///
/// println!("max cached allocation size: {}", 1 << lg_tcache_max.get().unwrap());
/// }
/// ```
#[derive(Copy, Clone)]
pub struct LgTcacheMax(Mib<[usize; 2]>);
impl LgTcacheMax {
/// Returns a new `LgTcacheMax`.
pub fn new() -> Result<Self> {
let mib = LG_TCACHE_MAX.name().mib()?;
Ok(LgTcacheMax(mib))
}
/// Returns the maximum cached size class.
pub fn get(self) -> Result<usize> {
self.0.read()
}
}
const BACKGROUND_THREAD: &[u8] = b"opt.background_thread\0";
/// Returns whether `jemalloc` is initialized with background worker threads
/// enabled.
///
/// # Examples
///
/// ```
/// extern crate jemallocator;
/// extern crate jemalloc_ctl;
///
/// #[global_allocator]
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
///
/// fn main() {
/// println!(
/// "initialized with background threads enabled: {}",
/// jemalloc_ctl::opt::background_thread().unwrap()
/// );
/// }
/// ```
pub fn background_thread() -> Result<bool> {
BACKGROUND_THREAD.name().read()
}
/// A type determining if `jemalloc` will be initialized with background worker
/// threads enabled.
///
/// # Examples
///
/// ```
/// extern crate jemallocator;
/// extern crate jemalloc_ctl;
///
/// use jemalloc_ctl::opt::BackgroundThread;
///
/// #[global_allocator]
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
///
/// fn main() {
/// let background_thread = BackgroundThread::new().unwrap();
///
/// println!(
/// "initialized with background threads enabled: {}",
/// background_thread.get().unwrap()
/// );
/// }
/// ```
#[derive(Copy, Clone)]
pub struct BackgroundThread(Mib<[usize; 2]>);
impl BackgroundThread {
/// Returns a new `BackgroundThread`.
pub fn new() -> Result<Self> {
let mib = BACKGROUND_THREAD.name().mib()?;
Ok(BackgroundThread(mib))
}
/// Returns the background thread initialization behavior.
pub fn get(self) -> Result<bool> {
self.0.read()
}
option! {
background_thread[ str: b"opt.background_thread\0", non_str: 2 ] => bool |
ops: r |
docs:
/// `jemalloc`'s default initialization behavior for background threads.
///
/// `jemalloc` automatically spawns background worker threads on
/// initialization (first `jemalloc` call) if this option is enabled. By
/// default this option is disabled - `malloc_conf=background_thread:true`
/// changes its default.
///
/// # Examples
///
/// ```
/// # extern crate jemallocator;
/// # extern crate jemalloc_ctl;
/// #
/// # #[global_allocator]
/// # static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
/// #
/// # fn main() {
/// use jemalloc_ctl::opt;
/// let background_thread = opt::background_thread::read().unwrap();
/// println!("background threads since initialization: {}", background_thread);
/// # }
/// ```
mib_docs: /// See [`background_thread`].
}

View File

@ -1,4 +1,4 @@
//! Raw `malloctl` getter/setters
//! Raw `unsafe` access to the `malloctl` API.
use error::{cvt, Result};
use libc::c_char;
@ -31,11 +31,11 @@ use {mem, ptr, slice};
/// use libc::{c_uint, c_char};
/// unsafe {
/// let mut mib = [0; 4];
/// let nbins: c_uint = raw::get(b"arenas.nbins\0").unwrap();
/// let nbins: c_uint = raw::read(b"arenas.nbins\0").unwrap();
/// raw::name_to_mib(b"arenas.bin.0.size\0", &mut mib).unwrap();
/// for i in 0..4 {
/// mib[2] = i;
/// let bin_size: usize = raw::get_mib(&mut mib).unwrap();
/// let bin_size: usize = raw::read_mib(&mut mib).unwrap();
/// println!("arena bin {} has size {}", i, bin_size);
/// }
/// }
@ -67,7 +67,7 @@ pub fn name_to_mib(name: &[u8], mib: &mut [usize]) -> Result<()> {
/// invalid `T`, for example, by passing `T=bool` for a key returning `u8`. The
/// sizes of `bool` and `u8` match, but `bool` cannot represent all values that
/// `u8` can.
pub unsafe fn get_mib<T: Copy>(mib: &[usize]) -> Result<T> {
pub unsafe fn read_mib<T: Copy>(mib: &[usize]) -> Result<T> {
let mut value = MaybeUninit { init: () };
let mut len = mem::size_of::<T>();
cvt(jemalloc_sys::mallctlbymib(
@ -91,7 +91,7 @@ pub unsafe fn get_mib<T: Copy>(mib: &[usize]) -> Result<T> {
/// invalid `T`, for example, by passing `T=bool` for a key returning `u8`. The
/// sizes of `bool` and `u8` match, but `bool` cannot represent all values that
/// `u8` can.
pub unsafe fn get<T: Copy>(name: &[u8]) -> Result<T> {
pub unsafe fn read<T: Copy>(name: &[u8]) -> Result<T> {
validate_name(name);
let mut value = MaybeUninit { init: () };
@ -107,7 +107,7 @@ pub unsafe fn get<T: Copy>(name: &[u8]) -> Result<T> {
Ok(value.maybe_uninit)
}
/// Uses the MIB `mib` as key to the _MALLCTL NAMESPACE_ and sets its `value`.
/// Uses the MIB `mib` as key to the _MALLCTL NAMESPACE_ and writes its `value`.
///
/// The [`name_to_mib`] API translates a string of the key (e.g. `arenas.nbins`)
/// to a `mib` (Management Information Base).
@ -118,7 +118,7 @@ pub unsafe fn get<T: Copy>(name: &[u8]) -> Result<T> {
/// invalid `T`, for example, by passing `T=u8` for a key expecting `bool`. The
/// sizes of `bool` and `u8` match, but `bool` cannot represent all values that
/// `u8` can.
pub unsafe fn set_mib<T>(mib: &[usize], mut value: T) -> Result<()> {
pub unsafe fn write_mib<T>(mib: &[usize], mut value: T) -> Result<()> {
cvt(jemalloc_sys::mallctlbymib(
mib.as_ptr(),
mib.len(),
@ -130,7 +130,7 @@ pub unsafe fn set_mib<T>(mib: &[usize], mut value: T) -> Result<()> {
}
/// Uses the null-terminated string `name` as the key to the _MALLCTL NAMESPACE_
/// and sets it `value`
/// and writes it `value`
///
/// # Safety
///
@ -138,7 +138,7 @@ pub unsafe fn set_mib<T>(mib: &[usize], mut value: T) -> Result<()> {
/// invalid `T`, for example, by passing `T=u8` for a key expecting `bool`. The
/// sizes of `bool` and `u8` match, but `bool` cannot represent all values that
/// `u8` can.
pub unsafe fn set<T>(name: &[u8], mut value: T) -> Result<()> {
pub unsafe fn write<T>(name: &[u8], mut value: T) -> Result<()> {
validate_name(name);
cvt(jemalloc_sys::mallctl(
@ -150,7 +150,7 @@ pub unsafe fn set<T>(name: &[u8], mut value: T) -> Result<()> {
))
}
/// Uses the MIB `mib` as key to the _MALLCTL NAMESPACE_ and sets its `value`
/// Uses the MIB `mib` as key to the _MALLCTL NAMESPACE_ and writes its `value`
/// returning its previous value.
///
/// The [`name_to_mib`] API translates a string of the key (e.g. `arenas.nbins`)
@ -162,7 +162,7 @@ pub unsafe fn set<T>(name: &[u8], mut value: T) -> Result<()> {
/// invalid `T`, for example, by passing `T=u8` for a key expecting `bool`. The
/// sizes of `bool` and `u8` match, but `bool` cannot represent all values that
/// `u8` can.
pub unsafe fn get_set_mib<T>(mib: &[usize], mut value: T) -> Result<T> {
pub unsafe fn update_mib<T>(mib: &[usize], mut value: T) -> Result<T> {
let mut len = mem::size_of::<T>();
cvt(jemalloc_sys::mallctlbymib(
mib.as_ptr(),
@ -177,7 +177,7 @@ pub unsafe fn get_set_mib<T>(mib: &[usize], mut value: T) -> Result<T> {
}
/// Uses the null-terminated string `name` as key to the _MALLCTL NAMESPACE_ and
/// sets its `value` returning its previous value.
/// writes its `value` returning its previous value.
///
/// # Safety
///
@ -185,7 +185,7 @@ pub unsafe fn get_set_mib<T>(mib: &[usize], mut value: T) -> Result<T> {
/// invalid `T`, for example, by passing `T=u8` for a key expecting `bool`. The
/// sizes of `bool` and `u8` match, but `bool` cannot represent all values that
/// `u8` can.
pub unsafe fn get_set<T>(name: &[u8], mut value: T) -> Result<T> {
pub unsafe fn update<T>(name: &[u8], mut value: T) -> Result<T> {
validate_name(name);
let mut len = mem::size_of::<T>();
@ -225,12 +225,12 @@ pub unsafe fn get_set<T>(name: &[u8], mut value: T) -> Result<T> {
/// If the pointer is valid but it does not point to a null-terminated string,
/// looking for `\0` will read garbage and might end up reading out-of-bounds,
/// which is undefined behavior.
pub unsafe fn get_str_mib(mib: &[usize]) -> Result<&'static [u8]> {
let ptr: *const c_char = get_mib(mib)?;
pub unsafe fn read_str_mib(mib: &[usize]) -> Result<&'static [u8]> {
let ptr: *const c_char = read_mib(mib)?;
ptr2str(ptr)
}
/// Uses the MIB `mib` as key to the _MALLCTL NAMESPACE_ and sets its `value`.
/// Uses the MIB `mib` as key to the _MALLCTL NAMESPACE_ and writes its `value`.
///
/// The [`name_to_mib`] API translates a string of the key (e.g. `arenas.nbins`)
/// to a `mib` (Management Information Base).
@ -238,16 +238,16 @@ pub unsafe fn get_str_mib(mib: &[usize]) -> Result<&'static [u8]> {
/// # Panics
///
/// If `value` is not a non-empty null-terminated string.
pub fn set_str_mib(mib: &[usize], value: &'static [u8]) -> Result<()> {
pub fn write_str_mib(mib: &[usize], value: &'static [u8]) -> Result<()> {
assert!(!value.is_empty(), "value cannot be empty");
assert_eq!(*value.last().unwrap(), b'\0');
// This is safe because `value` will always point to a null-terminated
// string, which makes it safe for all key value types: pointers to
// null-terminated strings, pointers, pointer-sized integers, etc.
unsafe { set_mib(mib, value.as_ptr() as *const c_char) }
unsafe { write_mib(mib, value.as_ptr() as *const c_char) }
}
/// Uses the MIB `mib` as key to the _MALLCTL NAMESPACE_ and sets its `value`
/// Uses the MIB `mib` as key to the _MALLCTL NAMESPACE_ and writes its `value`
/// returning its previous value.
///
/// The [`name_to_mib`] API translates a string of the key (e.g. `arenas.nbins`)
@ -273,8 +273,8 @@ pub fn set_str_mib(mib: &[usize], value: &'static [u8]) -> Result<()> {
/// If the pointer is valid but it does not point to a null-terminated string,
/// looking for `\0` will read garbage and might end up reading out-of-bounds,
/// which is undefined behavior.
pub unsafe fn get_set_str_mib(mib: &[usize], value: &'static [u8]) -> Result<&'static [u8]> {
let ptr: *const c_char = get_set_mib(mib, value.as_ptr() as *const c_char)?;
pub unsafe fn update_str_mib(mib: &[usize], value: &'static [u8]) -> Result<&'static [u8]> {
let ptr: *const c_char = update_mib(mib, value.as_ptr() as *const c_char)?;
ptr2str(ptr)
}
@ -301,24 +301,24 @@ pub unsafe fn get_set_str_mib(mib: &[usize], value: &'static [u8]) -> Result<&'s
/// If the pointer is valid but it does not point to a null-terminated string,
/// looking for `\0` will read garbage and might end up reading out-of-bounds,
/// which is undefined behavior.
pub unsafe fn get_str(name: &[u8]) -> Result<&'static [u8]> {
let ptr: *const c_char = get(name)?;
pub unsafe fn read_str(name: &[u8]) -> Result<&'static [u8]> {
let ptr: *const c_char = read(name)?;
ptr2str(ptr)
}
/// Uses the null-terminated string `name` as key to the _MALLCTL NAMESPACE_ and
/// sets its `value`.
pub fn set_str(name: &[u8], value: &'static [u8]) -> Result<()> {
/// writes its `value`.
pub fn write_str(name: &[u8], value: &'static [u8]) -> Result<()> {
assert!(!value.is_empty(), "value cannot be empty");
assert_eq!(*value.last().unwrap(), b'\0');
// This is safe because `value` will always point to a null-terminated
// string, which makes it safe for all key value types: pointers to
// null-terminated strings, pointers, pointer-sized integers, etc.
unsafe { set(name, value.as_ptr() as *const c_char) }
unsafe { write(name, value.as_ptr() as *const c_char) }
}
/// Uses the null-terminated string `name` as key to the _MALLCTL NAMESPACE_ and
/// sets its `value` returning its previous value.
/// writes its `value` returning its previous value.
///
/// # Safety
///
@ -340,8 +340,8 @@ pub fn set_str(name: &[u8], value: &'static [u8]) -> Result<()> {
/// If the pointer is valid but it does not point to a null-terminated string,
/// looking for `\0` will read garbage and might end up reading out-of-bounds,
/// which is undefined behavior.
pub unsafe fn get_set_str(name: &[u8], value: &'static [u8]) -> Result<&'static [u8]> {
let ptr: *const c_char = get_set(name, value.as_ptr() as *const c_char)?;
pub unsafe fn update_str(name: &[u8], value: &'static [u8]) -> Result<&'static [u8]> {
let ptr: *const c_char = update(name, value.as_ptr() as *const c_char)?;
ptr2str(ptr)
}

View File

@ -1,515 +1,228 @@
//! Global allocator statistics.
//!
//! `jemalloc` tracks a wide variety of statistics. Many of them are cached, and only refreshed when
//! the jemalloc "epoch" is advanced. See the [`Epoch`] type for more information.
//!
//! [`Epoch`]: ../struct.Epoch.html
//! `jemalloc` tracks a wide variety of statistics. Many of them are cached, and
//! only refreshed when the `jemalloc` "epoch" is advanced. See the [`::epoch`] type
//! for more information.
use error::Result;
use keys::{Access, AsName, Mib};
const ALLOCATED: &[u8] = b"stats.allocated\0";
/// Returns the total number of bytes allocated by the application.
///
/// This statistic is cached, and is only refreshed when the epoch is advanced. See the [`epoch`]
/// function for more information.
///
/// This corresponds to `stats.allocated` in jemalloc's API.
///
/// # Examples
///
/// ```
/// extern crate jemallocator;
/// extern crate jemalloc_ctl;
///
/// #[global_allocator]
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
///
/// fn main() {
/// let a = jemalloc_ctl::stats::allocated().unwrap();
/// let _buf = vec![0; 1024 * 1024];
/// jemalloc_ctl::epoch().unwrap();
/// let b = jemalloc_ctl::stats::allocated().unwrap();
/// assert!(a < b);
/// }
/// ```
///
/// [`epoch`]: ../fn.epoch().html
pub fn allocated() -> Result<usize> {
ALLOCATED.name().read()
option! {
allocated[ str: b"stats.allocated\0", non_str: 2 ] => libc::size_t |
ops: r |
docs:
/// Total number of bytes allocated by the application.
///
/// This statistic is cached, and is only refreshed when the epoch is
/// advanced. See the [`::epoch`] type for more information.
///
/// This corresponds to `stats.allocated` in jemalloc's API.
///
/// # Examples
///
/// ```rust
/// # extern crate jemallocator;
/// # extern crate jemalloc_ctl;
/// #
/// # #[global_allocator]
/// # static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
/// #
/// # fn main() {
/// use jemalloc_ctl::{epoch, stats};
/// let e = epoch::mib().unwrap();
/// let allocated = stats::allocated::mib().unwrap();
///
/// let a = allocated.read().unwrap();
/// let _buf = vec![0; 1024 * 1024];
/// e.advance().unwrap();
/// let b = allocated.read().unwrap();
/// assert!(a < b);
/// # }
/// ```
mib_docs: /// See [`allocated`].
}
/// A type providing access to the total number of bytes allocated by the application.
///
/// This statistic is cached, and is only refreshed when the epoch is advanced. See the [`Epoch`]
/// type for more information.
///
/// This corresponds to `stats.allocated` in jemalloc's API.
///
/// # Examples
///
/// ```rust
/// extern crate jemallocator;
/// extern crate jemalloc_ctl;
///
/// use jemalloc_ctl::Epoch;
/// use jemalloc_ctl::stats::Allocated;
///
/// #[global_allocator]
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
///
/// fn main() {
/// let epoch = Epoch::new().unwrap();
/// let allocated = Allocated::new().unwrap();
///
/// let a = allocated.get().unwrap();
/// let _buf = vec![0; 1024 * 1024];
/// epoch.advance().unwrap();
/// let b = allocated.get().unwrap();
/// assert!(a < b);
/// }
/// ```
///
/// [`Epoch`]: ../struct.Epoch.html
#[derive(Copy, Clone)]
pub struct Allocated(Mib<[usize; 2]>);
impl Allocated {
/// Returns a new `Allocated`.
pub fn new() -> Result<Self> {
let mib = ALLOCATED.name().mib()?;
Ok(Allocated(mib))
}
/// Returns the total number of bytes allocated by the application.
pub fn get(self) -> Result<usize> {
self.0.read()
}
option! {
active[ str: b"stats.active\0", non_str: 2 ] => libc::size_t |
ops: r |
docs:
/// Total number of bytes in active pages allocated by the application.
///
/// This is a multiple of the page size, and greater than or equal to the
/// value returned by [`allocated`].
///
/// This statistic is cached, and is only refreshed when the epoch is
/// advanced. See the [`::epoch`] type for more information.
///
/// This corresponds to `stats.active` in jemalloc's API.
///
/// # Examples
///
/// ```rust
/// # extern crate jemallocator;
/// # extern crate jemalloc_ctl;
/// #
/// # #[global_allocator]
/// # static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
/// #
/// # fn main() {
/// use jemalloc_ctl::{epoch, stats};
/// let e = epoch::mib().unwrap();
/// let active = stats::active::mib().unwrap();
///
/// let a = active.read().unwrap();
/// let _buf = vec![0; 1024 * 1024];
/// e.advance().unwrap();
/// let b = active.read().unwrap();
/// assert!(a < b);
/// # }
/// ```
mib_docs: /// See [`active`].
}
const ACTIVE: &[u8] = b"stats.active\0";
/// Returns the total number of bytes in active pages allocated by the application.
///
/// This is a multiple of the page size, and is greater than or equal to the value returned by
/// [`allocated`].
///
/// This statistic is cached, and is only refreshed when the epoch is advanced. See the [`epoch`]
/// type for more information.
///
/// This corresponds to `stats.active` in jemalloc's API.
///
/// # Examples
///
/// ```
/// extern crate jemallocator;
/// extern crate jemalloc_ctl;
///
/// #[global_allocator]
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
///
/// fn main() {
/// let a = jemalloc_ctl::stats::active().unwrap();
/// let _buf = vec![0; 1024 * 1024];
/// jemalloc_ctl::epoch().unwrap();
/// let b = jemalloc_ctl::stats::active().unwrap();
/// assert!(a < b);
/// }
/// ```
///
/// [`epoch`]: ../fn.epoch().html
/// [`allocated`]: fn.allocated.hml
pub fn active() -> Result<usize> {
ACTIVE.name().read()
option! {
metadata[ str: b"stats.metadata\0", non_str: 2 ] => libc::size_t |
ops: r |
docs:
/// Total number of bytes dedicated to `jemalloc` metadata.
///
/// This statistic is cached, and is only refreshed when the epoch is
/// advanced. See the [`::epoch`] type for more information.
///
/// This corresponds to `stats.metadata` in jemalloc's API.
///
/// # Examples
///
/// ```rust
/// # extern crate jemallocator;
/// # extern crate jemalloc_ctl;
/// #
/// # #[global_allocator]
/// # static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
/// #
/// # fn main() {
/// use jemalloc_ctl::{epoch, stats};
/// let e = epoch::mib().unwrap();
/// let metadata = stats::metadata::mib().unwrap();
///
/// e.advance().unwrap();
/// let size = metadata.read().unwrap();
/// println!("{} bytes of jemalloc metadata", size);
/// # }
/// ```
mib_docs: /// See [`metadata`].
}
/// A type providing access to the total number of bytes in active pages allocated by the
/// application.
///
/// This is a multiple of the page size, and greater than or equal to the value returned by
/// [`Allocated`].
///
/// This statistic is cached, and is only refreshed when the epoch is advanced. See the [`Epoch`]
/// type for more information.
///
/// This corresponds to `stats.active` in jemalloc's API.
///
/// # Examples
///
/// ```rust
/// extern crate jemallocator;
/// extern crate jemalloc_ctl;
///
/// use jemalloc_ctl::Epoch;
/// use jemalloc_ctl::stats::Active;
///
/// #[global_allocator]
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
///
/// fn main() {
/// let epoch = Epoch::new().unwrap();
/// let active = Active::new().unwrap();
///
/// let a = active.get().unwrap();
/// let _buf = vec![0; 1024 * 1024];
/// epoch.advance().unwrap();
/// let b = active.get().unwrap();
/// assert!(a < b);
/// }
/// ```
///
/// [`Epoch`]: ../struct.Epoch.html
/// [`Allocated`]: struct.Allocated.html
#[derive(Copy, Clone)]
pub struct Active(Mib<[usize; 2]>);
impl Active {
/// Returns a new `Allocated`.
pub fn new() -> Result<Self> {
let mib = ACTIVE.name().mib()?;
Ok(Active(mib))
}
/// Returns the total number of bytes in active pages allocated by the application.
pub fn get(self) -> Result<usize> {
self.0.read()
}
option! {
resident[ str: b"stats.resident\0", non_str: 2 ] => libc::size_t |
ops: r |
docs:
/// Total number of bytes in physically resident data pages mapped by the
/// allocator.
///
/// This consists of all pages dedicated to allocator metadata, pages
/// backing active allocations, and unused dirty pages. It may overestimate
/// the true value because pages may not actually be physically resident if
/// they correspond to demand-zeroed virtual memory that has not yet been
/// touched. This is a multiple of the page size, and is larger than the
/// value returned by [`active`].
///
/// This statistic is cached, and is only refreshed when the epoch is
/// advanced. See the [`::epoch`] type for more information.
///
/// This corresponds to `stats.resident` in jemalloc's API.
///
/// # Examples
///
/// ```rust
/// # extern crate jemallocator;
/// # extern crate jemalloc_ctl;
/// #
/// # #[global_allocator]
/// # static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
/// #
/// # fn main() {
/// use jemalloc_ctl::{epoch, stats};
/// let e = epoch::mib().unwrap();
/// let resident = stats::resident::mib().unwrap();
///
/// e.advance().unwrap();
/// let size = resident.read().unwrap();
/// println!("{} bytes of total resident data", size);
/// # }
/// ```
mib_docs: /// See [`resident`].
}
const METADATA: &[u8] = b"stats.metadata\0";
/// Returns the total number of bytes dedicated to jemalloc metadata.
///
/// This statistic is cached, and is only refreshed when the epoch is advanced. See the [`epoch`]
/// function for more information.
///
/// This corresponds to `stats.metadata` in jemalloc's API.
///
/// # Examples
///
/// ```
/// extern crate jemallocator;
/// extern crate jemalloc_ctl;
///
/// #[global_allocator]
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
///
/// fn main() {
/// jemalloc_ctl::epoch().unwrap();
/// println!("{} bytes of jemalloc metadata", jemalloc_ctl::stats::metadata().unwrap());
/// }
/// ```
///
/// [`epoch`]: ../fn.epoch.html
pub fn metadata() -> Result<usize> {
METADATA.name().read()
option! {
mapped[ str: b"stats.mapped\0", non_str: 2 ] => libc::size_t |
ops: r |
docs:
/// Total number of bytes in active extents mapped by the allocator.
///
/// This does not include inactive extents, even those that contain unused
/// dirty pages, so there is no strict ordering between this and the value
/// returned by [`resident`]. This is a multiple of the page size, and is
/// larger than the value returned by [`active`].
///
/// This statistic is cached, and is only refreshed when the epoch is
/// advanced. See the [`::epoch`] type for more information.
///
/// This corresponds to `stats.mapped` in jemalloc's API.
///
/// # Examples
///
/// ```rust
/// # extern crate jemallocator;
/// # extern crate jemalloc_ctl;
/// #
/// # #[global_allocator]
/// # static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
/// #
/// # fn main() {
/// use jemalloc_ctl::{epoch, stats};
/// let e = epoch::mib().unwrap();
/// let mapped = stats::mapped::mib().unwrap();
///
/// e.advance().unwrap();
/// let size = mapped.read().unwrap();
/// println!("{} bytes of total mapped data", size);
/// # }
/// ```
mib_docs: /// See [`mapped`].
}
/// A type providing access to the total number of bytes dedicated to jemalloc metadata.
///
/// This statistic is cached, and is only refreshed when the epoch is advanced. See the [`Epoch`]
/// type for more information.
///
/// This corresponds to `stats.metadata` in jemalloc's API.
///
/// # Examples
///
/// ```rust
/// extern crate jemallocator;
/// extern crate jemalloc_ctl;
///
/// use jemalloc_ctl::Epoch;
/// use jemalloc_ctl::stats::Metadata;
///
/// #[global_allocator]
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
///
/// fn main() {
/// let epoch = Epoch::new().unwrap();
/// let metadata = Metadata::new().unwrap();
///
/// epoch.advance().unwrap();
/// let size = metadata.get().unwrap();
/// println!("{} bytes of jemalloc metadata", size);
/// }
/// ```
///
/// [`Epoch`]: ../struct.Epoch.html
#[derive(Copy, Clone)]
pub struct Metadata(Mib<[usize; 2]>);
impl Metadata {
/// Returns a new `Metadata`.
pub fn new() -> Result<Self> {
let mib = METADATA.name().mib()?;
Ok(Metadata(mib))
}
/// Returns the total number of bytes dedicated to jemalloc metadata.
pub fn get(self) -> Result<usize> {
self.0.read()
}
}
const RESIDENT: &[u8] = b"stats.resident\0";
/// Returns the total number of bytes in physically resident data pages mapped by the allocator.
///
/// This consists of all pages dedicated to allocator metadata, pages backing active allocations,
/// and unused dirty pages. It may overestimate the true value because pages may not actually be
/// physically resident if they correspond to demand-zeroed virtual memory that has not yet been
/// touched. This is a multiple of the page size, and is larger than the value returned by
/// [`active`].
///
/// This statistic is cached, and is only refreshed when the epoch is advanced. See the [`epoch`]
/// function for more information.
///
/// This corresponds to `stats.resident` in jemalloc's API.
///
/// # Examples
///
/// ```
/// extern crate jemallocator;
/// extern crate jemalloc_ctl;
///
/// #[global_allocator]
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
///
/// fn main() {
/// jemalloc_ctl::epoch().unwrap();
/// println!("{} bytes of total resident data", jemalloc_ctl::stats::resident().unwrap());
/// }
/// ```
///
/// [`epoch`]: ../fn.epoch.html
/// [`active`]: fn.active.html
pub fn resident() -> Result<usize> {
RESIDENT.name().read()
}
/// A type providing access to the total number of bytes in physically resident data pages mapped
/// by the allocator.
///
/// This consists of all pages dedicated to allocator metadata, pages backing active allocations,
/// and unused dirty pages. It may overestimate the true value because pages may not actually be
/// physically resident if they correspond to demand-zeroed virtual memory that has not yet been
/// touched. This is a multiple of the page size, and is larger than the value returned by
/// [`Active`].
///
/// This statistic is cached, and is only refreshed when the epoch is advanced. See the [`Epoch`]
/// type for more information.
///
/// This corresponds to `stats.resident` in jemalloc's API.
///
/// # Examples
///
/// ```rust
/// extern crate jemallocator;
/// extern crate jemalloc_ctl;
///
/// use jemalloc_ctl::Epoch;
/// use jemalloc_ctl::stats::Resident;
///
/// #[global_allocator]
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
///
/// fn main() {
/// let epoch = Epoch::new().unwrap();
/// let resident = Resident::new().unwrap();
///
/// epoch.advance().unwrap();
/// let size = resident.get().unwrap();
/// println!("{} bytes of total resident data", size);
/// }
/// ```
///
/// [`Epoch`]: ../struct.Epoch.html
/// [`Active`]: struct.Active.html
#[derive(Copy, Clone)]
pub struct Resident(Mib<[usize; 2]>);
impl Resident {
/// Returns a new `Resident`.
pub fn new() -> Result<Self> {
let mib = RESIDENT.name().mib()?;
Ok(Resident(mib))
}
/// Returns the total number of bytes in physically resident data pages mapped by the allocator.
pub fn get(self) -> Result<usize> {
self.0.read()
}
}
const MAPPED: &[u8] = b"stats.mapped\0";
/// Returns the total number of bytes in active extents mapped by the allocator.
///
/// This does not include inactive extents, even those taht contain unused dirty pages, so there
/// is no strict ordering between this and the value returned by [`resident`]. This is a
/// multiple of the page size, and is larger than the value returned by [`active`].
///
/// This statistic is cached, and is only refreshed when the epoch is advanced. See the [`epoch`]
/// type for more information.
///
/// This corresponds to `stats.mapped` in jemalloc's API.
///
/// # Examples
///
/// ```
/// extern crate jemallocator;
/// extern crate jemalloc_ctl;
///
/// #[global_allocator]
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
///
/// fn main() {
/// jemalloc_ctl::epoch().unwrap();
/// println!("{} bytes of total mapped data", jemalloc_ctl::stats::mapped().unwrap());
/// }
/// ```
///
/// [`epoch`]: ../fn.epoch.html
/// [`resident`]: fn.resident.html
/// [`active`]: fn.active.html
pub fn mapped() -> Result<usize> {
MAPPED.name().read()
}
/// A type providing access to the total number of bytes in active extents mapped by the allocator.
///
/// This does not include inactive extents, even those that contain unused dirty pages, so there
/// is no strict ordering between this and the value returned by [`Resident`]. This is a
/// multiple of the page size, and is larger than the value returned by [`Active`].
///
/// This statistic is cached, and is only refreshed when the epoch is advanced. See the [`Epoch`]
/// type for more information.
///
/// This corresponds to `stats.mapped` in jemalloc's API.
///
/// # Examples
///
/// ```rust
/// extern crate jemallocator;
/// extern crate jemalloc_ctl;
///
/// use jemalloc_ctl::Epoch;
/// use jemalloc_ctl::stats::Mapped;
///
/// #[global_allocator]
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
///
/// fn main() {
/// let epoch = Epoch::new().unwrap();
/// let mapped = Mapped::new().unwrap();
///
/// epoch.advance().unwrap();
/// let size = mapped.get().unwrap();
/// println!("{} bytes of total mapped data", size);
/// }
/// ```
///
/// [`Epoch`]: ../struct.Epoch.html
/// [`Resident`]: struct.Resident.html
/// [`Active`]: struct.Active.html
#[derive(Copy, Clone)]
pub struct Mapped(Mib<[usize; 2]>);
impl Mapped {
/// Returns a new `Mapped`.
pub fn new() -> Result<Self> {
let mib = MAPPED.name().mib()?;
Ok(Mapped(mib))
}
/// Returns the total number of bytes in active extents mapped by the allocator.
pub fn get(self) -> Result<usize> {
self.0.read()
}
}
const RETAINED: &[u8] = b"stats.retained\0";
/// Returns the total number of bytes in virtual memory mappings that were retained rather than being returned to the
/// operating system via e.g. `munmap(2)`.
///
/// Retained virtual memory is typically untouched, decommitted, or purged, so it has no strongly associated physical
/// memory. Retained memory is excluded from mapped memory statistics, e.g. [`mapped`].
///
/// This statistic is cached, and is only refreshed when the epoch is advanced. See the [`epoch`]
/// type for more information.
///
/// This corresponds to `stats.retained` in jemalloc's API.
///
/// # Examples
///
/// ```
/// extern crate jemallocator;
/// extern crate jemalloc_ctl;
///
/// #[global_allocator]
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
///
/// fn main() {
/// jemalloc_ctl::epoch().unwrap();
/// println!("{} bytes of total retained data", jemalloc_ctl::stats::retained().unwrap());
/// }
/// ```
///
/// [`epoch`]: ../fn.epoch.html
/// [`mapped`]: fn.mapped.html
pub fn retained() -> Result<usize> {
RETAINED.name().read()
}
/// A type providing access to the total number of bytes in virtual memory mappings that were retained rather than being
/// returned to the operating system via e.g. `munmap(2)`.
///
/// Retained virtual memory is typically untouched, decommitted, or purged, so it has no strongly associated physical
/// memory. Retained memory is excluded from mapped memory statistics, e.g. [`Mapped`].
///
/// This statistic is cached, and is only refreshed when the epoch is advanced. See the [`Epoch`]
/// type for more information.
///
/// This corresponds to `stats.retained` in jemalloc's API.
///
/// # Examples
///
/// ```rust
/// extern crate jemallocator;
/// extern crate jemalloc_ctl;
///
/// use jemalloc_ctl::Epoch;
/// use jemalloc_ctl::stats::Retained;
///
/// #[global_allocator]
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
///
/// fn main() {
/// let epoch = Epoch::new().unwrap();
/// let retained = Retained::new().unwrap();
///
/// epoch.advance().unwrap();
/// let size = retained.get().unwrap();
/// println!("{} bytes of total retained data", size);
/// }
/// ```
///
/// [`Epoch`]: ../struct.Epoch.html
/// [`Mapped`]: struct.Mapped.html
#[derive(Copy, Clone)]
pub struct Retained(Mib<[usize; 2]>);
impl Retained {
/// Returns a new `Retained`.
pub fn new() -> Result<Self> {
let mib = RETAINED.name().mib()?;
Ok(Retained(mib))
}
/// Returns the total number of bytes in virtual memory mappings that were retained.
pub fn get(self) -> Result<usize> {
self.0.read()
}
option! {
retained[ str: b"stats.retained\0", non_str: 2 ] => libc::size_t |
ops: r |
docs:
/// Total number of bytes in virtual memory mappings that were retained
/// rather than being returned to the operating system via e.g. `munmap(2)`.
///
/// Retained virtual memory is typically untouched, decommitted, or purged,
/// so it has no strongly associated physical memory. Retained memory is
/// excluded from mapped memory statistics, e.g. [`mapped`].
///
/// This statistic is cached, and is only refreshed when the epoch is
/// advanced. See the [`::epoch`] type for more information.
///
/// This corresponds to `stats.retained` in jemalloc's API.
///
/// # Examples
///
/// ```rust
/// # extern crate jemallocator;
/// # extern crate jemalloc_ctl;
/// #
/// # #[global_allocator]
/// # static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
/// #
/// # fn main() {
/// use jemalloc_ctl::{epoch, stats};
/// let e = epoch::mib().unwrap();
/// let retained = stats::retained::mib().unwrap();
///
/// e.advance().unwrap();
/// let size = retained.read().unwrap();
/// println!("{} bytes of total retained data", size);
/// # }
/// ```
mib_docs: /// See [`retained`].
}

View File

@ -1,188 +1,115 @@
//! Thread specific operations.
use error::Result;
use raw::{get, get_mib, name_to_mib};
use raw::{read, read_mib};
const ALLOCATEDP: &[u8] = b"thread.allocatedp\0";
/// Returns a thread-local pointer to the total number of bytes allocated by the current thread.
///
/// Unlike [`stats::allocated`], the value returned by this type is not the number of bytes
/// *currently* allocated, but rather the number of bytes that have *ever* been allocated by this
/// thread.
///
/// This function doesn't return the value directly, but actually a pointer to the value. This
/// allows for very fast repeated lookup, since there is no function call overhead. The pointer type
/// cannot be sent to other threads, but `allocated` can be called on different threads and will
/// return the appropriate pointer for each of them.
///
/// This corresponds to `thread.allocatedp` in jemalloc's API.
///
/// # Examples
///
/// ```
/// extern crate jemallocator;
/// extern crate jemalloc_ctl;
///
/// #[global_allocator]
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
///
/// fn main() {
/// let allocated = jemalloc_ctl::thread::allocatedp().unwrap();
///
/// let a = allocated.get();
/// let buf = vec![0; 1024 * 1024];
/// let b = allocated.get();
/// drop(buf);
/// let c = allocated.get();
///
/// assert!(a < b);
/// assert_eq!(b, c);
/// }
/// ```
pub fn allocatedp() -> Result<ThreadLocal<u64>> {
unsafe { get(ALLOCATEDP).map(ThreadLocal) }
option! {
allocatedp[ str: b"thread.allocatedp\0", non_str: 2 ] => *mut u64 |
ops: |
docs:
/// Access to the total number of bytes allocated by the current thread.
///
/// Unlike [`::stats::allocated`], the value returned by this type is not the
/// number of bytes *currently* allocated, but rather the number of bytes
/// that have *ever* been allocated by this thread.
///
/// The `read` method doesn't return the value directly, but actually a
/// pointer to the value. This allows for very fast repeated lookup, since
/// there is no function call overhead. The pointer type cannot be sent to
/// other threads, but `allocated::read` can be called on different threads
/// and will return the appropriate pointer for each of them.
///
/// # Example
///
/// ```
/// # extern crate jemallocator;
/// # extern crate jemalloc_ctl;
/// #
/// # #[global_allocator]
/// # static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
/// #
/// # fn main() {
/// use jemalloc_ctl::thread;
/// let allocated = thread::allocatedp::mib().unwrap();
/// let allocated = allocated.read().unwrap();
///
/// let a = allocated.get();
/// let buf = vec![0; 1024 * 1024];
/// let b = allocated.get();
/// drop( buf);
/// let c = allocated.get();
///
/// assert!(a < b);
/// assert_eq!(b, c);
/// # }
/// ```
mib_docs: /// See [`allocatedp`].
}
/// A type providing access to the total number of bytes allocated by the current thread.
///
/// Unlike [`stats::Allocated`], the value returned by this type is not the number of bytes
/// *currently* allocated, but rather the number of bytes that have *ever* been allocated by this
/// thread.
///
/// The `get` method doesn't return the value directly, but actually a pointer to the value. This
/// allows for very fast repeated lookup, since there is no function call overhead. The pointer type
/// cannot be sent to other threads, but `Allocated::get` can be called on different threads and
/// will return the appropriate pointer for each of them.
///
/// # Example
///
/// ```
/// extern crate jemallocator;
/// extern crate jemalloc_ctl;
///
/// use jemalloc_ctl::thread::AllocatedP;
///
/// #[global_allocator]
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
///
/// fn main() {
/// let allocated = AllocatedP::new().unwrap();
/// let allocated = allocated.get().unwrap();
///
/// let a = allocated.get();
/// let buf = vec![0; 1024 * 1024];
/// let b = allocated.get();
/// drop(buf);
/// let c = allocated.get();
///
/// assert!(a < b);
/// assert_eq!(b, c);
/// }
/// ```
///
/// [`stats::Allocated`]: ../stats/struct.Allocated.html
#[derive(Copy, Clone)]
pub struct AllocatedP([usize; 2]);
impl AllocatedP {
/// Returns a new `Allocated`.
pub fn new() -> Result<Self> {
let mut mib = [0; 2];
name_to_mib(ALLOCATEDP, &mut mib)?;
Ok(AllocatedP(mib))
}
/// Returns a thread-local pointer to the total number of bytes allocated by this thread.
pub fn get(&self) -> Result<ThreadLocal<u64>> {
unsafe { get_mib(&self.0).map(ThreadLocal) }
impl allocatedp {
/// Reads value using string API.
pub fn read() -> Result<ThreadLocal<u64>> {
unsafe { read(Self::name().as_bytes()).map(ThreadLocal) }
}
}
const DEALLOCATEDP: &[u8] = b"thread.deallocatedp\0";
/// Returns a pointer to the total number of bytes deallocated by the current thread.
///
/// This function doesn't return the value directly, but actually a pointer to the value. This
/// allows for very fast repeated lookup, since there is no function call overhead. The pointer type
/// cannot be sent to other threads, but `deallocatedp` can be called on different threads and will
/// return the appropriate pointer for each of them.
///
/// This corresponds to `thread.deallocatedp` in jemalloc's API.
///
/// # Examples
///
/// ```
/// extern crate jemallocator;
/// extern crate jemalloc_ctl;
///
/// #[global_allocator]
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
///
/// fn main() {
/// let deallocated = jemalloc_ctl::thread::deallocatedp().unwrap();
///
/// let a = deallocated.get();
/// let buf = vec![0; 1024 * 1024];
/// let b = deallocated.get();
/// drop(buf);
/// let c = deallocated.get();
///
/// assert_eq!(a, b);
/// assert!(b < c);
/// }
/// ```
pub fn deallocatedp() -> Result<ThreadLocal<u64>> {
unsafe { get(DEALLOCATEDP).map(ThreadLocal) }
impl allocatedp_mib {
/// Reads value using MIB API.
pub fn read(&self) -> Result<ThreadLocal<u64>> {
unsafe { read_mib(self.0.as_ref()).map(ThreadLocal) }
}
}
/// A type providing access to the total number of bytes deallocated by the current thread.
///
/// The `get` method doesn't return the value directly, but actually a pointer to the value. This
/// allows for very fast repeated lookup, since there is no function call overhead. The pointer type
/// cannot be sent to other threads, but `DeallocatedP::get` can be called on different threads and
/// will return the appropriate pointer for each of them.
///
/// # Example
///
/// ```
/// extern crate jemallocator;
/// extern crate jemalloc_ctl;
///
/// use jemalloc_ctl::thread::DeallocatedP;
///
/// #[global_allocator]
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
///
/// fn main() {
/// let deallocated = DeallocatedP::new().unwrap();
/// let deallocated = deallocated.get().unwrap();
///
/// let a = deallocated.get();
/// let buf = vec![0; 1024 * 1024];
/// let b = deallocated.get();
/// drop(buf);
/// let c = deallocated.get();
///
/// assert_eq!(a, b);
/// assert!(b < c);
/// }
/// ```
#[derive(Copy, Clone)]
pub struct DeallocatedP([usize; 2]);
option! {
deallocatedp[ str: b"thread.deallocatedp\0", non_str: 2 ] => *mut u64 |
ops: |
docs:
/// Access to the total number of bytes deallocated by the current thread.
///
/// The `read` method doesn't return the value directly, but actually a
/// pointer to the value. This allows for very fast repeated lookup, since
/// there is no function call overhead. The pointer type cannot be sent to
/// other threads, but [`deallocatedp::read`] can be called on different
/// threads and will return the appropriate pointer for each of them.
///
/// # Example
///
/// ```
/// # extern crate jemallocator;
/// # extern crate jemalloc_ctl;
/// #
/// # #[global_allocator]
/// # static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
/// #
/// # fn main() {
/// use jemalloc_ctl::thread;
/// let deallocated = thread::deallocatedp::mib().unwrap();
/// let deallocated = deallocated.read().unwrap();
///
/// let a = deallocated.get();
/// let buf = vec![0; 1024 * 1024];
/// let b = deallocated.get();
/// drop(buf);
/// let c = deallocated.get();
///
/// assert_eq!(a, b);
/// assert!(b < c);
/// # }
/// ```
mib_docs: /// See [`deallocatedp`].
}
impl DeallocatedP {
/// Returns a new `Deallocated`.
pub fn new() -> Result<Self> {
let mut mib = [0; 2];
name_to_mib(DEALLOCATEDP, &mut mib)?;
Ok(DeallocatedP(mib))
impl deallocatedp {
/// Reads value using string API.
pub fn read() -> Result<ThreadLocal<u64>> {
unsafe { read(Self::name().as_bytes()).map(ThreadLocal) }
}
}
/// Returns a thread-local pointer to the total number of bytes deallocated by this thread.
pub fn get(&self) -> Result<ThreadLocal<u64>> {
let ptr = unsafe { get_mib::<*mut u64>(&self.0)? };
Ok(ThreadLocal(ptr))
impl deallocatedp_mib {
/// Reads value using MIB API.
pub fn read(&self) -> Result<ThreadLocal<u64>> {
unsafe { read_mib(self.0.as_ref()).map(ThreadLocal) }
}
}
@ -190,6 +117,7 @@ impl DeallocatedP {
///
/// It is neither `Sync` nor `Send`.
// NB we need *const here specifically since it's !Sync + !Send
#[repr(transparent)]
#[derive(Copy, Clone)]
pub struct ThreadLocal<T>(*const T);

View File

@ -1,63 +0,0 @@
//! Version operations.
use error::Result;
use keys::{Access, AsName, MibStr};
const VERSION: &[u8] = b"version\0";
/// Returns the jemalloc version string.
///
/// # Note
///
/// The version of jemalloc currently shipped with the Rust distribution has a bogus version string.
///
/// # Example
///
/// ```
/// extern crate jemallocator;
/// extern crate jemalloc_ctl;
///
/// #[global_allocator]
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
///
/// fn main() {
/// println!("jemalloc version {}", jemalloc_ctl::version().unwrap());
/// }
/// ```
pub fn version() -> Result<&'static str> {
VERSION.name().read()
}
/// A type providing access to the jemalloc version string.
///
/// # Example
///
/// ```
/// extern crate jemallocator;
/// extern crate jemalloc_ctl;
///
/// use jemalloc_ctl::Version;
///
/// #[global_allocator]
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
///
/// fn main() {
/// let version = Version::new().unwrap();
///
/// println!("jemalloc version {}", version.get().unwrap());
/// }
#[derive(Copy, Clone)]
pub struct Version(MibStr<[usize; 1]>);
impl Version {
/// Returns a new `Version`.
pub fn new() -> Result<Self> {
let mib = VERSION.name().mib_str()?;
Ok(Version(mib))
}
/// Returns the jemalloc version string.
pub fn get(self) -> Result<&'static str> {
self.0.read()
}
}

View File

@ -11,7 +11,7 @@ static A: Jemalloc = Jemalloc;
// Returns true if background threads are enabled.
fn background_threads() -> bool {
jemalloc_ctl::opt::background_thread().unwrap()
jemalloc_ctl::opt::background_thread::read().unwrap()
}
#[test]

View File

@ -32,5 +32,5 @@ pub static malloc_conf: Option<&'static libc::c_char> = Some(unsafe {
#[test]
fn background_threads_enabled() {
// Background threads are unconditionally enabled at run-time by default.
assert_eq!(jemalloc_ctl::opt::background_thread().unwrap(), true);
assert_eq!(jemalloc_ctl::opt::background_thread::read().unwrap(), true);
}

View File

@ -2,7 +2,7 @@ extern crate jemalloc_ctl;
extern crate jemallocator;
extern crate libc;
use jemalloc_ctl::{AsName, Access};
use jemalloc_ctl::{Access, AsName};
use jemallocator::Jemalloc;
use std::alloc::{GlobalAlloc, Layout};
@ -21,33 +21,33 @@ fn smoke() {
#[test]
fn ctl_get_set() {
let epoch: u64 = b"epoch\0".name().read().unwrap();
let epoch: u64 = "epoch\0".name().read().unwrap();
assert!(epoch > 0);
b"epoch\0".name().write(epoch).unwrap();
"epoch\0".name().write(epoch).unwrap();
}
#[test]
#[should_panic]
fn ctl_panic_empty_get() {
let _ : u64 = b"".name().read().unwrap();
let _: u64 = "".name().read().unwrap();
}
#[test]
#[should_panic]
fn ctl_panic_empty_set() {
let epoch: u64 = b"epoch\0".name().read().unwrap();
b"".name().write(epoch).unwrap();
let epoch: u64 = "epoch\0".name().read().unwrap();
"".name().write(epoch).unwrap();
}
#[test]
#[should_panic]
fn ctl_panic_non_null_terminated_get() {
let _: u64 = b"epoch".name().read().unwrap();
let _: u64 = "epoch".name().read().unwrap();
}
#[test]
#[should_panic]
fn ctl_panic_non_null_terminated_set() {
let epoch: u64 = b"epoch\0".name().read().unwrap();
b"epoch".name().write(epoch).unwrap();
let epoch: u64 = "epoch\0".name().read().unwrap();
"epoch".name().write(epoch).unwrap();
}