Struct sharded_slab::Pool

source ·
pub struct Pool<T, C = DefaultConfig>where
    T: Clear + Default,
    C: Config,{ /* private fields */ }
Expand description

A lock-free concurrent object pool.

Slabs provide pre-allocated storage for many instances of a single type. But, when working with heap allocated objects, the advantages of a slab are lost, as the memory allocated for the object is freed when the object is removed from the slab. With a pool, we can instead reuse this memory for objects being added to the pool in the future, therefore reducing memory fragmentation and avoiding additional allocations.

This type implements a lock-free concurrent pool, indexed by usizes. The items stored in this type need to implement Clear and Default.

The Pool type shares similar semantics to Slab when it comes to sharing across threads and storing mutable shared data. The biggest difference is there are no Slab::insert and Slab::take analouges for the Pool type. Instead new items are added to the pool by using the Pool::create method, and marked for clearing by the Pool::clear method.

Examples

Add an entry to the pool, returning an index:

let pool: Pool<String> = Pool::new();

let key = pool.create_with(|item| item.push_str("hello world")).unwrap();
assert_eq!(pool.get(key).unwrap(), String::from("hello world"));

Create a new pooled item, returning a guard that allows mutable access:

let pool: Pool<String> = Pool::new();

let mut guard = pool.create().unwrap();
let key = guard.key();
guard.push_str("hello world");

drop(guard); // release the guard, allowing immutable access.
assert_eq!(pool.get(key).unwrap(), String::from("hello world"));

Pool entries can be cleared by calling Pool::clear. This marks the entry to be cleared when the guards referencing to it are dropped.

let pool: Pool<String> = Pool::new();

let key = pool.create_with(|item| item.push_str("hello world")).unwrap();

// Mark this entry to be cleared.
pool.clear(key);

// The cleared entry is no longer available in the pool
assert!(pool.get(key).is_none());

Configuration

Both Pool and Slab share the same configuration mechanism. See crate level documentation for more details.

Implementations§

source§

impl<T> Pool<T>where T: Clear + Default,

source

pub fn new() -> Self

Returns a new Pool with the default configuration parameters.

source

pub fn new_with_config<C: Config>() -> Pool<T, C>

Returns a new Pool with the provided configuration parameters.

source§

impl<T, C> Pool<T, C>where T: Clear + Default, C: Config,

source

pub const USED_BITS: usize = C::USED_BITS

The number of bits in each index which are used by the pool.

If other data is packed into the usize indices returned by Pool::create, user code is free to use any bits higher than the USED_BITS-th bit freely.

This is determined by the Config type that configures the pool’s parameters. By default, all bits are used; this can be changed by overriding the Config::RESERVED_BITS constant.

source

pub fn create(&self) -> Option<RefMut<'_, T, C>>

Creates a new object in the pool, returning an RefMut guard that may be used to mutate the new object.

If this function returns None, then the shard for the current thread is full and no items can be added until some are removed, or the maximum number of shards has been reached.

Examples
let pool: Pool<String> = Pool::new();

// Create a new pooled item, returning a guard that allows mutable
// access to the new item.
let mut item = pool.create().unwrap();
// Return a key that allows indexing the created item once the guard
// has been dropped.
let key = item.key();

// Mutate the item.
item.push_str("Hello");
// Drop the guard, releasing mutable access to the new item.
drop(item);

/// Other threads may now (immutably) access the item using the returned key.
thread::spawn(move || {
   assert_eq!(pool.get(key).unwrap(), String::from("Hello"));
}).join().unwrap();
source

pub fn create_owned(self: Arc<Self>) -> Option<OwnedRefMut<T, C>>

Creates a new object in the pool, returning an OwnedRefMut guard that may be used to mutate the new object.

If this function returns None, then the shard for the current thread is full and no items can be added until some are removed, or the maximum number of shards has been reached.

Unlike create, which borrows the pool, this method clones the Arc around the pool if a value exists for the given key. This means that the returned OwnedRefMut can be held for an arbitrary lifetime. However, this method requires that the pool itself be wrapped in an Arc.

An OwnedRefMut<T> functions more or less identically to an owned Box<T>: it can be passed to functions, stored in structure fields, and borrowed mutably or immutably, and can be owned for arbitrary lifetimes. The difference is that, unlike a Box<T>, the memory allocation for the T lives in the Pool; when an OwnedRefMut is created, it may reuse memory that was allocated for a previous pooled object that has been cleared. Additionally, the OwnedRefMut may be downgraded to an OwnedRef which may be shared freely, essentially turning the Box into an Arc.

Examples
use std::sync::Arc;

let pool: Arc<Pool<String>> = Arc::new(Pool::new());

// Create a new pooled item, returning an owned guard that allows mutable
// access to the new item.
let mut item = pool.clone().create_owned().unwrap();
// Return a key that allows indexing the created item once the guard
// has been dropped.
let key = item.key();

// Mutate the item.
item.push_str("Hello");
// Drop the guard, releasing mutable access to the new item.
drop(item);

/// Other threads may now (immutably) access the item using the returned key.
thread::spawn(move || {
   assert_eq!(pool.get(key).unwrap(), String::from("Hello"));
}).join().unwrap();
use std::sync::Arc;

let pool: Arc<Pool<String>> = Arc::new(Pool::new());

// Create a new item, returning an owned, mutable guard.
let mut value = pool.clone().create_owned().unwrap();

// Now, the original `Arc` clone of the pool may be dropped, but the
// returned `OwnedRefMut` can still access the value.
drop(pool);

value.push_str("hello world");
assert_eq!(value, String::from("hello world"));

Unlike RefMut, an OwnedRefMut may be stored in a struct which must live for the 'static lifetime:

use sharded_slab::pool::OwnedRefMut;
use std::sync::Arc;

pub struct MyStruct {
    pool_ref: OwnedRefMut<String>,
    // ... other fields ...
}

// Suppose this is some arbitrary function which requires a value that
// lives for the 'static lifetime...
fn function_requiring_static<T: 'static>(t: &T) {
    // ... do something extremely important and interesting ...
}

let pool: Arc<Pool<String>> = Arc::new(Pool::new());

// Create a new item, returning a mutable owned reference.
let pool_ref = pool.clone().create_owned().unwrap();

let my_struct = MyStruct {
    pool_ref,
    // ...
};

// We can use `my_struct` anywhere where it is required to have the
// `'static` lifetime:
function_requiring_static(&my_struct);

OwnedRefMuts may be sent between threads:

use std::{thread, sync::Arc};

let pool: Arc<Pool<String>> = Arc::new(Pool::new());

let mut value = pool.clone().create_owned().unwrap();
let key = value.key();

thread::spawn(move || {
    value.push_str("hello world");
    // ...
}).join().unwrap();

// Once the `OwnedRefMut` has been dropped by the other thread, we may
// now access the value immutably on this thread.

assert_eq!(pool.get(key).unwrap(), String::from("hello world"));

Downgrading from a mutable to an immutable reference:

use std::{thread, sync::Arc};

let pool: Arc<Pool<String>> = Arc::new(Pool::new());

let mut value = pool.clone().create_owned().unwrap();
let key = value.key();
value.push_str("hello world");

// Downgrade the mutable owned ref to an immutable owned ref.
let value = value.downgrade();

// Once the `OwnedRefMut` has been downgraded, other threads may
// immutably access the pooled value:
thread::spawn(move || {
    assert_eq!(pool.get(key).unwrap(), String::from("hello world"));
}).join().unwrap();

// This thread can still access the pooled value through the
// immutable owned ref:
assert_eq!(value, String::from("hello world"));
source

pub fn create_with(&self, init: impl FnOnce(&mut T)) -> Option<usize>

Creates a new object in the pool with the provided initializer, returning a key that may be used to access the new object.

If this function returns None, then the shard for the current thread is full and no items can be added until some are removed, or the maximum number of shards has been reached.

Examples
let pool: Pool<String> = Pool::new();

// Create a new pooled item, returning its integer key.
let key = pool.create_with(|s| s.push_str("Hello")).unwrap();

/// Other threads may now (immutably) access the item using the key.
thread::spawn(move || {
   assert_eq!(pool.get(key).unwrap(), String::from("Hello"));
}).join().unwrap();
source

pub fn get(&self, key: usize) -> Option<Ref<'_, T, C>>

Return a borrowed reference to the value associated with the given key.

If the pool does not contain a value for the given key, None is returned instead.

Examples
let pool: Pool<String> = Pool::new();
let key = pool.create_with(|item| item.push_str("hello world")).unwrap();

assert_eq!(pool.get(key).unwrap(), String::from("hello world"));
assert!(pool.get(12345).is_none());
source

pub fn get_owned(self: Arc<Self>, key: usize) -> Option<OwnedRef<T, C>>

Return an owned reference to the value associated with the given key.

If the pool does not contain a value for the given key, None is returned instead.

Unlike get, which borrows the pool, this method clones the Arc around the pool if a value exists for the given key. This means that the returned OwnedRef can be held for an arbitrary lifetime. However, this method requires that the pool itself be wrapped in an Arc.

Examples
use std::sync::Arc;

let pool: Arc<Pool<String>> = Arc::new(Pool::new());
let key = pool.create_with(|item| item.push_str("hello world")).unwrap();

// Look up the created `Key`, returning an `OwnedRef`.
let value = pool.clone().get_owned(key).unwrap();

// Now, the original `Arc` clone of the pool may be dropped, but the
// returned `OwnedRef` can still access the value.
assert_eq!(value, String::from("hello world"));

Unlike Ref, an OwnedRef may be stored in a struct which must live for the 'static lifetime:

use sharded_slab::pool::OwnedRef;
use std::sync::Arc;

pub struct MyStruct {
    pool_ref: OwnedRef<String>,
    // ... other fields ...
}

// Suppose this is some arbitrary function which requires a value that
// lives for the 'static lifetime...
fn function_requiring_static<T: 'static>(t: &T) {
    // ... do something extremely important and interesting ...
}

let pool: Arc<Pool<String>> = Arc::new(Pool::new());
let key = pool.create_with(|item| item.push_str("hello world")).unwrap();

// Look up the created `Key`, returning an `OwnedRef`.
let pool_ref = pool.clone().get_owned(key).unwrap();
let my_struct = MyStruct {
    pool_ref,
    // ...
};

// We can use `my_struct` anywhere where it is required to have the
// `'static` lifetime:
function_requiring_static(&my_struct);

OwnedRefs may be sent between threads:

use std::{thread, sync::Arc};

let pool: Arc<Pool<String>> = Arc::new(Pool::new());
let key = pool.create_with(|item| item.push_str("hello world")).unwrap();

// Look up the created `Key`, returning an `OwnedRef`.
let value = pool.clone().get_owned(key).unwrap();

thread::spawn(move || {
    assert_eq!(value, String::from("hello world"));
    // ...
}).join().unwrap();
source

pub fn clear(&self, key: usize) -> bool

Remove the value using the storage associated with the given key from the pool, returning true if the value was removed.

This method does not block the current thread until the value can be cleared. Instead, if another thread is currently accessing that value, this marks it to be cleared by that thread when it is done accessing that value.

Examples
let pool: Pool<String> = Pool::new();

// Check out an item from the pool.
let mut item = pool.create().unwrap();
let key = item.key();
item.push_str("hello world");
drop(item);

assert_eq!(pool.get(key).unwrap(), String::from("hello world"));

pool.clear(key);
assert!(pool.get(key).is_none());
let pool: Pool<String> = Pool::new();

let key = pool.create_with(|item| item.push_str("Hello world!")).unwrap();

// Clearing a key that doesn't exist in the `Pool` will return `false`
assert_eq!(pool.clear(key + 69420), false);

// Clearing a key that does exist returns `true`
assert!(pool.clear(key));

// Clearing a key that has previously been cleared will return `false`
assert_eq!(pool.clear(key), false);

Trait Implementations§

source§

impl<T, C> Debug for Pool<T, C>where T: Debug + Clear + Default, C: Config,

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl<T> Default for Pool<T>where T: Clear + Default,

source§

fn default() -> Self

Returns the “default value” for a type. Read more
source§

impl<T, C> Send for Pool<T, C>where T: Send + Clear + Default, C: Config,

source§

impl<T, C> Sync for Pool<T, C>where T: Sync + Clear + Default, C: Config,

Auto Trait Implementations§

§

impl<T, C> RefUnwindSafe for Pool<T, C>where C: RefUnwindSafe,

§

impl<T, C> Unpin for Pool<T, C>where C: Unpin,

§

impl<T, C = DefaultConfig> !UnwindSafe for Pool<T, C>

Blanket Implementations§

source§

impl<T> Any for Twhere T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

source§

impl<T, U> Into<U> for Twhere U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

source§

impl<T, U> TryFrom<U> for Twhere U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.