make the API of jemalloc_ctl sound
This commit is contained in:
parent
034067f951
commit
3b40f07216
|
@ -1,8 +1,8 @@
|
|||
//! Arena operations.
|
||||
|
||||
use error::Result;
|
||||
use keys::{Access, IntoName, Mib};
|
||||
use libc::c_uint;
|
||||
use raw::{get, get_mib, name_to_mib};
|
||||
|
||||
const NARENAS: &[u8] = b"arenas.narenas\0";
|
||||
|
||||
|
@ -25,7 +25,7 @@ const NARENAS: &[u8] = b"arenas.narenas\0";
|
|||
/// }
|
||||
/// ```
|
||||
pub fn narenas() -> Result<c_uint> {
|
||||
get(NARENAS)
|
||||
NARENAS.name().read()
|
||||
}
|
||||
|
||||
/// A type providing access to the current limit on the number of arenas.
|
||||
|
@ -48,18 +48,17 @@ pub fn narenas() -> Result<c_uint> {
|
|||
/// }
|
||||
/// ```
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct NArenas([usize; 2]);
|
||||
pub struct NArenas(Mib<[usize; 2]>);
|
||||
|
||||
impl NArenas {
|
||||
/// Returns a new `NArenas`.
|
||||
pub fn new() -> Result<Self> {
|
||||
let mut mib = [0; 2];
|
||||
name_to_mib(NARENAS, &mut mib)?;
|
||||
let mib = NARENAS.name().mib()?;
|
||||
Ok(NArenas(mib))
|
||||
}
|
||||
|
||||
/// Returns the maximum number of arenas.
|
||||
pub fn get(self) -> Result<c_uint> {
|
||||
get_mib(&self.0)
|
||||
self.0.read()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
//! Background thread operations.
|
||||
|
||||
use error::Result;
|
||||
use raw::{get, get_mib, name_to_mib, set, set_mib};
|
||||
use keys::{Access, IntoName, Mib};
|
||||
|
||||
const BACKGROUND_THREAD: &[u8] = b"background_thread\0";
|
||||
|
||||
|
@ -27,7 +27,7 @@ const BACKGROUND_THREAD: &[u8] = b"background_thread\0";
|
|||
/// }
|
||||
/// ```
|
||||
pub fn background_thread() -> Result<bool> {
|
||||
get(BACKGROUND_THREAD)
|
||||
BACKGROUND_THREAD.name().read()
|
||||
}
|
||||
|
||||
/// Enables or disables internal background worker threads.
|
||||
|
@ -51,7 +51,7 @@ pub fn background_thread() -> Result<bool> {
|
|||
/// }
|
||||
/// ```
|
||||
pub fn set_background_thread(background_thread: bool) -> Result<()> {
|
||||
set(BACKGROUND_THREAD, background_thread)
|
||||
BACKGROUND_THREAD.name().write(background_thread)
|
||||
}
|
||||
|
||||
/// A type providing access to the state of internal background worker threads.
|
||||
|
@ -77,24 +77,23 @@ pub fn set_background_thread(background_thread: bool) -> Result<()> {
|
|||
/// }
|
||||
/// ```
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct BackgroundThread([usize; 1]);
|
||||
pub struct BackgroundThread(Mib<[usize; 1]>);
|
||||
|
||||
impl BackgroundThread {
|
||||
/// Returns a new `BackgroundThread`.
|
||||
pub fn new() -> Result<Self> {
|
||||
let mut mib = [0; 1];
|
||||
name_to_mib(BACKGROUND_THREAD, &mut mib)?;
|
||||
let mib = BACKGROUND_THREAD.name().mib()?;
|
||||
Ok(BackgroundThread(mib))
|
||||
}
|
||||
|
||||
/// Returns the current background thread state.
|
||||
pub fn get(self) -> Result<bool> {
|
||||
get_mib(&self.0)
|
||||
self.0.read()
|
||||
}
|
||||
|
||||
/// Sets the background thread state.
|
||||
pub fn set(self, background_thread: bool) -> Result<()> {
|
||||
set_mib(&self.0, background_thread)
|
||||
self.0.write(background_thread)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -118,7 +117,7 @@ const MAX_BACKGROUND_THREADS: &[u8] = b"max_background_threads\0";
|
|||
/// }
|
||||
/// ```
|
||||
pub fn max_background_threads() -> Result<usize> {
|
||||
get(MAX_BACKGROUND_THREADS)
|
||||
MAX_BACKGROUND_THREADS.name().read()
|
||||
}
|
||||
|
||||
/// Sets the maximum number of background threads that will be created.
|
||||
|
@ -138,7 +137,7 @@ pub fn max_background_threads() -> Result<usize> {
|
|||
/// }
|
||||
/// ```
|
||||
pub fn set_max_background_threads(max_background_threads: usize) -> Result<()> {
|
||||
set(MAX_BACKGROUND_THREADS, max_background_threads)
|
||||
MAX_BACKGROUND_THREADS.name().write(max_background_threads)
|
||||
}
|
||||
|
||||
/// A type providing access to the maximum number of background threads that
|
||||
|
@ -161,23 +160,22 @@ pub fn set_max_background_threads(max_background_threads: usize) -> Result<()> {
|
|||
/// }
|
||||
/// ```
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct MaxBackgroundThreads([usize; 1]);
|
||||
pub struct MaxBackgroundThreads(Mib<[usize; 1]>);
|
||||
|
||||
impl MaxBackgroundThreads {
|
||||
/// Returns a new `MaxBackgroundThreads`.
|
||||
pub fn new() -> Result<Self> {
|
||||
let mut mib = [0; 1];
|
||||
name_to_mib(MAX_BACKGROUND_THREADS, &mut mib)?;
|
||||
let mib = MAX_BACKGROUND_THREADS.name().mib()?;
|
||||
Ok(MaxBackgroundThreads(mib))
|
||||
}
|
||||
|
||||
/// Returns the current background thread limit.
|
||||
pub fn get(self) -> Result<usize> {
|
||||
get_mib(&self.0)
|
||||
self.0.read()
|
||||
}
|
||||
|
||||
/// Sets the background thread limit.
|
||||
pub fn set(self, max_background_threads: usize) -> Result<()> {
|
||||
set_mib(&self.0, max_background_threads)
|
||||
self.0.write(max_background_threads)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
//! Information about the jemalloc compile-time configuration
|
||||
use error::Result;
|
||||
use raw::{get_str, get_str_mib, name_to_mib};
|
||||
use keys::{Access, IntoName, MibStr};
|
||||
|
||||
const MALLOC_CONF: &[u8] = b"config.malloc_conf\0";
|
||||
|
||||
|
@ -26,7 +26,7 @@ const MALLOC_CONF: &[u8] = b"config.malloc_conf\0";
|
|||
/// }
|
||||
/// ```
|
||||
pub fn malloc_conf() -> Result<&'static str> {
|
||||
get_str(MALLOC_CONF)
|
||||
MALLOC_CONF.name().read()
|
||||
}
|
||||
|
||||
/// A type providing access to the embedded configure-time-specified run-time
|
||||
|
@ -53,18 +53,17 @@ pub fn malloc_conf() -> Result<&'static str> {
|
|||
/// }
|
||||
/// ```
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct MallocConf([usize; 2]);
|
||||
pub struct MallocConf(MibStr<[usize; 2]>);
|
||||
|
||||
impl MallocConf {
|
||||
/// Returns a new `MallocConf`.
|
||||
pub fn new() -> Result<Self> {
|
||||
let mut mib = [0; 2];
|
||||
name_to_mib(MALLOC_CONF, &mut mib)?;
|
||||
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> {
|
||||
get_str_mib(&self.0)
|
||||
self.0.read()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
//! Epoch access.
|
||||
|
||||
use error::Result;
|
||||
use raw::{get_set, get_set_mib, name_to_mib};
|
||||
use keys::{Access, IntoName, Mib};
|
||||
|
||||
const EPOCH: &[u8] = b"epoch\0";
|
||||
|
||||
|
@ -28,7 +28,7 @@ const EPOCH: &[u8] = b"epoch\0";
|
|||
/// }
|
||||
/// ```
|
||||
pub fn epoch() -> Result<u64> {
|
||||
get_set(EPOCH, 1)
|
||||
EPOCH.name().read_write(1)
|
||||
}
|
||||
|
||||
/// A type providing access to the jemalloc epoch.
|
||||
|
@ -57,13 +57,12 @@ pub fn epoch() -> Result<u64> {
|
|||
/// assert_eq!(a + 1, b);
|
||||
/// }
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Epoch([usize; 1]);
|
||||
pub struct Epoch(Mib<[usize; 1]>);
|
||||
|
||||
impl Epoch {
|
||||
/// Returns a new `Epoch`.
|
||||
pub fn new() -> Result<Self> {
|
||||
let mut mib = [0; 1];
|
||||
name_to_mib(EPOCH, &mut mib)?;
|
||||
let mib = EPOCH.name().mib()?;
|
||||
Ok(Epoch(mib))
|
||||
}
|
||||
|
||||
|
@ -72,6 +71,6 @@ impl Epoch {
|
|||
/// 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> {
|
||||
get_set_mib(&self.0, 1)
|
||||
self.0.read_write(1)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,417 @@
|
|||
//! 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 [`IntoName`]
|
||||
//! and [`IntoMib`] traits. The [`Access`] trait provides provides safe access
|
||||
//! into the `_MALLCTL NAMESPACE_`.
|
||||
//!
|
||||
//! # Example
|
||||
//!
|
||||
//! ```
|
||||
//! extern crate libc;
|
||||
//! extern crate jemallocator;
|
||||
//! extern crate jemalloc_ctl;
|
||||
//!
|
||||
//! #[global_allocator]
|
||||
//! static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
|
||||
//!
|
||||
//! fn main() {
|
||||
//! use jemalloc_ctl::{Access, IntoName, Name, Mib};
|
||||
//! use libc::{c_uint, c_char};
|
||||
//! let name = b"arenas.nbins\0".name();
|
||||
//! let nbins: c_uint = name.read().unwrap();
|
||||
//! let mut mib: Mib<[usize; 4]> = b"arenas.bin.0.size\0".name().mib().unwrap();
|
||||
//! for i in 0..4 {
|
||||
//! mib[2] = i;
|
||||
//! let bin_size: usize = mib.read().unwrap();
|
||||
//! println!("arena bin {} has size {}", i, bin_size);
|
||||
//! }
|
||||
//! }
|
||||
//! ```
|
||||
|
||||
use error::Result;
|
||||
use std::str;
|
||||
use {fmt, ops, raw};
|
||||
|
||||
/// A `Name` in the _MALLCTL NAMESPACE_.
|
||||
#[repr(transparent)]
|
||||
#[derive(PartialEq)]
|
||||
pub struct Name([u8]);
|
||||
|
||||
/// Converts a null-terminated byte-string into a [`Name`].
|
||||
pub trait IntoName {
|
||||
/// Converts a null-terminated byte-string into a [`Name`].
|
||||
fn name(&self) -> &Name;
|
||||
}
|
||||
|
||||
impl IntoName for [u8] {
|
||||
fn name(&self) -> &Name {
|
||||
use str;
|
||||
assert!(
|
||||
!self.is_empty(),
|
||||
"cannot create Name from empty byte-string"
|
||||
);
|
||||
assert_eq!(
|
||||
*self.last().unwrap(),
|
||||
b'\0',
|
||||
"cannot create Name from non-null-terminated byte-string \"{}\"",
|
||||
str::from_utf8(self).unwrap()
|
||||
);
|
||||
unsafe { &*(self as *const [u8] as *const Name) }
|
||||
}
|
||||
}
|
||||
|
||||
impl Name {
|
||||
/// Returns the [`Mib`] of `self`.
|
||||
pub fn mib<T: MibArg>(&self) -> Result<Mib<T>> {
|
||||
let mut mib: Mib<T> = Mib::default();
|
||||
raw::name_to_mib(&self.0, mib.0.as_mut())?;
|
||||
Ok(mib)
|
||||
}
|
||||
|
||||
/// Returns the [`MibStr`] of `self` which is a key whose value is a string.
|
||||
pub fn mib_str<T: MibArg>(&self) -> Result<MibStr<T>> {
|
||||
assert!(
|
||||
self.value_type_str(),
|
||||
"key \"{}\" does not refer to a string",
|
||||
self
|
||||
);
|
||||
let mut mib: MibStr<T> = MibStr::default();
|
||||
raw::name_to_mib(&self.0, mib.0.as_mut())?;
|
||||
Ok(mib)
|
||||
}
|
||||
|
||||
/// Returns `true` if `self` is a key in the _MALLCTL NAMESPCE_ referring to
|
||||
/// a null-terminated string.
|
||||
pub fn value_type_str(&self) -> bool {
|
||||
// remove the null-terminator:
|
||||
let name = self.0.split_at(self.0.len() - 1).0;
|
||||
if name.is_empty() {
|
||||
return false;
|
||||
}
|
||||
debug_assert_ne!(*name.last().unwrap(), b'\0');
|
||||
|
||||
match name {
|
||||
b"version"
|
||||
| b"config.malloc_conf"
|
||||
| b"opt.metadata_thp"
|
||||
| b"opt.dss"
|
||||
| b"opt.percpu_arena"
|
||||
| b"opt.stats_print_opts"
|
||||
| b"opt.junk"
|
||||
| b"opt.thp"
|
||||
| b"opt.prof_prefix"
|
||||
| b"thread.prof.name"
|
||||
| b"prof.dump" => true,
|
||||
v if v.starts_with(b"arena.") && v.ends_with(b".dss") => true,
|
||||
v if v.starts_with(b"stats.arenas.") && v.ends_with(b".dss") => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Name {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
use str;
|
||||
write!(f, "{}", str::from_utf8(&self.0).unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Name {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
use str;
|
||||
write!(f, "{}", str::from_utf8(&self.0).unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
/// Management Information Base.
|
||||
#[repr(transparent)]
|
||||
#[derive(Copy, Clone, PartialEq, Debug, Default)]
|
||||
pub struct Mib<T: MibArg>(T);
|
||||
|
||||
/// Management Information Base refering to a string value.
|
||||
#[repr(transparent)]
|
||||
#[derive(Copy, Clone, PartialEq, Debug, Default)]
|
||||
pub struct MibStr<T: MibArg>(T);
|
||||
|
||||
impl<T: MibArg> ops::Index<usize> for Mib<T> {
|
||||
type Output = usize;
|
||||
fn index(&self, idx: usize) -> &Self::Output {
|
||||
&self.0.as_ref()[idx]
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: MibArg> ops::IndexMut<usize> for Mib<T> {
|
||||
fn index_mut(&mut self, idx: usize) -> &mut Self::Output {
|
||||
&mut self.0.as_mut()[idx]
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: MibArg> ops::Index<usize> for MibStr<T> {
|
||||
type Output = usize;
|
||||
fn index(&self, idx: usize) -> &Self::Output {
|
||||
&self.0.as_ref()[idx]
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: MibArg> ops::IndexMut<usize> for MibStr<T> {
|
||||
fn index_mut(&mut self, idx: usize) -> &mut Self::Output {
|
||||
&mut self.0.as_mut()[idx]
|
||||
}
|
||||
}
|
||||
|
||||
/// Safe read access to the _MALLCTL NAMESPACE_.
|
||||
pub trait Access<T> {
|
||||
/// Read the key at `self`.
|
||||
fn read(&self) -> Result<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>;
|
||||
}
|
||||
|
||||
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()) }
|
||||
}
|
||||
fn write(&self, value: $id) -> Result<()> {
|
||||
unsafe { raw::set_mib(self.0.as_ref(), value) }
|
||||
}
|
||||
fn read_write(&self, value: $id) -> Result<$id> {
|
||||
unsafe { raw::get_set_mib(self.0.as_ref(), value) }
|
||||
}
|
||||
}
|
||||
impl Access<$id> for Name {
|
||||
fn read(&self) -> Result<$id> {
|
||||
unsafe { raw::get(&self.0) }
|
||||
}
|
||||
fn write(&self, value: $id) -> Result<()> {
|
||||
unsafe { raw::set(&self.0, value) }
|
||||
}
|
||||
fn read_write(&self, value: $id) -> Result<$id> {
|
||||
unsafe { raw::get_set(&self.0, value) }
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_access!(u32);
|
||||
impl_access!(u64);
|
||||
impl_access!(isize);
|
||||
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())?;
|
||||
assert!(v == 0 || v == 1);
|
||||
Ok(v == 1)
|
||||
}
|
||||
}
|
||||
fn write(&self, value: bool) -> Result<()> {
|
||||
unsafe { raw::set_mib(self.0.as_ref(), value) }
|
||||
}
|
||||
fn read_write(&self, value: bool) -> Result<bool> {
|
||||
unsafe {
|
||||
let v: u8 = raw::get_set_mib(self.0.as_ref(), value as u8)?;
|
||||
Ok(v == 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Access<bool> for Name {
|
||||
fn read(&self) -> Result<bool> {
|
||||
unsafe {
|
||||
let v: u8 = raw::get(&self.0)?;
|
||||
assert!(v == 0 || v == 1);
|
||||
Ok(v == 1)
|
||||
}
|
||||
}
|
||||
fn write(&self, value: bool) -> Result<()> {
|
||||
unsafe { raw::set(&self.0, value) }
|
||||
}
|
||||
fn read_write(&self, value: bool) -> Result<bool> {
|
||||
unsafe {
|
||||
let v: u8 = raw::get_set(&self.0, value as u8)?;
|
||||
Ok(v == 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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()) }
|
||||
}
|
||||
fn write(&self, value: &'static [u8]) -> Result<()> {
|
||||
raw::set_str_mib(self.0.as_ref(), value)
|
||||
}
|
||||
fn read_write(&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) }
|
||||
}
|
||||
}
|
||||
|
||||
impl Access<&'static [u8]> for Name {
|
||||
fn read(&self) -> 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_str(&self.0) }
|
||||
}
|
||||
fn write(&self, value: &'static [u8]) -> Result<()> {
|
||||
assert!(
|
||||
self.value_type_str(),
|
||||
"the name \"{:?}\" does not refer to a byte string",
|
||||
self
|
||||
);
|
||||
raw::set_str(&self.0, value)
|
||||
}
|
||||
fn read_write(&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) }
|
||||
}
|
||||
}
|
||||
|
||||
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())? };
|
||||
Ok(str::from_utf8(s).unwrap())
|
||||
}
|
||||
fn write(&self, value: &'static str) -> Result<()> {
|
||||
raw::set_str_mib(self.0.as_ref(), value.as_bytes())
|
||||
}
|
||||
fn read_write(&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())? };
|
||||
Ok(str::from_utf8(s).unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
impl Access<&'static str> for Name {
|
||||
fn read(&self) -> 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_str(&self.0)? };
|
||||
Ok(str::from_utf8(s).unwrap())
|
||||
}
|
||||
fn write(&self, value: &'static str) -> Result<()> {
|
||||
assert!(
|
||||
self.value_type_str(),
|
||||
"the name \"{:?}\" does not refer to a byte string",
|
||||
self
|
||||
);
|
||||
raw::set_str(&self.0, value.as_bytes())
|
||||
}
|
||||
fn read_write(&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())? };
|
||||
Ok(str::from_utf8(s).unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{Access, IntoName, Mib, MibStr};
|
||||
#[test]
|
||||
fn bool_rw() {
|
||||
let name = b"thread.tcache.enabled\0".name();
|
||||
let tcache: bool = name.read().unwrap();
|
||||
|
||||
let new_tcache = !tcache;
|
||||
|
||||
name.write(new_tcache).unwrap();
|
||||
|
||||
let mib: Mib<[usize; 3]> = name.mib().unwrap();
|
||||
let r: bool = mib.read().unwrap();
|
||||
assert_eq!(r, new_tcache);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn u32_r() {
|
||||
let name = b"arenas.bin.0.nregs\0".name();
|
||||
let v: u32 = name.read().unwrap();
|
||||
|
||||
let mib: Mib<[usize; 4]> = name.mib().unwrap();
|
||||
let r: u32 = mib.read().unwrap();
|
||||
assert_eq!(r, v);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn size_t_r() {
|
||||
let name = b"arenas.lextent.0.size\0".name();
|
||||
let v: libc::size_t = name.read().unwrap();
|
||||
|
||||
let mib: Mib<[usize; 4]> = name.mib().unwrap();
|
||||
let r: libc::size_t = mib.read().unwrap();
|
||||
assert_eq!(r, v);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ssize_t_rw() {
|
||||
let name = b"arenas.dirty_decay_ms\0".name();
|
||||
let v: libc::ssize_t = name.read().unwrap();
|
||||
name.write(v).unwrap();
|
||||
|
||||
let mib: Mib<[usize; 2]> = name.mib().unwrap();
|
||||
let r: libc::ssize_t = mib.read().unwrap();
|
||||
assert_eq!(r, v);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn u64_rw() {
|
||||
let name = b"epoch\0".name();
|
||||
let epoch: u64 = name.read().unwrap();
|
||||
name.write(epoch).unwrap();
|
||||
|
||||
let mib: Mib<[usize; 1]> = name.mib().unwrap();
|
||||
let epoch: u64 = mib.read().unwrap();
|
||||
mib.write(epoch).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn str_rw() {
|
||||
let name = b"arena.0.dss\0".name();
|
||||
let dss: &'static [u8] = name.read().unwrap();
|
||||
name.write(dss).unwrap();
|
||||
|
||||
let mib: MibStr<[usize; 3]> = name.mib_str().unwrap();
|
||||
let dss2: &'static [u8] = mib.read().unwrap();
|
||||
mib.write(dss2).unwrap();
|
||||
|
||||
assert_eq!(dss, dss2);
|
||||
}
|
||||
}
|
||||
|
||||
pub trait MibArg:
|
||||
Copy + Clone + PartialEq + Default + fmt::Debug + AsRef<[usize]> + AsMut<[usize]>
|
||||
{
|
||||
}
|
||||
impl<T> MibArg for T where
|
||||
T: Copy + Clone + PartialEq + Default + fmt::Debug + AsRef<[usize]> + AsMut<[usize]>
|
||||
{
|
||||
}
|
|
@ -71,6 +71,7 @@
|
|||
#![deny(missing_docs)]
|
||||
#![cfg_attr(not(feature = "use_std"), no_std)]
|
||||
#![cfg_attr(feature = "cargo-clippy", allow(clippy::stutter))]
|
||||
#![feature(coerce_unsized)]
|
||||
|
||||
extern crate jemalloc_sys;
|
||||
extern crate libc;
|
||||
|
@ -84,13 +85,14 @@ static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
|
|||
|
||||
#[cfg(not(feature = "use_std"))]
|
||||
use core as std;
|
||||
use std::{fmt, mem, ptr, result, slice};
|
||||
use std::{fmt, mem, ops, ptr, result, slice, str};
|
||||
|
||||
pub mod arenas;
|
||||
mod background_threads;
|
||||
pub mod config;
|
||||
mod epoch;
|
||||
mod error;
|
||||
mod keys;
|
||||
pub mod opt;
|
||||
pub mod raw;
|
||||
pub mod stats;
|
||||
|
@ -102,4 +104,5 @@ mod version;
|
|||
pub use background_threads::*;
|
||||
pub use epoch::*;
|
||||
pub use error::{Error, Result};
|
||||
pub use keys::{Access, IntoName, Mib, MibStr, Name};
|
||||
pub use version::*;
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
//!
|
||||
//! These settings are controlled by the `MALLOC_CONF` environment variable.
|
||||
use error::Result;
|
||||
use keys::{Access, IntoName, Mib, MibStr};
|
||||
use libc::c_uint;
|
||||
use raw::{get, get_mib, get_str, get_str_mib, name_to_mib};
|
||||
|
||||
const ABORT: &[u8] = b"opt.abort\0";
|
||||
|
||||
|
@ -25,7 +25,7 @@ const ABORT: &[u8] = b"opt.abort\0";
|
|||
/// }
|
||||
/// ```
|
||||
pub fn abort() -> Result<bool> {
|
||||
get(ABORT)
|
||||
ABORT.name().read()
|
||||
}
|
||||
|
||||
/// A type determining if `jemalloc` will call `abort(3)` on most warnings.
|
||||
|
@ -50,19 +50,18 @@ pub fn abort() -> Result<bool> {
|
|||
/// }
|
||||
/// ```
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Abort([usize; 2]);
|
||||
pub struct Abort(Mib<[usize; 2]>);
|
||||
|
||||
impl Abort {
|
||||
/// Returns a new `Abort`.
|
||||
pub fn new() -> Result<Self> {
|
||||
let mut mib = [0; 2];
|
||||
name_to_mib(ABORT, &mut mib)?;
|
||||
let mib = ABORT.name().mib()?;
|
||||
Ok(Abort(mib))
|
||||
}
|
||||
|
||||
/// Returns the abort-on-warning behavior.
|
||||
pub fn get(self) -> Result<bool> {
|
||||
get_mib(&self.0)
|
||||
self.0.read()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -88,7 +87,7 @@ const DSS: &[u8] = b"opt.dss\0";
|
|||
/// }
|
||||
/// ```
|
||||
pub fn dss() -> Result<&'static str> {
|
||||
get_str(DSS)
|
||||
DSS.name().read()
|
||||
}
|
||||
|
||||
/// A type providing access to the dss (`sbrk(2)`) allocation precedence as related to `mmap(2)`
|
||||
|
@ -116,19 +115,18 @@ pub fn dss() -> Result<&'static str> {
|
|||
/// }
|
||||
/// ```
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Dss([usize; 2]);
|
||||
pub struct Dss(MibStr<[usize; 2]>);
|
||||
|
||||
impl Dss {
|
||||
/// Returns a new `Dss`.
|
||||
pub fn new() -> Result<Self> {
|
||||
let mut mib = [0; 2];
|
||||
name_to_mib(DSS, &mut mib)?;
|
||||
let mib = DSS.name().mib_str()?;
|
||||
Ok(Dss(mib))
|
||||
}
|
||||
|
||||
/// Returns the dss allocation precedence.
|
||||
pub fn get(self) -> Result<&'static str> {
|
||||
get_str_mib(&self.0)
|
||||
self.0.read()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -152,7 +150,7 @@ const NARENAS: &[u8] = b"opt.narenas\0";
|
|||
/// }
|
||||
/// ```
|
||||
pub fn narenas() -> Result<c_uint> {
|
||||
get(NARENAS)
|
||||
NARENAS.name().read()
|
||||
}
|
||||
|
||||
/// A type providing access to the maximum number of arenas to use for automatic multiplexing of
|
||||
|
@ -178,19 +176,18 @@ pub fn narenas() -> Result<c_uint> {
|
|||
/// }
|
||||
/// ```
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct NArenas([usize; 2]);
|
||||
pub struct NArenas(Mib<[usize; 2]>);
|
||||
|
||||
impl NArenas {
|
||||
/// Returns a new `NArenas`.
|
||||
pub fn new() -> Result<Self> {
|
||||
let mut mib = [0; 2];
|
||||
name_to_mib(NARENAS, &mut mib)?;
|
||||
let mib = NARENAS.name().mib()?;
|
||||
Ok(NArenas(mib))
|
||||
}
|
||||
|
||||
/// Returns the maximum number of arenas.
|
||||
pub fn get(self) -> Result<c_uint> {
|
||||
get_mib(&self.0)
|
||||
self.0.read()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -222,7 +219,7 @@ const JUNK: &[u8] = b"opt.junk\0";
|
|||
/// }
|
||||
/// ```
|
||||
pub fn junk() -> Result<&'static str> {
|
||||
get_str(JUNK)
|
||||
JUNK.name().read()
|
||||
}
|
||||
|
||||
/// A type providing access to `jemalloc`'s junk filling mode.
|
||||
|
@ -255,19 +252,18 @@ pub fn junk() -> Result<&'static str> {
|
|||
/// }
|
||||
/// ```
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Junk([usize; 2]);
|
||||
pub struct Junk(MibStr<[usize; 2]>);
|
||||
|
||||
impl Junk {
|
||||
/// Returns a new `Junk`.
|
||||
pub fn new() -> Result<Self> {
|
||||
let mut mib = [0; 2];
|
||||
name_to_mib(JUNK, &mut mib)?;
|
||||
let mib = JUNK.name().mib_str()?;
|
||||
Ok(Junk(mib))
|
||||
}
|
||||
|
||||
/// Returns jemalloc's junk filling mode.
|
||||
pub fn get(self) -> Result<&'static str> {
|
||||
get_str_mib(&self.0)
|
||||
self.0.read()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -294,7 +290,7 @@ const ZERO: &[u8] = b"opt.zero\0";
|
|||
/// }
|
||||
/// ```
|
||||
pub fn zero() -> Result<bool> {
|
||||
get(ZERO)
|
||||
ZERO.name().read()
|
||||
}
|
||||
|
||||
/// A type providing access to jemalloc's zeroing behavior.
|
||||
|
@ -322,19 +318,18 @@ pub fn zero() -> Result<bool> {
|
|||
/// }
|
||||
/// ```
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Zero([usize; 2]);
|
||||
pub struct Zero(Mib<[usize; 2]>);
|
||||
|
||||
impl Zero {
|
||||
/// Returns a new `Zero`.
|
||||
pub fn new() -> Result<Self> {
|
||||
let mut mib = [0; 2];
|
||||
name_to_mib(ZERO, &mut mib)?;
|
||||
let mib = ZERO.name().mib()?;
|
||||
Ok(Zero(mib))
|
||||
}
|
||||
|
||||
/// Returns the `jemalloc` zeroing behavior.
|
||||
pub fn get(self) -> Result<bool> {
|
||||
get_mib(&self.0)
|
||||
self.0.read()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -359,7 +354,7 @@ const TCACHE: &[u8] = b"opt.tcache\0";
|
|||
/// }
|
||||
/// ```
|
||||
pub fn tcache() -> Result<bool> {
|
||||
get(TCACHE)
|
||||
TCACHE.name().read()
|
||||
}
|
||||
|
||||
/// A type providing access to thread-local allocation caching behavior.
|
||||
|
@ -385,19 +380,18 @@ pub fn tcache() -> Result<bool> {
|
|||
/// }
|
||||
/// ```
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Tcache([usize; 2]);
|
||||
pub struct Tcache(Mib<[usize; 2]>);
|
||||
|
||||
impl Tcache {
|
||||
/// Returns a new `Tcache`.
|
||||
pub fn new() -> Result<Self> {
|
||||
let mut mib = [0; 2];
|
||||
name_to_mib(TCACHE, &mut mib)?;
|
||||
let mib = TCACHE.name().mib()?;
|
||||
Ok(Tcache(mib))
|
||||
}
|
||||
|
||||
/// Returns the thread-local caching behavior.
|
||||
pub fn get(self) -> Result<bool> {
|
||||
get_mib(&self.0)
|
||||
self.0.read()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -422,7 +416,7 @@ const LG_TCACHE_MAX: &[u8] = b"opt.lg_tcache_max\0";
|
|||
/// }
|
||||
/// ```
|
||||
pub fn lg_tcache_max() -> Result<usize> {
|
||||
get(LG_TCACHE_MAX)
|
||||
LG_TCACHE_MAX.name().read()
|
||||
}
|
||||
|
||||
/// A type providing access to the maximum size class (log base 2) to cache in the thread-specific
|
||||
|
@ -449,19 +443,18 @@ pub fn lg_tcache_max() -> Result<usize> {
|
|||
/// }
|
||||
/// ```
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct LgTcacheMax([usize; 2]);
|
||||
pub struct LgTcacheMax(Mib<[usize; 2]>);
|
||||
|
||||
impl LgTcacheMax {
|
||||
/// Returns a new `LgTcacheMax`.
|
||||
pub fn new() -> Result<Self> {
|
||||
let mut mib = [0; 2];
|
||||
name_to_mib(LG_TCACHE_MAX, &mut mib)?;
|
||||
let mib = LG_TCACHE_MAX.name().mib()?;
|
||||
Ok(LgTcacheMax(mib))
|
||||
}
|
||||
|
||||
/// Returns the maximum cached size class.
|
||||
pub fn get(self) -> Result<usize> {
|
||||
get_mib(&self.0)
|
||||
self.0.read()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -487,7 +480,7 @@ const BACKGROUND_THREAD: &[u8] = b"opt.background_thread\0";
|
|||
/// }
|
||||
/// ```
|
||||
pub fn background_thread() -> Result<bool> {
|
||||
get(BACKGROUND_THREAD)
|
||||
BACKGROUND_THREAD.name().read()
|
||||
}
|
||||
|
||||
/// A type determining if `jemalloc` will be initialized with background worker
|
||||
|
@ -514,18 +507,17 @@ pub fn background_thread() -> Result<bool> {
|
|||
/// }
|
||||
/// ```
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct BackgroundThread([usize; 2]);
|
||||
pub struct BackgroundThread(Mib<[usize; 2]>);
|
||||
|
||||
impl BackgroundThread {
|
||||
/// Returns a new `BackgroundThread`.
|
||||
pub fn new() -> Result<Self> {
|
||||
let mut mib = [0; 2];
|
||||
name_to_mib(BACKGROUND_THREAD, &mut mib)?;
|
||||
let mib = BACKGROUND_THREAD.name().mib()?;
|
||||
Ok(BackgroundThread(mib))
|
||||
}
|
||||
|
||||
/// Returns the background thread initialization behavior.
|
||||
pub fn get(self) -> Result<bool> {
|
||||
get_mib(&self.0)
|
||||
self.0.read()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
//! Raw `malloctl` getter/setters
|
||||
|
||||
use error::{cvt, Error, Result};
|
||||
use error::{cvt, Result};
|
||||
use libc::c_char;
|
||||
use {mem, ptr, slice};
|
||||
|
||||
|
@ -60,74 +60,94 @@ pub fn name_to_mib(name: &[u8], mib: &mut [usize]) -> Result<()> {
|
|||
///
|
||||
/// The [`name_to_mib`] API translates a string of the key (e.g. `arenas.nbins`)
|
||||
/// to a `mib` (Management Information Base).
|
||||
pub fn get_mib<T: Copy>(mib: &[usize]) -> Result<T> {
|
||||
unsafe {
|
||||
let mut value = MaybeUninit { init: () };
|
||||
let mut len = mem::size_of::<T>();
|
||||
cvt(jemalloc_sys::mallctlbymib(
|
||||
mib.as_ptr(),
|
||||
mib.len(),
|
||||
&mut value.init as *mut _ as *mut _,
|
||||
&mut len,
|
||||
ptr::null_mut(),
|
||||
0,
|
||||
))?;
|
||||
assert_eq!(len, mem::size_of::<T>());
|
||||
Ok(value.maybe_uninit)
|
||||
}
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This function is `unsafe` because it is possible to use it to construct an
|
||||
/// 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> {
|
||||
let mut value = MaybeUninit { init: () };
|
||||
let mut len = mem::size_of::<T>();
|
||||
cvt(jemalloc_sys::mallctlbymib(
|
||||
mib.as_ptr(),
|
||||
mib.len(),
|
||||
&mut value.init as *mut _ as *mut _,
|
||||
&mut len,
|
||||
ptr::null_mut(),
|
||||
0,
|
||||
))?;
|
||||
assert_eq!(len, mem::size_of::<T>());
|
||||
Ok(value.maybe_uninit)
|
||||
}
|
||||
|
||||
/// Uses the null-terminated string `name` as key to the _MALLCTL NAMESPACE_ and
|
||||
/// reads its value.
|
||||
pub fn get<T: Copy>(name: &[u8]) -> Result<T> {
|
||||
unsafe {
|
||||
validate_name(name);
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This function is `unsafe` because it is possible to use it to construct an
|
||||
/// 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> {
|
||||
validate_name(name);
|
||||
|
||||
let mut value = MaybeUninit { init: () };
|
||||
let mut len = mem::size_of::<T>();
|
||||
cvt(jemalloc_sys::mallctl(
|
||||
name as *const _ as *const c_char,
|
||||
&mut value.init as *mut _ as *mut _,
|
||||
&mut len,
|
||||
ptr::null_mut(),
|
||||
0,
|
||||
))?;
|
||||
assert_eq!(len, mem::size_of::<T>());
|
||||
Ok(value.maybe_uninit)
|
||||
}
|
||||
let mut value = MaybeUninit { init: () };
|
||||
let mut len = mem::size_of::<T>();
|
||||
cvt(jemalloc_sys::mallctl(
|
||||
name as *const _ as *const c_char,
|
||||
&mut value.init as *mut _ as *mut _,
|
||||
&mut len,
|
||||
ptr::null_mut(),
|
||||
0,
|
||||
))?;
|
||||
assert_eq!(len, mem::size_of::<T>());
|
||||
Ok(value.maybe_uninit)
|
||||
}
|
||||
|
||||
/// Uses the MIB `mib` as key to the _MALLCTL NAMESPACE_ and sets its `value`.
|
||||
///
|
||||
/// The [`name_to_mib`] API translates a string of the key (e.g. `arenas.nbins`)
|
||||
/// to a `mib` (Management Information Base).
|
||||
pub fn set_mib<T>(mib: &[usize], mut value: T) -> Result<()> {
|
||||
unsafe {
|
||||
cvt(jemalloc_sys::mallctlbymib(
|
||||
mib.as_ptr(),
|
||||
mib.len(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
&mut value as *mut _ as *mut _,
|
||||
mem::size_of::<T>(),
|
||||
))
|
||||
}
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This function is `unsafe` because it is possible to use it to construct an
|
||||
/// 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<()> {
|
||||
cvt(jemalloc_sys::mallctlbymib(
|
||||
mib.as_ptr(),
|
||||
mib.len(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
&mut value as *mut _ as *mut _,
|
||||
mem::size_of::<T>(),
|
||||
))
|
||||
}
|
||||
|
||||
/// Uses the null-terminated string `name` as the key to the _MALLCTL NAMESPACE_
|
||||
/// and sets it `value`
|
||||
pub fn set<T>(name: &[u8], mut value: T) -> Result<()> {
|
||||
unsafe {
|
||||
validate_name(name);
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This function is `unsafe` because it is possible to use it to construct an
|
||||
/// 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<()> {
|
||||
validate_name(name);
|
||||
|
||||
cvt(jemalloc_sys::mallctl(
|
||||
name as *const _ as *const c_char,
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
&mut value as *mut _ as *mut _,
|
||||
mem::size_of::<T>(),
|
||||
))
|
||||
}
|
||||
cvt(jemalloc_sys::mallctl(
|
||||
name as *const _ as *const c_char,
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
&mut value as *mut _ as *mut _,
|
||||
mem::size_of::<T>(),
|
||||
))
|
||||
}
|
||||
|
||||
/// Uses the MIB `mib` as key to the _MALLCTL NAMESPACE_ and sets its `value`
|
||||
|
@ -135,68 +155,214 @@ pub fn set<T>(name: &[u8], mut value: T) -> Result<()> {
|
|||
///
|
||||
/// The [`name_to_mib`] API translates a string of the key (e.g. `arenas.nbins`)
|
||||
/// to a `mib` (Management Information Base).
|
||||
pub fn get_set_mib<T>(mib: &[usize], mut value: T) -> Result<T> {
|
||||
unsafe {
|
||||
let mut len = mem::size_of::<T>();
|
||||
cvt(jemalloc_sys::mallctlbymib(
|
||||
mib.as_ptr(),
|
||||
mib.len(),
|
||||
&mut value as *mut _ as *mut _,
|
||||
&mut len,
|
||||
&mut value as *mut _ as *mut _,
|
||||
len,
|
||||
))?;
|
||||
assert_eq!(len, mem::size_of::<T>());
|
||||
Ok(value)
|
||||
}
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This function is `unsafe` because it is possible to use it to construct an
|
||||
/// 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> {
|
||||
let mut len = mem::size_of::<T>();
|
||||
cvt(jemalloc_sys::mallctlbymib(
|
||||
mib.as_ptr(),
|
||||
mib.len(),
|
||||
&mut value as *mut _ as *mut _,
|
||||
&mut len,
|
||||
&mut value as *mut _ as *mut _,
|
||||
len,
|
||||
))?;
|
||||
assert_eq!(len, mem::size_of::<T>());
|
||||
Ok(value)
|
||||
}
|
||||
|
||||
/// Uses the null-terminated string `name` as key to the _MALLCTL NAMESPACE_ and
|
||||
/// sets its `value` returning its previous value.
|
||||
pub fn get_set<T>(name: &[u8], mut value: T) -> Result<T> {
|
||||
unsafe {
|
||||
validate_name(name);
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This function is `unsafe` because it is possible to use it to construct an
|
||||
/// 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> {
|
||||
validate_name(name);
|
||||
|
||||
let mut len = mem::size_of::<T>();
|
||||
cvt(jemalloc_sys::mallctl(
|
||||
name as *const _ as *const c_char,
|
||||
&mut value as *mut _ as *mut _,
|
||||
&mut len,
|
||||
&mut value as *mut _ as *mut _,
|
||||
len,
|
||||
))?;
|
||||
assert_eq!(len, mem::size_of::<T>());
|
||||
Ok(value)
|
||||
}
|
||||
let mut len = mem::size_of::<T>();
|
||||
cvt(jemalloc_sys::mallctl(
|
||||
name as *const _ as *const c_char,
|
||||
&mut value as *mut _ as *mut _,
|
||||
&mut len,
|
||||
&mut value as *mut _ as *mut _,
|
||||
len,
|
||||
))?;
|
||||
assert_eq!(len, mem::size_of::<T>());
|
||||
Ok(value)
|
||||
}
|
||||
|
||||
/// Uses the MIB `mib` as key to the _MALLCTL NAMESPACE_ and reads its value of
|
||||
/// type `&str`
|
||||
/// Uses the MIB `mib` as key to the _MALLCTL NAMESPACE_ and reads its value.
|
||||
///
|
||||
/// The [`name_to_mib`] API translates a string of the key (e.g. `arenas.nbins`)
|
||||
/// to a `mib` (Management Information Base).
|
||||
pub fn get_str_mib(mib: &[usize]) -> Result<&'static str> {
|
||||
unsafe {
|
||||
let ptr: *const c_char = get_mib(mib)?;
|
||||
ptr2str(ptr)
|
||||
}
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This function is unsafe because if the key does not return a pointer to a
|
||||
/// null-terminated string the behavior is undefined.
|
||||
///
|
||||
/// For example, a key for a `u64` value can be used to read a pointer on 64-bit
|
||||
/// platform, where this pointer will point to the address denoted by the `u64`s
|
||||
/// representation. Also, a key to a `*mut extent_hooks_t` will return a pointer
|
||||
/// that will not point to a null-terminated string.
|
||||
///
|
||||
/// This function needs to compute the length of the string by looking for the
|
||||
/// null-terminator: `\0`. This requires reading the memory behind the pointer.
|
||||
///
|
||||
/// If the pointer is invalid (e.g. because it was converted from a `u64` that
|
||||
/// does not represent a valid address), reading the string to look for `\0`
|
||||
/// will dereference a non-dereferenceable pointer, which is undefined behavior.
|
||||
///
|
||||
/// 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)?;
|
||||
ptr2str(ptr)
|
||||
}
|
||||
|
||||
/// Uses the MIB `mib` as key to the _MALLCTL NAMESPACE_ and sets its `value`.
|
||||
///
|
||||
/// The [`name_to_mib`] API translates a string of the key (e.g. `arenas.nbins`)
|
||||
/// to a `mib` (Management Information Base).
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// If `value` is not a non-empty null-terminated string.
|
||||
pub fn set_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) }
|
||||
}
|
||||
|
||||
/// Uses the MIB `mib` as key to the _MALLCTL NAMESPACE_ and sets its `value`
|
||||
/// returning its previous value.
|
||||
///
|
||||
/// The [`name_to_mib`] API translates a string of the key (e.g. `arenas.nbins`)
|
||||
/// to a `mib` (Management Information Base).
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This function is unsafe because if the key does not return a pointer to a
|
||||
/// null-terminated string the behavior is undefined.
|
||||
///
|
||||
/// For example, a key for a `u64` value can be used to read a pointer on 64-bit
|
||||
/// platform, where this pointer will point to the address denoted by the `u64`s
|
||||
/// representation. Also, a key to a `*mut extent_hooks_t` will return a pointer
|
||||
/// that will not point to a null-terminated string.
|
||||
///
|
||||
/// This function needs to compute the length of the string by looking for the
|
||||
/// null-terminator: `\0`. This requires reading the memory behind the pointer.
|
||||
///
|
||||
/// If the pointer is invalid (e.g. because it was converted from a `u64` that
|
||||
/// does not represent a valid address), reading the string to look for `\0`
|
||||
/// will dereference a non-dereferenceable pointer, which is undefined behavior.
|
||||
///
|
||||
/// 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)?;
|
||||
ptr2str(ptr)
|
||||
}
|
||||
|
||||
/// Uses the null-terminated string `name` as key to the _MALLCTL NAMESPACE_ and
|
||||
/// reads its value of type `&str`.
|
||||
pub fn get_str(name: &[u8]) -> Result<&'static str> {
|
||||
unsafe {
|
||||
validate_name(name);
|
||||
|
||||
let ptr: *const c_char = get(name)?;
|
||||
ptr2str(ptr)
|
||||
}
|
||||
/// reads its value.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This function is unsafe because if the key does not return a pointer to a
|
||||
/// null-terminated string the behavior is undefined.
|
||||
///
|
||||
/// For example, a key for a `u64` value can be used to read a pointer on 64-bit
|
||||
/// platform, where this pointer will point to the address denoted by the `u64`s
|
||||
/// representation. Also, a key to a `*mut extent_hooks_t` will return a pointer
|
||||
/// that will not point to a null-terminated string.
|
||||
///
|
||||
/// This function needs to compute the length of the string by looking for the
|
||||
/// null-terminator: `\0`. This requires reading the memory behind the pointer.
|
||||
///
|
||||
/// If the pointer is invalid (e.g. because it was converted from a `u64` that
|
||||
/// does not represent a valid address), reading the string to look for `\0`
|
||||
/// will dereference a non-dereferenceable pointer, which is undefined behavior.
|
||||
///
|
||||
/// 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)?;
|
||||
ptr2str(ptr)
|
||||
}
|
||||
|
||||
unsafe fn ptr2str(ptr: *const c_char) -> Result<&'static str> {
|
||||
/// 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<()> {
|
||||
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) }
|
||||
}
|
||||
|
||||
/// Uses the null-terminated string `name` as key to the _MALLCTL NAMESPACE_ and
|
||||
/// sets its `value` returning its previous value.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This function is unsafe because if the key does not return a pointer to a
|
||||
/// null-terminated string the behavior is undefined.
|
||||
///
|
||||
/// For example, a key for a `u64` value can be used to read a pointer on 64-bit
|
||||
/// platform, where this pointer will point to the address denoted by the `u64`s
|
||||
/// representation. Also, a key to a `*mut extent_hooks_t` will return a pointer
|
||||
/// that will not point to a null-terminated string.
|
||||
///
|
||||
/// This function needs to compute the length of the string by looking for the
|
||||
/// null-terminator: `\0`. This requires reading the memory behind the pointer.
|
||||
///
|
||||
/// If the pointer is invalid (e.g. because it was converted from a `u64` that
|
||||
/// does not represent a valid address), reading the string to look for `\0`
|
||||
/// will dereference a non-dereferenceable pointer, which is undefined behavior.
|
||||
///
|
||||
/// 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)?;
|
||||
ptr2str(ptr)
|
||||
}
|
||||
|
||||
/// Converts a non-empty null-terminated character string at `ptr` into a valid
|
||||
/// null-terminated UTF-8 string.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// If `ptr.is_null()`.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// If `ptr` does not point to a null-terminated character string the behavior
|
||||
/// is undefined.
|
||||
unsafe fn ptr2str(ptr: *const c_char) -> Result<&'static [u8]> {
|
||||
assert!(
|
||||
!ptr.is_null(),
|
||||
"attempt to convert a null-ptr to a UTF-8 string"
|
||||
);
|
||||
let len = libc::strlen(ptr);
|
||||
let byte_slice = slice::from_raw_parts(ptr as *const u8, (len + 1) as _);
|
||||
core::str::from_utf8(byte_slice).map_err(|_| Error::EINVAL)
|
||||
Ok(slice::from_raw_parts(ptr as *const u8, len + 1))
|
||||
}
|
||||
|
||||
fn validate_name(name: &[u8]) {
|
||||
|
@ -230,15 +396,15 @@ mod tests {
|
|||
assert!(rstr.is_ok());
|
||||
let rstr = rstr.unwrap();
|
||||
assert!(rstr.len() == 1);
|
||||
assert_eq!(rstr, "\0");
|
||||
assert_eq!(rstr, b"\0");
|
||||
}
|
||||
{
|
||||
let cstr = b"foo baaar\0";
|
||||
let rstr = ptr2str(cstr as *const _ as *const c_char);
|
||||
assert!(rstr.is_ok());
|
||||
let rstr = rstr.unwrap();
|
||||
assert!(rstr.len() == "foo baaar\0".len());
|
||||
assert_eq!(rstr, "foo baaar\0");
|
||||
assert!(rstr.len() == b"foo baaar\0".len());
|
||||
assert_eq!(rstr, b"foo baaar\0");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
//! [`Epoch`]: ../struct.Epoch.html
|
||||
|
||||
use error::Result;
|
||||
use raw::{get, get_mib, name_to_mib};
|
||||
use keys::{Access, IntoName, Mib};
|
||||
|
||||
const ALLOCATED: &[u8] = b"stats.allocated\0";
|
||||
|
||||
|
@ -37,7 +37,7 @@ const ALLOCATED: &[u8] = b"stats.allocated\0";
|
|||
///
|
||||
/// [`epoch`]: ../fn.epoch().html
|
||||
pub fn allocated() -> Result<usize> {
|
||||
get(ALLOCATED)
|
||||
ALLOCATED.name().read()
|
||||
}
|
||||
|
||||
/// A type providing access to the total number of bytes allocated by the application.
|
||||
|
@ -73,19 +73,18 @@ pub fn allocated() -> Result<usize> {
|
|||
///
|
||||
/// [`Epoch`]: ../struct.Epoch.html
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Allocated([usize; 2]);
|
||||
pub struct Allocated(Mib<[usize; 2]>);
|
||||
|
||||
impl Allocated {
|
||||
/// Returns a new `Allocated`.
|
||||
pub fn new() -> Result<Self> {
|
||||
let mut mib = [0; 2];
|
||||
name_to_mib(ALLOCATED, &mut mib)?;
|
||||
let mib = ALLOCATED.name().mib()?;
|
||||
Ok(Allocated(mib))
|
||||
}
|
||||
|
||||
/// Returns the total number of bytes allocated by the application.
|
||||
pub fn get(self) -> Result<usize> {
|
||||
get_mib(&self.0)
|
||||
self.0.read()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -122,7 +121,7 @@ const ACTIVE: &[u8] = b"stats.active\0";
|
|||
/// [`epoch`]: ../fn.epoch().html
|
||||
/// [`allocated`]: fn.allocated.hml
|
||||
pub fn active() -> Result<usize> {
|
||||
get(ACTIVE)
|
||||
ACTIVE.name().read()
|
||||
}
|
||||
|
||||
/// A type providing access to the total number of bytes in active pages allocated by the
|
||||
|
@ -163,19 +162,18 @@ pub fn active() -> Result<usize> {
|
|||
/// [`Epoch`]: ../struct.Epoch.html
|
||||
/// [`Allocated`]: struct.Allocated.html
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Active([usize; 2]);
|
||||
pub struct Active(Mib<[usize; 2]>);
|
||||
|
||||
impl Active {
|
||||
/// Returns a new `Allocated`.
|
||||
pub fn new() -> Result<Self> {
|
||||
let mut mib = [0; 2];
|
||||
name_to_mib(ACTIVE, &mut mib)?;
|
||||
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> {
|
||||
get_mib(&self.0)
|
||||
self.0.read()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -205,7 +203,7 @@ const METADATA: &[u8] = b"stats.metadata\0";
|
|||
///
|
||||
/// [`epoch`]: ../fn.epoch.html
|
||||
pub fn metadata() -> Result<usize> {
|
||||
get(METADATA)
|
||||
METADATA.name().read()
|
||||
}
|
||||
|
||||
/// A type providing access to the total number of bytes dedicated to jemalloc metadata.
|
||||
|
@ -239,19 +237,18 @@ pub fn metadata() -> Result<usize> {
|
|||
///
|
||||
/// [`Epoch`]: ../struct.Epoch.html
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Metadata([usize; 2]);
|
||||
pub struct Metadata(Mib<[usize; 2]>);
|
||||
|
||||
impl Metadata {
|
||||
/// Returns a new `Metadata`.
|
||||
pub fn new() -> Result<Self> {
|
||||
let mut mib = [0; 2];
|
||||
name_to_mib(METADATA, &mut mib)?;
|
||||
let mib = METADATA.name().mib()?;
|
||||
Ok(Metadata(mib))
|
||||
}
|
||||
|
||||
/// Returns the total number of bytes dedicated to jemalloc metadata.
|
||||
pub fn get(self) -> Result<usize> {
|
||||
get_mib(&self.0)
|
||||
self.0.read()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -288,7 +285,7 @@ const RESIDENT: &[u8] = b"stats.resident\0";
|
|||
/// [`epoch`]: ../fn.epoch.html
|
||||
/// [`active`]: fn.active.html
|
||||
pub fn resident() -> Result<usize> {
|
||||
get(RESIDENT)
|
||||
RESIDENT.name().read()
|
||||
}
|
||||
|
||||
/// A type providing access to the total number of bytes in physically resident data pages mapped
|
||||
|
@ -330,19 +327,18 @@ pub fn resident() -> Result<usize> {
|
|||
/// [`Epoch`]: ../struct.Epoch.html
|
||||
/// [`Active`]: struct.Active.html
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Resident([usize; 2]);
|
||||
pub struct Resident(Mib<[usize; 2]>);
|
||||
|
||||
impl Resident {
|
||||
/// Returns a new `Resident`.
|
||||
pub fn new() -> Result<Self> {
|
||||
let mut mib = [0; 2];
|
||||
name_to_mib(RESIDENT, &mut mib)?;
|
||||
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> {
|
||||
get_mib(&self.0)
|
||||
self.0.read()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -378,7 +374,7 @@ const MAPPED: &[u8] = b"stats.mapped\0";
|
|||
/// [`resident`]: fn.resident.html
|
||||
/// [`active`]: fn.active.html
|
||||
pub fn mapped() -> Result<usize> {
|
||||
get(MAPPED)
|
||||
MAPPED.name().read()
|
||||
}
|
||||
|
||||
/// A type providing access to the total number of bytes in active extents mapped by the allocator.
|
||||
|
@ -418,19 +414,18 @@ pub fn mapped() -> Result<usize> {
|
|||
/// [`Resident`]: struct.Resident.html
|
||||
/// [`Active`]: struct.Active.html
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Mapped([usize; 2]);
|
||||
pub struct Mapped(Mib<[usize; 2]>);
|
||||
|
||||
impl Mapped {
|
||||
/// Returns a new `Mapped`.
|
||||
pub fn new() -> Result<Self> {
|
||||
let mut mib = [0; 2];
|
||||
name_to_mib(MAPPED, &mut mib)?;
|
||||
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> {
|
||||
get_mib(&self.0)
|
||||
self.0.read()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -465,7 +460,7 @@ const RETAINED: &[u8] = b"stats.retained\0";
|
|||
/// [`epoch`]: ../fn.epoch.html
|
||||
/// [`mapped`]: fn.mapped.html
|
||||
pub fn retained() -> Result<usize> {
|
||||
get(RETAINED)
|
||||
RETAINED.name().read()
|
||||
}
|
||||
|
||||
/// A type providing access to the total number of bytes in virtual memory mappings that were retained rather than being
|
||||
|
@ -504,18 +499,17 @@ pub fn retained() -> Result<usize> {
|
|||
/// [`Epoch`]: ../struct.Epoch.html
|
||||
/// [`Mapped`]: struct.Mapped.html
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Retained([usize; 2]);
|
||||
pub struct Retained(Mib<[usize; 2]>);
|
||||
|
||||
impl Retained {
|
||||
/// Returns a new `Retained`.
|
||||
pub fn new() -> Result<Self> {
|
||||
let mut mib = [0; 2];
|
||||
name_to_mib(RETAINED, &mut mib)?;
|
||||
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> {
|
||||
get_mib(&self.0)
|
||||
self.0.read()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,7 +41,7 @@ const ALLOCATEDP: &[u8] = b"thread.allocatedp\0";
|
|||
/// }
|
||||
/// ```
|
||||
pub fn allocatedp() -> Result<ThreadLocal<u64>> {
|
||||
get(ALLOCATEDP).map(ThreadLocal)
|
||||
unsafe { get(ALLOCATEDP).map(ThreadLocal) }
|
||||
}
|
||||
|
||||
/// A type providing access to the total number of bytes allocated by the current thread.
|
||||
|
@ -95,7 +95,7 @@ impl AllocatedP {
|
|||
|
||||
/// Returns a thread-local pointer to the total number of bytes allocated by this thread.
|
||||
pub fn get(&self) -> Result<ThreadLocal<u64>> {
|
||||
get_mib(&self.0).map(ThreadLocal)
|
||||
unsafe { get_mib(&self.0).map(ThreadLocal) }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -133,7 +133,7 @@ const DEALLOCATEDP: &[u8] = b"thread.deallocatedp\0";
|
|||
/// }
|
||||
/// ```
|
||||
pub fn deallocatedp() -> Result<ThreadLocal<u64>> {
|
||||
get(DEALLOCATEDP).map(ThreadLocal)
|
||||
unsafe { get(DEALLOCATEDP).map(ThreadLocal) }
|
||||
}
|
||||
|
||||
/// A type providing access to the total number of bytes deallocated by the current thread.
|
||||
|
@ -181,7 +181,7 @@ impl DeallocatedP {
|
|||
|
||||
/// Returns a thread-local pointer to the total number of bytes deallocated by this thread.
|
||||
pub fn get(&self) -> Result<ThreadLocal<u64>> {
|
||||
let ptr = get_mib::<*mut u64>(&self.0)?;
|
||||
let ptr = unsafe { get_mib::<*mut u64>(&self.0)? };
|
||||
Ok(ThreadLocal(ptr))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
//! Version operations.
|
||||
|
||||
use error::Result;
|
||||
use raw::{get_str, get_str_mib, name_to_mib};
|
||||
use keys::{Access, IntoName, MibStr};
|
||||
|
||||
const VERSION: &[u8] = b"version\0";
|
||||
|
||||
|
@ -25,7 +25,7 @@ const VERSION: &[u8] = b"version\0";
|
|||
/// }
|
||||
/// ```
|
||||
pub fn version() -> Result<&'static str> {
|
||||
get_str(VERSION)
|
||||
VERSION.name().read()
|
||||
}
|
||||
|
||||
/// A type providing access to the jemalloc version string.
|
||||
|
@ -47,18 +47,17 @@ pub fn version() -> Result<&'static str> {
|
|||
/// println!("jemalloc version {}", version.get().unwrap());
|
||||
/// }
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Version([usize; 1]);
|
||||
pub struct Version(MibStr<[usize; 1]>);
|
||||
|
||||
impl Version {
|
||||
/// Returns a new `Version`.
|
||||
pub fn new() -> Result<Self> {
|
||||
let mut mib = [0; 1];
|
||||
name_to_mib(VERSION, &mut mib)?;
|
||||
let mib = VERSION.name().mib_str()?;
|
||||
Ok(Version(mib))
|
||||
}
|
||||
|
||||
/// Returns the jemalloc version string.
|
||||
pub fn get(self) -> Result<&'static str> {
|
||||
get_str_mib(&self.0)
|
||||
self.0.read()
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue