Skip to main content

Group

Struct Group 

Source
pub struct Group<K, V, S = RandomState> { /* private fields */ }
Expand description

A namespace that coalesces concurrent work by key.

Different keys run independently. A group neither caches completed results nor limits overall concurrency.

Implementations§

Source§

impl<K, V> Group<K, V, RandomState>
where K: Eq + Hash, V: Clone,

Source

pub fn new() -> Self

Creates a new Group with the default hasher.

Source§

impl<K, V, S> Group<K, V, S>
where K: Eq + Hash, V: Clone, S: BuildHasher,

Source

pub fn with_hasher(hasher: S) -> Self

Creates a new Group with the given hasher.

Source

pub async fn work<F>(&self, key: K, func: F) -> V
where F: AsyncFnOnce() -> V,

Executes and returns the results of the given function, making sure that only one execution is in-flight for a given key at a time.

If a duplicate comes in, the duplicate caller waits for the original to complete and receives the same results.

If the computation is cancelled or panics, another caller waiting for the same key may retry it.

Once the function completes, the key, if not forgotten, is removed from the group, allowing future calls with the same key to execute the function again.

§Deadlocks

The function must not recursively call work or try_work for the same current key because it would wait for its own result. Work for other keys remains independent.

§Examples
use std::sync::Arc;
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering;
use std::time::Duration;

use asyncband::singleflight::Group;

let group = Group::new();
let counter = Arc::new(AtomicUsize::new(0));

let c1 = counter.clone();
let fut1 = group.work("key", || async move {
    c1.fetch_add(1, Ordering::SeqCst);
    // simulate heavy work to avoid immediate completion
    tokio::time::sleep(Duration::from_millis(100)).await;
    "result"
});

let c2 = counter.clone();
let fut2 = group.work("key", || async move {
    c2.fetch_add(1, Ordering::SeqCst);
    // simulate heavy work to avoid immediate completion
    tokio::time::sleep(Duration::from_millis(100)).await;
    "result"
});

let (r1, r2) = tokio::join!(fut1, fut2);

assert_eq!(r1, "result");
assert_eq!(r2, "result");
assert_eq!(counter.load(Ordering::SeqCst), 1);
Source

pub async fn try_work<E, F>(&self, key: K, func: F) -> Result<V, E>
where F: AsyncFnOnce() -> Result<V, E>,

Executes and returns the results of the given function, making sure that only one execution is in-flight for a given key at a time.

If a duplicate comes in, the duplicate caller waits for the original to complete and receives the same results.

If the computation returns an error, it is returned to that caller. After an error, cancellation, or panic, another caller may retry the computation.

Once the function completes successfully, the key, if not forgotten, is removed from the group, allowing future calls with the same key to execute the function again.

§Deadlocks

The function must not recursively call work or try_work for the same current key because it would wait for its own result. Work for other keys remains independent.

§Examples
use std::sync::Arc;
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering;
use std::time::Duration;

use asyncband::singleflight::Group;

let group = Group::new();

let fut1 = group.try_work("key", || async move {
    // simulate heavy work to avoid immediate completion
    tokio::time::sleep(Duration::from_millis(100)).await;
    Err::<_, &'static str>("fut1")
});

let fut2 = group.try_work("key", || async move {
    // simulate heavy work to avoid immediate completion
    tokio::time::sleep(Duration::from_millis(200)).await;
    Ok::<_, &'static str>("fut2")
});

let (r1, r2) = tokio::join!(fut1, fut2);

assert_eq!(r1, Err("fut1"));
assert_eq!(r2, Ok("fut2"));
Source

pub fn forget<Q>(&self, key: &Q)
where K: Borrow<Q>, Q: Hash + Eq + ?Sized,

Forgets about the given key.

Future calls to work for this key will call the function rather than waiting for an earlier call to complete. Existing calls to work for this key are not affected.

Trait Implementations§

Source§

impl<K, V, S> Debug for Group<K, V, S>

Source§

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

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

impl<K, V, S> Default for Group<K, V, S>
where K: Eq + Hash, V: Clone, S: BuildHasher + Default,

Source§

fn default() -> Self

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

Auto Trait Implementations§

§

impl<K, V, S = RandomState> !Freeze for Group<K, V, S>

§

impl<K, V, S> RefUnwindSafe for Group<K, V, S>
where Mutex<HashTable<Arc<Entry<K, V>>>>: RefUnwindSafe, S: RefUnwindSafe,

§

impl<K, V, S> Send for Group<K, V, S>
where Mutex<HashTable<Arc<Entry<K, V>>>>: Send, S: Send,

§

impl<K, V, S> Sync for Group<K, V, S>
where Mutex<HashTable<Arc<Entry<K, V>>>>: Sync, S: Sync,

§

impl<K, V, S> Unpin for Group<K, V, S>
where Mutex<HashTable<Arc<Entry<K, V>>>>: Unpin, S: Unpin,

§

impl<K, V, S> UnsafeUnpin for Group<K, V, S>
where Mutex<HashTable<Arc<Entry<K, V>>>>: UnsafeUnpin, S: UnsafeUnpin,

§

impl<K, V, S> UnwindSafe for Group<K, V, S>
where Mutex<HashTable<Arc<Entry<K, V>>>>: UnwindSafe, S: UnwindSafe,

Blanket Implementations§

Source§

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

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

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

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where 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 T
where 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 T
where U: Into<T>,

Source§

type Error = !

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

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

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

Source§

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.