Struct Pool

pub struct Pool<T, F = fn() -> T>(/* private field */);

A thread safe pool that works in an alloc-only context.

Getting a value out comes with a guard. When that guard is dropped, the value is automatically put back in the pool. The guard provides both a Deref and a DerefMut implementation for easy access to an underlying T.

A Pool impls Sync when T is Send (even if T is not Sync). This is possible because a pool is guaranteed to provide a value to exactly one thread at any time.

Currently, a pool never contracts in size. Its size is proportional to the maximum number of simultaneous uses. This may change in the future.

A Pool is a particularly useful data structure for this crate because many of the regex engines require a mutable "cache" in order to execute a search. Since regexes themselves tend to be global, the problem is then: how do you get a mutable cache to execute a search? You could:

  1. Use a thread_local!, which requires the standard library and requires that the regex pattern be statically known.
  2. Use a Pool.
  3. Make the cache an explicit dependency in your code and pass it around.
  4. Put the cache state in a Mutex, but this means only one search can execute at a time.
  5. Create a new cache for every search.

A thread_local! is perhaps the best choice if it works for your use case. Putting the cache in a mutex or creating a new cache for every search are perhaps the worst choices. Of the remaining two choices, whether you use this Pool or thread through a cache explicitly in your code is a matter of taste and depends on your code architecture.

Warning: may use a spin lock

When this crate is compiled without the std feature, then this type may used a spin lock internally. This can have subtle effects that may be undesirable. See Spinlocks Considered Harmful for a more thorough treatment of this topic.

Example

This example shows how to share a single hybrid regex among multiple threads, while also safely getting exclusive access to a hybrid's Cache without preventing other searches from running while your thread uses the Cache.

use regex_automata::{
    hybrid::regex::{Cache, Regex},
    util::{lazy::Lazy, pool::Pool},
    Match,
};

static RE: Lazy<Regex> =
    Lazy::new(|| Regex::new("foo[0-9]+bar").unwrap());
static CACHE: Lazy<Pool<Cache>> =
    Lazy::new(|| Pool::new(|| RE.create_cache()));

let expected = Some(Match::must(0, 3..14));
assert_eq!(expected, RE.find(&mut CACHE.get(), b"zzzfoo12345barzzz"));

Implementations

impl<T, F> Pool<T, F>

fn new(create: F) -> Pool<T, F>

Create a new pool. The given closure is used to create values in the pool when necessary.

fn with_capacity(capacity: usize, create: F) -> Pool<T, F>

Create a new pool. The given closure is used to create values in the pool when necessary.

When the std feature is enabled, a Pool is thread-aware and spreads its memory out across multiple cache lines. The number of cache lines is determined by the capacity parameter passed here. By default, a fixed reasonable number is used. A smaller number means less memory is used, but a higher number means there may be less contention on this pool in highly threaded environments doing a lot of searches using the same Regex value.

When std is not enabled, then the capacity parameter is ignored because the underlying pool implementation is not thread-aware.

The capacity must be at least 1. If it's less than 1, then it is forced to be 1.

fn with_available_parallelism_capacity(create: F) -> Pool<T, F>

Create a new pool. The given closure is used to create values in the pool when necessary.

This is a convenience routine for calling Pool::with_capacity with a number equivalent to the available parallelism for this environment.

If std is not enabled or if the query for available parallelism failed, then this is equivalent to calling Pool::new.

impl<T: Send, F: Fn() -> T> Pool<T, F>

fn get(&self) -> PoolGuard<'_, T, F>

Get a value from the pool. The caller is guaranteed to have exclusive access to the given value. Namely, it is guaranteed that this will never return a value that was returned by another call to get but was not put back into the pool.

When the guard goes out of scope and its destructor is called, then it will automatically be put back into the pool. Alternatively, PoolGuard::put may be used to explicitly put it back in the pool without relying on its destructor.

Note that there is no guarantee provided about which value in the pool is returned. That is, calling get, dropping the guard (causing the value to go back into the pool) and then calling get again is not guaranteed to return the same value received in the first get call.

Trait Implementations

impl<T: Debug, F> Debug for Pool<T, F>

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

Auto Trait Implementations

impl<T, F> Freeze for Pool<T, F> where Box<Pool<T, F>>: Freeze,

impl<T, F> RefUnwindSafe for Pool<T, F> where Box<Pool<T, F>>: RefUnwindSafe,

impl<T, F> Send for Pool<T, F> where Box<Pool<T, F>>: Send,

impl<T, F> Sync for Pool<T, F> where Box<Pool<T, F>>: Sync,

impl<T, F> Unpin for Pool<T, F> where Box<Pool<T, F>>: Unpin,

impl<T, F> UnsafeUnpin for Pool<T, F> where Box<Pool<T, F>>: UnsafeUnpin,

impl<T, F> UnwindSafe for Pool<T, F> where Box<Pool<T, F>>: UnwindSafe,

Blanket Implementations

impl<T> Any for Pool<T, F> where T: 'static + ?Sized,

fn type_id(&self) -> TypeId

impl<T> Borrow<T> for Pool<T, F> where T: ?Sized,

fn borrow(&self) -> &T

impl<T> BorrowMut<T> for Pool<T, F> where T: ?Sized,

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

impl<T> From<T> for Pool<T, F>

fn from(t: T) -> T

Returns the argument unchanged.

impl<T, U> Into<U> for Pool<T, F> where U: From<T>,

fn into(self) -> U

Calls U::from(self).

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

impl<T, U> TryFrom<U> for Pool<T, F> where U: Into<T>,

type Error = never;
fn try_from(value: U) -> Result<T, never>

impl<T, U> TryInto<U> for Pool<T, F> where U: TryFrom<T>,

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