1342 lines
42 KiB
Rust
1342 lines
42 KiB
Rust
//! A lock-free concurrent object pool.
|
|
//!
|
|
//! See the [`Pool` type's documentation][pool] for details on the object pool API and how
|
|
//! it differs from the [`Slab`] API.
|
|
//!
|
|
//! [pool]: ../struct.Pool.html
|
|
//! [`Slab`]: ../struct.Slab.html
|
|
use crate::{
|
|
cfg::{self, CfgPrivate, DefaultConfig},
|
|
clear::Clear,
|
|
page, shard,
|
|
tid::Tid,
|
|
Pack, Shard,
|
|
};
|
|
|
|
use std::{fmt, marker::PhantomData, sync::Arc};
|
|
|
|
/// 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 `usize`s. 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:
|
|
/// ```
|
|
/// # use sharded_slab::Pool;
|
|
/// 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:
|
|
/// ```
|
|
/// # use sharded_slab::Pool;
|
|
/// 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.
|
|
/// ```
|
|
/// # use sharded_slab::Pool;
|
|
/// 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][config-doc]
|
|
/// for more details.
|
|
///
|
|
/// [`Slab::take`]: crate::Slab::take
|
|
/// [`Slab::insert`]: crate::Slab::insert
|
|
/// [`Pool::create`]: Pool::create
|
|
/// [`Pool::clear`]: Pool::clear
|
|
/// [config-doc]: crate#configuration
|
|
/// [`Clear`]: crate::Clear
|
|
/// [`Slab`]: crate::Slab
|
|
pub struct Pool<T, C = DefaultConfig>
|
|
where
|
|
T: Clear + Default,
|
|
C: cfg::Config,
|
|
{
|
|
shards: shard::Array<T, C>,
|
|
_cfg: PhantomData<C>,
|
|
}
|
|
|
|
/// A guard that allows access to an object in a pool.
|
|
///
|
|
/// While the guard exists, it indicates to the pool that the item the guard references is
|
|
/// currently being accessed. If the item is removed from the pool while the guard exists, the
|
|
/// removal will be deferred until all guards are dropped.
|
|
pub struct Ref<'a, T, C = DefaultConfig>
|
|
where
|
|
T: Clear + Default,
|
|
C: cfg::Config,
|
|
{
|
|
inner: page::slot::Guard<T, C>,
|
|
shard: &'a Shard<T, C>,
|
|
key: usize,
|
|
}
|
|
|
|
/// A guard that allows exclusive mutable access to an object in a pool.
|
|
///
|
|
/// While the guard exists, it indicates to the pool that the item the guard
|
|
/// references is currently being accessed. If the item is removed from the pool
|
|
/// while a guard exists, the removal will be deferred until the guard is
|
|
/// dropped. The slot cannot be accessed by other threads while it is accessed
|
|
/// mutably.
|
|
pub struct RefMut<'a, T, C = DefaultConfig>
|
|
where
|
|
T: Clear + Default,
|
|
C: cfg::Config,
|
|
{
|
|
inner: page::slot::InitGuard<T, C>,
|
|
shard: &'a Shard<T, C>,
|
|
key: usize,
|
|
}
|
|
|
|
/// An owned guard that allows shared immutable access to an object in a pool.
|
|
///
|
|
/// While the guard exists, it indicates to the pool that the item the guard references is
|
|
/// currently being accessed. If the item is removed from the pool while the guard exists, the
|
|
/// removal will be deferred until all guards are dropped.
|
|
///
|
|
/// Unlike [`Ref`], which borrows the pool, an `OwnedRef` clones the `Arc`
|
|
/// around the pool. Therefore, it keeps the pool from being dropped until all
|
|
/// such guards have been dropped. This means that an `OwnedRef` may be held for
|
|
/// an arbitrary lifetime.
|
|
///
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// ```
|
|
/// # use sharded_slab::Pool;
|
|
/// 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;
|
|
/// 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);
|
|
/// ```
|
|
///
|
|
/// `OwnedRef`s may be sent between threads:
|
|
///
|
|
/// ```
|
|
/// # use sharded_slab::Pool;
|
|
/// 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();
|
|
/// ```
|
|
///
|
|
/// [`Ref`]: crate::pool::Ref
|
|
pub struct OwnedRef<T, C = DefaultConfig>
|
|
where
|
|
T: Clear + Default,
|
|
C: cfg::Config,
|
|
{
|
|
inner: page::slot::Guard<T, C>,
|
|
pool: Arc<Pool<T, C>>,
|
|
key: usize,
|
|
}
|
|
|
|
/// An owned guard that allows exclusive, mutable access to an object in a pool.
|
|
///
|
|
/// 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`.
|
|
///
|
|
/// This is returned by [`Pool::create_owned`].
|
|
///
|
|
/// While the guard exists, it indicates to the pool that the item the guard
|
|
/// references is currently being accessed. If the item is removed from the pool
|
|
/// while the guard exists, theremoval will be deferred until all guards are
|
|
/// dropped.
|
|
///
|
|
/// Unlike [`RefMut`], which borrows the pool, an `OwnedRefMut` clones the `Arc`
|
|
/// around the pool. Therefore, it keeps the pool from being dropped until all
|
|
/// such guards have been dropped. This means that an `OwnedRefMut` may be held for
|
|
/// an arbitrary lifetime.
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// ```rust
|
|
/// # use sharded_slab::Pool;
|
|
/// # use std::thread;
|
|
/// 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();
|
|
/// ```
|
|
///
|
|
/// ```rust
|
|
/// # use sharded_slab::Pool;
|
|
/// 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;
|
|
/// 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);
|
|
/// ```
|
|
///
|
|
/// `OwnedRefMut`s may be sent between threads:
|
|
///
|
|
/// ```
|
|
/// # use sharded_slab::Pool;
|
|
/// 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 sharded_slab::Pool;
|
|
/// 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"));
|
|
/// ```
|
|
///
|
|
/// [`Pool::create_owned`]: crate::Pool::create_owned
|
|
/// [`RefMut`]: crate::pool::RefMut
|
|
/// [`OwnedRefMut`]: crate::pool::OwnedRefMut
|
|
/// [downgraded]: crate::pool::OwnedRefMut::downgrade
|
|
pub struct OwnedRefMut<T, C = DefaultConfig>
|
|
where
|
|
T: Clear + Default,
|
|
C: cfg::Config,
|
|
{
|
|
inner: page::slot::InitGuard<T, C>,
|
|
pool: Arc<Pool<T, C>>,
|
|
key: usize,
|
|
}
|
|
|
|
impl<T> Pool<T>
|
|
where
|
|
T: Clear + Default,
|
|
{
|
|
/// Returns a new `Pool` with the default configuration parameters.
|
|
pub fn new() -> Self {
|
|
Self::new_with_config()
|
|
}
|
|
|
|
/// Returns a new `Pool` with the provided configuration parameters.
|
|
pub fn new_with_config<C: cfg::Config>() -> Pool<T, C> {
|
|
C::validate();
|
|
Pool {
|
|
shards: shard::Array::new(),
|
|
_cfg: PhantomData,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<T, C> Pool<T, C>
|
|
where
|
|
T: Clear + Default,
|
|
C: cfg::Config,
|
|
{
|
|
/// 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`][res] constant.
|
|
///
|
|
/// [`Config`]: trait.Config.html
|
|
/// [res]: trait.Config.html#associatedconstant.RESERVED_BITS
|
|
/// [`Slab::insert`]: struct.Slab.html#method.insert
|
|
pub const USED_BITS: usize = C::USED_BITS;
|
|
|
|
/// 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
|
|
/// ```rust
|
|
/// # use sharded_slab::Pool;
|
|
/// # use std::thread;
|
|
/// 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();
|
|
/// ```
|
|
///
|
|
/// [`RefMut`]: crate::pool::RefMut
|
|
pub fn create(&self) -> Option<RefMut<'_, T, C>> {
|
|
let (tid, shard) = self.shards.current();
|
|
test_println!("pool: create {:?}", tid);
|
|
let (key, inner) = shard.init_with(|idx, slot| {
|
|
let guard = slot.init()?;
|
|
let gen = guard.generation();
|
|
Some((gen.pack(idx), guard))
|
|
})?;
|
|
Some(RefMut {
|
|
inner,
|
|
key: tid.pack(key),
|
|
shard,
|
|
})
|
|
}
|
|
|
|
/// 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
|
|
///
|
|
/// ```rust
|
|
/// # use sharded_slab::Pool;
|
|
/// # use std::thread;
|
|
/// 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();
|
|
/// ```
|
|
///
|
|
/// ```rust
|
|
/// # use sharded_slab::Pool;
|
|
/// 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;
|
|
/// 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);
|
|
/// ```
|
|
///
|
|
/// `OwnedRefMut`s may be sent between threads:
|
|
///
|
|
/// ```
|
|
/// # use sharded_slab::Pool;
|
|
/// 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 sharded_slab::Pool;
|
|
/// 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"));
|
|
/// ```
|
|
///
|
|
/// [`create`]: Pool::create
|
|
/// [`OwnedRef`]: crate::pool::OwnedRef
|
|
/// [`RefMut`]: crate::pool::RefMut
|
|
/// [`OwnedRefMut`]: crate::pool::OwnedRefMut
|
|
/// [downgraded]: crate::pool::OwnedRefMut::downgrade
|
|
pub fn create_owned(self: Arc<Self>) -> Option<OwnedRefMut<T, C>> {
|
|
let (tid, shard) = self.shards.current();
|
|
test_println!("pool: create_owned {:?}", tid);
|
|
let (inner, key) = shard.init_with(|idx, slot| {
|
|
let inner = slot.init()?;
|
|
let gen = inner.generation();
|
|
Some((inner, tid.pack(gen.pack(idx))))
|
|
})?;
|
|
Some(OwnedRefMut {
|
|
inner,
|
|
pool: self,
|
|
key,
|
|
})
|
|
}
|
|
|
|
/// 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
|
|
/// ```rust
|
|
/// # use sharded_slab::Pool;
|
|
/// # use std::thread;
|
|
/// 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();
|
|
/// ```
|
|
pub fn create_with(&self, init: impl FnOnce(&mut T)) -> Option<usize> {
|
|
test_println!("pool: create_with");
|
|
let mut guard = self.create()?;
|
|
init(&mut guard);
|
|
Some(guard.key())
|
|
}
|
|
|
|
/// 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
|
|
///
|
|
/// ```rust
|
|
/// # use sharded_slab::Pool;
|
|
/// 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());
|
|
/// ```
|
|
pub fn get(&self, key: usize) -> Option<Ref<'_, T, C>> {
|
|
let tid = C::unpack_tid(key);
|
|
|
|
test_println!("pool: get{:?}; current={:?}", tid, Tid::<C>::current());
|
|
let shard = self.shards.get(tid.as_usize())?;
|
|
let inner = shard.with_slot(key, |slot| slot.get(C::unpack_gen(key)))?;
|
|
Some(Ref { inner, shard, key })
|
|
}
|
|
|
|
/// 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
|
|
///
|
|
/// ```rust
|
|
/// # use sharded_slab::Pool;
|
|
/// 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;
|
|
/// 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);
|
|
/// ```
|
|
///
|
|
/// `OwnedRef`s may be sent between threads:
|
|
///
|
|
/// ```
|
|
/// # use sharded_slab::Pool;
|
|
/// 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();
|
|
/// ```
|
|
///
|
|
/// [`get`]: Pool::get
|
|
/// [`OwnedRef`]: crate::pool::OwnedRef
|
|
/// [`Ref`]: crate::pool::Ref
|
|
pub fn get_owned(self: Arc<Self>, key: usize) -> Option<OwnedRef<T, C>> {
|
|
let tid = C::unpack_tid(key);
|
|
|
|
test_println!("pool: get{:?}; current={:?}", tid, Tid::<C>::current());
|
|
let shard = self.shards.get(tid.as_usize())?;
|
|
let inner = shard.with_slot(key, |slot| slot.get(C::unpack_gen(key)))?;
|
|
Some(OwnedRef {
|
|
inner,
|
|
pool: self.clone(),
|
|
key,
|
|
})
|
|
}
|
|
|
|
/// 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
|
|
///
|
|
/// ```rust
|
|
/// # use sharded_slab::Pool;
|
|
/// 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());
|
|
/// ```
|
|
///
|
|
/// ```
|
|
/// # use sharded_slab::Pool;
|
|
/// 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);
|
|
/// ```
|
|
/// [`clear`]: #method.clear
|
|
pub fn clear(&self, key: usize) -> bool {
|
|
let tid = C::unpack_tid(key);
|
|
|
|
let shard = self.shards.get(tid.as_usize());
|
|
if tid.is_current() {
|
|
shard
|
|
.map(|shard| shard.mark_clear_local(key))
|
|
.unwrap_or(false)
|
|
} else {
|
|
shard
|
|
.map(|shard| shard.mark_clear_remote(key))
|
|
.unwrap_or(false)
|
|
}
|
|
}
|
|
}
|
|
|
|
unsafe impl<T, C> Send for Pool<T, C>
|
|
where
|
|
T: Send + Clear + Default,
|
|
C: cfg::Config,
|
|
{
|
|
}
|
|
unsafe impl<T, C> Sync for Pool<T, C>
|
|
where
|
|
T: Sync + Clear + Default,
|
|
C: cfg::Config,
|
|
{
|
|
}
|
|
|
|
impl<T> Default for Pool<T>
|
|
where
|
|
T: Clear + Default,
|
|
{
|
|
fn default() -> Self {
|
|
Self::new()
|
|
}
|
|
}
|
|
|
|
impl<T, C> fmt::Debug for Pool<T, C>
|
|
where
|
|
T: fmt::Debug + Clear + Default,
|
|
C: cfg::Config,
|
|
{
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
f.debug_struct("Pool")
|
|
.field("shards", &self.shards)
|
|
.field("config", &C::debug())
|
|
.finish()
|
|
}
|
|
}
|
|
|
|
// === impl Ref ===
|
|
|
|
impl<'a, T, C> Ref<'a, T, C>
|
|
where
|
|
T: Clear + Default,
|
|
C: cfg::Config,
|
|
{
|
|
/// Returns the key used to access this guard
|
|
pub fn key(&self) -> usize {
|
|
self.key
|
|
}
|
|
|
|
#[inline]
|
|
fn value(&self) -> &T {
|
|
unsafe {
|
|
// Safety: calling `slot::Guard::value` is unsafe, since the `Guard`
|
|
// value contains a pointer to the slot that may outlive the slab
|
|
// containing that slot. Here, the `Ref` has a borrowed reference to
|
|
// the shard containing that slot, which ensures that the slot will
|
|
// not be dropped while this `Guard` exists.
|
|
self.inner.value()
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<'a, T, C> std::ops::Deref for Ref<'a, T, C>
|
|
where
|
|
T: Clear + Default,
|
|
C: cfg::Config,
|
|
{
|
|
type Target = T;
|
|
|
|
fn deref(&self) -> &Self::Target {
|
|
self.value()
|
|
}
|
|
}
|
|
|
|
impl<'a, T, C> Drop for Ref<'a, T, C>
|
|
where
|
|
T: Clear + Default,
|
|
C: cfg::Config,
|
|
{
|
|
fn drop(&mut self) {
|
|
test_println!("drop Ref: try clearing data");
|
|
let should_clear = unsafe {
|
|
// Safety: calling `slot::Guard::release` is unsafe, since the
|
|
// `Guard` value contains a pointer to the slot that may outlive the
|
|
// slab containing that slot. Here, the `Ref` guard owns a
|
|
// borrowed reference to the shard containing that slot, which
|
|
// ensures that the slot will not be dropped while this `Ref`
|
|
// exists.
|
|
self.inner.release()
|
|
};
|
|
if should_clear {
|
|
self.shard.clear_after_release(self.key);
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<'a, T, C> fmt::Debug for Ref<'a, T, C>
|
|
where
|
|
T: fmt::Debug + Clear + Default,
|
|
C: cfg::Config,
|
|
{
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
fmt::Debug::fmt(self.value(), f)
|
|
}
|
|
}
|
|
|
|
impl<'a, T, C> PartialEq<T> for Ref<'a, T, C>
|
|
where
|
|
T: PartialEq<T> + Clear + Default,
|
|
C: cfg::Config,
|
|
{
|
|
fn eq(&self, other: &T) -> bool {
|
|
*self.value() == *other
|
|
}
|
|
}
|
|
|
|
// === impl GuardMut ===
|
|
|
|
impl<'a, T, C: cfg::Config> RefMut<'a, T, C>
|
|
where
|
|
T: Clear + Default,
|
|
C: cfg::Config,
|
|
{
|
|
/// Returns the key used to access the guard.
|
|
pub fn key(&self) -> usize {
|
|
self.key
|
|
}
|
|
|
|
/// Downgrades the mutable guard to an immutable guard, allowing access to
|
|
/// the pooled value from other threads.
|
|
///
|
|
/// ## Examples
|
|
///
|
|
/// ```
|
|
/// # use sharded_slab::Pool;
|
|
/// # use std::{sync::Arc, thread};
|
|
/// let pool = Arc::new(Pool::<String>::new());
|
|
///
|
|
/// let mut guard_mut = pool.clone().create_owned().unwrap();
|
|
/// let key = guard_mut.key();
|
|
/// guard_mut.push_str("Hello");
|
|
///
|
|
/// // The pooled string is currently borrowed mutably, so other threads
|
|
/// // may not access it.
|
|
/// let pool2 = pool.clone();
|
|
/// thread::spawn(move || {
|
|
/// assert!(pool2.get(key).is_none())
|
|
/// }).join().unwrap();
|
|
///
|
|
/// // Downgrade the guard to an immutable reference.
|
|
/// let guard = guard_mut.downgrade();
|
|
///
|
|
/// // Now, other threads may also access the pooled value.
|
|
/// let pool2 = pool.clone();
|
|
/// thread::spawn(move || {
|
|
/// let guard = pool2.get(key)
|
|
/// .expect("the item may now be referenced by other threads");
|
|
/// assert_eq!(guard, String::from("Hello"));
|
|
/// }).join().unwrap();
|
|
///
|
|
/// // We can still access the value immutably through the downgraded guard.
|
|
/// assert_eq!(guard, String::from("Hello"));
|
|
/// ```
|
|
pub fn downgrade(mut self) -> Ref<'a, T, C> {
|
|
let inner = unsafe { self.inner.downgrade() };
|
|
Ref {
|
|
inner,
|
|
shard: self.shard,
|
|
key: self.key,
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
fn value(&self) -> &T {
|
|
unsafe {
|
|
// Safety: we are holding a reference to the shard which keeps the
|
|
// pointed slot alive. The returned reference will not outlive
|
|
// `self`.
|
|
self.inner.value()
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<'a, T, C: cfg::Config> std::ops::Deref for RefMut<'a, T, C>
|
|
where
|
|
T: Clear + Default,
|
|
C: cfg::Config,
|
|
{
|
|
type Target = T;
|
|
|
|
fn deref(&self) -> &Self::Target {
|
|
self.value()
|
|
}
|
|
}
|
|
|
|
impl<'a, T, C> std::ops::DerefMut for RefMut<'a, T, C>
|
|
where
|
|
T: Clear + Default,
|
|
C: cfg::Config,
|
|
{
|
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
|
unsafe {
|
|
// Safety: we are holding a reference to the shard which keeps the
|
|
// pointed slot alive. The returned reference will not outlive `self`.
|
|
self.inner.value_mut()
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<'a, T, C> Drop for RefMut<'a, T, C>
|
|
where
|
|
T: Clear + Default,
|
|
C: cfg::Config,
|
|
{
|
|
fn drop(&mut self) {
|
|
test_println!(" -> drop RefMut: try clearing data");
|
|
let should_clear = unsafe {
|
|
// Safety: we are holding a reference to the shard which keeps the
|
|
// pointed slot alive. The returned reference will not outlive `self`.
|
|
self.inner.release()
|
|
};
|
|
if should_clear {
|
|
self.shard.clear_after_release(self.key);
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<'a, T, C> fmt::Debug for RefMut<'a, T, C>
|
|
where
|
|
T: fmt::Debug + Clear + Default,
|
|
C: cfg::Config,
|
|
{
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
fmt::Debug::fmt(self.value(), f)
|
|
}
|
|
}
|
|
|
|
impl<'a, T, C> PartialEq<T> for RefMut<'a, T, C>
|
|
where
|
|
T: PartialEq<T> + Clear + Default,
|
|
C: cfg::Config,
|
|
{
|
|
fn eq(&self, other: &T) -> bool {
|
|
self.value().eq(other)
|
|
}
|
|
}
|
|
|
|
// === impl OwnedRef ===
|
|
|
|
impl<T, C> OwnedRef<T, C>
|
|
where
|
|
T: Clear + Default,
|
|
C: cfg::Config,
|
|
{
|
|
/// Returns the key used to access this guard
|
|
pub fn key(&self) -> usize {
|
|
self.key
|
|
}
|
|
|
|
#[inline]
|
|
fn value(&self) -> &T {
|
|
unsafe {
|
|
// Safety: calling `slot::Guard::value` is unsafe, since the `Guard`
|
|
// value contains a pointer to the slot that may outlive the slab
|
|
// containing that slot. Here, the `Ref` has a borrowed reference to
|
|
// the shard containing that slot, which ensures that the slot will
|
|
// not be dropped while this `Guard` exists.
|
|
self.inner.value()
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<T, C> std::ops::Deref for OwnedRef<T, C>
|
|
where
|
|
T: Clear + Default,
|
|
C: cfg::Config,
|
|
{
|
|
type Target = T;
|
|
|
|
fn deref(&self) -> &Self::Target {
|
|
self.value()
|
|
}
|
|
}
|
|
|
|
impl<T, C> Drop for OwnedRef<T, C>
|
|
where
|
|
T: Clear + Default,
|
|
C: cfg::Config,
|
|
{
|
|
fn drop(&mut self) {
|
|
test_println!("drop OwnedRef: try clearing data");
|
|
let should_clear = unsafe {
|
|
// Safety: calling `slot::Guard::release` is unsafe, since the
|
|
// `Guard` value contains a pointer to the slot that may outlive the
|
|
// slab containing that slot. Here, the `OwnedRef` owns an `Arc`
|
|
// clone of the pool, which keeps it alive as long as the `OwnedRef`
|
|
// exists.
|
|
self.inner.release()
|
|
};
|
|
if should_clear {
|
|
let shard_idx = Tid::<C>::from_packed(self.key);
|
|
test_println!("-> shard={:?}", shard_idx);
|
|
if let Some(shard) = self.pool.shards.get(shard_idx.as_usize()) {
|
|
shard.clear_after_release(self.key);
|
|
} else {
|
|
test_println!("-> shard={:?} does not exist! THIS IS A BUG", shard_idx);
|
|
debug_assert!(std::thread::panicking(), "[internal error] tried to drop an `OwnedRef` to a slot on a shard that never existed!");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<T, C> fmt::Debug for OwnedRef<T, C>
|
|
where
|
|
T: fmt::Debug + Clear + Default,
|
|
C: cfg::Config,
|
|
{
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
fmt::Debug::fmt(self.value(), f)
|
|
}
|
|
}
|
|
|
|
impl<T, C> PartialEq<T> for OwnedRef<T, C>
|
|
where
|
|
T: PartialEq<T> + Clear + Default,
|
|
C: cfg::Config,
|
|
{
|
|
fn eq(&self, other: &T) -> bool {
|
|
*self.value() == *other
|
|
}
|
|
}
|
|
|
|
unsafe impl<T, C> Sync for OwnedRef<T, C>
|
|
where
|
|
T: Sync + Clear + Default,
|
|
C: cfg::Config,
|
|
{
|
|
}
|
|
|
|
unsafe impl<T, C> Send for OwnedRef<T, C>
|
|
where
|
|
T: Sync + Clear + Default,
|
|
C: cfg::Config,
|
|
{
|
|
}
|
|
|
|
// === impl OwnedRefMut ===
|
|
|
|
impl<T, C> OwnedRefMut<T, C>
|
|
where
|
|
T: Clear + Default,
|
|
C: cfg::Config,
|
|
{
|
|
/// Returns the key used to access this guard
|
|
pub fn key(&self) -> usize {
|
|
self.key
|
|
}
|
|
|
|
/// Downgrades the owned mutable guard to an owned immutable guard, allowing
|
|
/// access to the pooled value from other threads.
|
|
///
|
|
/// ## Examples
|
|
///
|
|
/// ```
|
|
/// # use sharded_slab::Pool;
|
|
/// # use std::{sync::Arc, thread};
|
|
/// let pool = Arc::new(Pool::<String>::new());
|
|
///
|
|
/// let mut guard_mut = pool.clone().create_owned().unwrap();
|
|
/// let key = guard_mut.key();
|
|
/// guard_mut.push_str("Hello");
|
|
///
|
|
/// // The pooled string is currently borrowed mutably, so other threads
|
|
/// // may not access it.
|
|
/// let pool2 = pool.clone();
|
|
/// thread::spawn(move || {
|
|
/// assert!(pool2.get(key).is_none())
|
|
/// }).join().unwrap();
|
|
///
|
|
/// // Downgrade the guard to an immutable reference.
|
|
/// let guard = guard_mut.downgrade();
|
|
///
|
|
/// // Now, other threads may also access the pooled value.
|
|
/// let pool2 = pool.clone();
|
|
/// thread::spawn(move || {
|
|
/// let guard = pool2.get(key)
|
|
/// .expect("the item may now be referenced by other threads");
|
|
/// assert_eq!(guard, String::from("Hello"));
|
|
/// }).join().unwrap();
|
|
///
|
|
/// // We can still access the value immutably through the downgraded guard.
|
|
/// assert_eq!(guard, String::from("Hello"));
|
|
/// ```
|
|
pub fn downgrade(mut self) -> OwnedRef<T, C> {
|
|
let inner = unsafe { self.inner.downgrade() };
|
|
OwnedRef {
|
|
inner,
|
|
pool: self.pool.clone(),
|
|
key: self.key,
|
|
}
|
|
}
|
|
|
|
fn shard(&self) -> Option<&Shard<T, C>> {
|
|
let shard_idx = Tid::<C>::from_packed(self.key);
|
|
test_println!("-> shard={:?}", shard_idx);
|
|
self.pool.shards.get(shard_idx.as_usize())
|
|
}
|
|
|
|
#[inline]
|
|
fn value(&self) -> &T {
|
|
unsafe {
|
|
// Safety: calling `slot::InitGuard::value` is unsafe, since the `Guard`
|
|
// value contains a pointer to the slot that may outlive the slab
|
|
// containing that slot. Here, the `OwnedRefMut` has an `Arc` clone of
|
|
// the shard containing that slot, which ensures that the slot will
|
|
// not be dropped while this `Guard` exists.
|
|
self.inner.value()
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<T, C> std::ops::Deref for OwnedRefMut<T, C>
|
|
where
|
|
T: Clear + Default,
|
|
C: cfg::Config,
|
|
{
|
|
type Target = T;
|
|
|
|
fn deref(&self) -> &Self::Target {
|
|
self.value()
|
|
}
|
|
}
|
|
|
|
impl<T, C> std::ops::DerefMut for OwnedRefMut<T, C>
|
|
where
|
|
T: Clear + Default,
|
|
C: cfg::Config,
|
|
{
|
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
|
unsafe {
|
|
// Safety: calling `slot::InitGuard::value_mut` is unsafe, since the
|
|
// `Guard` value contains a pointer to the slot that may outlive
|
|
// the slab containing that slot. Here, the `OwnedRefMut` has an
|
|
// `Arc` clone of the shard containing that slot, which ensures that
|
|
// the slot will not be dropped while this `Guard` exists.
|
|
self.inner.value_mut()
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<T, C> Drop for OwnedRefMut<T, C>
|
|
where
|
|
T: Clear + Default,
|
|
C: cfg::Config,
|
|
{
|
|
fn drop(&mut self) {
|
|
test_println!("drop OwnedRefMut: try clearing data");
|
|
let should_clear = unsafe {
|
|
// Safety: calling `slot::Guard::release` is unsafe, since the
|
|
// `Guard` value contains a pointer to the slot that may outlive the
|
|
// slab containing that slot. Here, the `OwnedRefMut` owns an `Arc`
|
|
// clone of the pool, which keeps it alive as long as the
|
|
// `OwnedRefMut` exists.
|
|
self.inner.release()
|
|
};
|
|
if should_clear {
|
|
if let Some(shard) = self.shard() {
|
|
shard.clear_after_release(self.key);
|
|
} else {
|
|
test_println!("-> shard does not exist! THIS IS A BUG");
|
|
debug_assert!(std::thread::panicking(), "[internal error] tried to drop an `OwnedRefMut` to a slot on a shard that never existed!");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<T, C> fmt::Debug for OwnedRefMut<T, C>
|
|
where
|
|
T: fmt::Debug + Clear + Default,
|
|
C: cfg::Config,
|
|
{
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
fmt::Debug::fmt(self.value(), f)
|
|
}
|
|
}
|
|
|
|
impl<T, C> PartialEq<T> for OwnedRefMut<T, C>
|
|
where
|
|
T: PartialEq<T> + Clear + Default,
|
|
C: cfg::Config,
|
|
{
|
|
fn eq(&self, other: &T) -> bool {
|
|
*self.value() == *other
|
|
}
|
|
}
|
|
|
|
unsafe impl<T, C> Sync for OwnedRefMut<T, C>
|
|
where
|
|
T: Sync + Clear + Default,
|
|
C: cfg::Config,
|
|
{
|
|
}
|
|
|
|
unsafe impl<T, C> Send for OwnedRefMut<T, C>
|
|
where
|
|
T: Sync + Clear + Default,
|
|
C: cfg::Config,
|
|
{
|
|
}
|