async-condvar-fair

Condition variables for async Rust. Features:

Main entrypoint

The main entrypoint to this library is [Condvar]. See its documentation for details of the constructors and methods available.

Example

``` use std::collections::VecDeque; use asynccondvarfair::{Condvar, BatonExt}; use parking_lot::Mutex;

struct Shared { cv: Condvar, queue: Mutex>, }

impl Shared { pub async fn procssor(&self) { let mut guard = self.queue.lock(); let mut baton = None; loop { while let Some(entry) = guard.popfront() { println!("processing {:?}", &entry); if entry.isempty() { break } } baton.dispose();

        let got = self.cv.wait_baton(guard).await;
        guard = got.0;
        baton = got.1;
    }
}

}

```

Mutexes - choosing a mutex, sync vs async

Note that it can make perfect sense to have an async condvar, but protect the shared state with a sync mutex. If the operations which occur while holding the mutex are fast (and, in particular, do not perform IO), this is usually the best choice.

[parking_lot::Mutex] is a good choice for such a sync mutex, and is conveniently supported by async-condvar-fair.

If you use a sync mutex, you probably don't intend to be awaiting with the mutex held, so you can probably use plain [wait] (rather than [wait_baton]).

If the operations you do while holding the mutex are slow, you should use the async mutex supplied with your async runtime. In this case, if you are using [notify_one] you should consider using [wait_baton] to avoid task cancellation causing lost notifications: see [Baton].

Mutexes - how to pass the mutex to wait_baton et al

[Condvar::wait_baton] and [wait] can in principle work with any mutex. But they need to know how to relock the mutex.

For the most convenient mutexes, like [parking_lot::Mutex], can just pass the guard to wait. Condvar will use the guard to unlock the mutex, and then to relock it again during wakeup.

But for many mutexes, this is not possible, because the guard type does not provide a way to get back to the unlocked mutex reference. This is the case, for example, for [std::sync::Mutex].

For these inconvenient mutexes, you can pass a tuple to [wait_baton] or [wait], or use [wait_no_relock] and relock the mutex yourself.

| feature | mutex type | pass to [wait] / [wait_baton] | | -------------- | ---------------------- | ------------------------------- | | always enabled | [parking_lot::Mutex] | MutexGuard | | always enabled | [parking_lot::FairMutex] | FairMutexGuard | | always enabled | [std::sync::Mutex] | (MutexGuard, &Mutex) | | tokio | [tokio::sync::Mutex] | (MutexGuard, &Mutex) | | tokio | [Arc]<[tokio::sync::Mutex]> | (OwnedMutexGuard, Arc<Mutex>) | | tokio | [tokio::sync::RwLock] | (RwLockReadGuard, &Mutex) | | tokio | [tokio::sync::RwLock] | (RwLockWriteGuard, &Mutex) | | tokio | [Arc]<[tokio::sync::RwLock]> | (OwnedRwLockReadGuard, Arc<RwLock>) | | tokio | [Arc]<[tokio::sync::RwLock]> | (OwnedRwLockWriteGuard, Arc<RwLock>) | | smol | [smol::lock::Mutex] | MutexGuard | | smol | [smol::lock::RwLock] | (RwLockReadGuard, &RwLock) | | smol | [smol::lock::RwLock] | (RwLockWriteGuard, &RwLock) | | smol | [Arc]<[smol::lock::Mutex]> | MutexGuardArc |

This is all achieved through provided implementations of the [RelockMutexGuard] trait.

If you want to use a mutex type without builtin support in async-condvar-wait, use the [RelockMutexGuard!] macro to define a suitable impl, define that impl by hand, or use [wait_no_relock] and relock the mutex yourself each time.

Example of passing a tuple of guard and mutex

```

use std::collections::VecDeque;

use asynccondvarfair::{Condvar, BatonExt};

use std::sync::Mutex; struct Shared { cv: Condvar, queue: Mutex>, }

impl Shared {

pub async fn procssor(&self) {

let mut guard = self.queue.lock().unwrap();

let mut baton = None;

loop { while let Some(entry) = guard.popfront() { println!("processing {:?}", &entry); if entry.isempty() { break } } baton.dispose();

let got = self.cv.wait_baton((guard, &self.queue)).await;
guard = got.0;
baton = got.1;

}

}

}

```