Macro async_condvar_fair::RelockMutexGuard
source · macro_rules! RelockMutexGuard { { $struct:ident ($($guard:tt)+) $(,)? [$($mutex:tt)+] $(,)? $l:pat => $lock:expr, $g:pat => $get_mutex:expr $( , where $xbound:path )* $(,)? } => { ... }; { $struct:ident ($guard:ident $(:: $guardx:ident)*, $($mutex:tt)+) $(,)? $l:pat => $lock:expr $( , where $xbound:path )* $(,)? } => { ... }; { ($($guard:tt)+) $(,)? [$($mutex:tt)+] $(,)? $l:ident => $lock:expr, $g:ident => $get_mutex:expr $( , where $xbound:path )* $(,)? } => { ... }; { ($guard:ident $(:: $guardx:ident)*, $($mutex:tt)+) $(,)? $l:ident => $lock:expr $( , where $xbound:path )* $(,)? } => { ... }; { < $($gen_lf0:lifetime, $($gen_lf1:lifetime,)*)? $($gen_ty:ident),* > $(,)? ( $guard_in:ty ) $(,)? [ $mutexref:ty, $guard_out:path ] $(,)? $l:pat => $lock:expr , $g:pat => $get_mutex:expr , where $t:ident : $($bound:tt)* } => { ... }; }
Expand description
Implements RelockMutexGuard
(needed for any mutexes used
with a condvar)
§Summary
Helper macro to impl
RelockMutexGuard
, for various mutex
types. RelockMutexGuard!
has five forms, for five use cases:
- Third-party impl, convenient mutex: mutex ref recoverable from guard
- Third-party impl, inconveneint mutex ref passed separately
- First or second party impl, convenient mutex
- Within
async_condvar_fair
, inconvenient mutex - Explicitly specify type and lifetime parameters.
An alternative to implementing RelockMutexGuard
(via this
macro or otherwisse) is to use wait_no_relock
everywhere.
§Trait coherence, first/second party vs third party
The firt or second party forms can only be used in the crate
defining the mutex guard type (or within async_condvar_fair
),
because of Rust’s trait coherence rules.
The third party forms define a helper struct, for pasing to
wait_baton
or wait
.
§Convenient vs inconvenient mutexes
Ideally, mutex guards implement a method for recovering a reference to the original mutex. This saves users who need to unlock and relock a mutex (like anyone uusing a condition variable) from having to carry a separate copy of a reference to the mutex.
For convenient mutexes, you can just pass the guard to
wait_baton
or wait
. For inconvenient mutexes, you must
also pass a reference to the uneerlying mutex - typically, as
element .1
of a tuple or tuple struct.
§Examples
§Third party impl, convenient mutex
RelockMutexGuard!{
NiceGuardForCondvar(nice::MutexGuard) [nice::Mutex],
l => async move { l.lock() },
g => nice::MutexGuard::mutex(&g),
}
condvar.wait_baton(NiceGuardForCondvar(guard));
Macro expansion:
struct NiceGuardForCondvar<'l,T>(nice::MutexGuard<'l,T>);
impl<'l,T> RelockMutexGuard for NiceGuardForCondvar<'l,T> {/*...*/}
async fn wait(&self, NiceGuardForCondvar<'l,T>) -> nice::MutexGuard<'l,T>; //roughly
The expression after l =>
must be a a future, such as an async
block or an un-await
ed call to an async function. l
will have
type &'l MutexMT>
. When awaited, the lock expression must yield
Guard<'l, T>
.
The expression after g =>
must recover the mutex reference. g
will have type Guard<'l, T>
, and the expression must have type
&'l Mutex<T>
.
The mutex recovery expression is given ownership of the guard, but
it should discard it. (The expression forms the core of the
generated implementation of unlock_for_relock
.)
The optional $xbound
s are additional bounds on T
(each with
their own where
, contrary to normal Rust syntax). (This is
needed, for example, for RwLockReadGuard
, which needs T: Send
.)
§Third party impl, inconvenient mutex
RelockMutexGuard!{
AwkwardGuardForCondvar(awkward::MutexGuard, awkward::Mutex),
l => async move { l.lock() },
}
condvar.wait_baton(AwkwardGuardForCondvar(guard, &mutex));
Macro expansion:
struct AwkwardGuardForCondvar<'o,'i,T>(
awkward::MutexGuard<'i,T>,
&'o awkward::Mutex<T>,
);
impl<'o,'i,T> RelockMutexGuard for AwkwardGuardForCondvar<'o,'i,T> {/*...*/}
async fn wait(&self, AwkwardGuardForCondvar<'o,'i,T>) -> awkward::MutexGuard<'o,T>; //roughly
§First and second-party impl’s
If you are invoking RelockMutexGuard!
in the same crate as
defines a convenient mutex, or within async_condvar_fair
, omit
the struct name.
This will implement the trait directly for the guard type, or the pair consisting of the guard and the mutex reference:
§First or second party impl, convenient mutex
impl<'l,T> MutexGuard<'l,T> {
fn mutex(self_: &Self) -> &'l Mutex<T> { todo!() }
}
RelockMutexGuard!{
(MutexGuard) [Mutex],
l => async move { l.lock() },
g => MutexGuard::mutex(&g),
}
Generates:
impl<'l,T> RelockMutexGuard for MutexGuard<'l,T> {/*...*/}
async fn wait(&self, MutexGuard<'l,T>) -> MutexGuard<'l,T>; //roughly
§Within async_condvar_fair
, inconvenient mutex
RelockMutexGuard!{
(MutexGuard, Mutex),
l => async move { l.lock() },
}
Generates:
impl<'o,'i,T> RelockMutexGuard for (MutexGuard<'i,T>, &'o Mutex<T>) {/*...*/}
async fn wait(&self, (MutexGuard<'i,T>, &'o Mutex<T>)) -> MutexGuard<'o,T>; //roughly
§Explicit type and lifetime parameters
The other four forms all assume that the unlocked mutex is &...
and that the mutex is a wrapper type directly around T
. For other
situations, for example Arc
-based owned guards, the explicit form
is needed.
The other four forms are convenience wrappers that all expand to the explicit form.
RelockMutexGuard!{
<T>
(smol::lock::MutexGuardArc<T>)
[std::sync::Arc<smol::lock::Mutex<T>>, smol::lock::MutexGuardArc<T>],
l => async move { l.lock_arc().await },
g => smol::lock::MutexGuardArc::source(&g).clone(),
where T: 'static
}
$guard_in
is the argument to wait
. $guard_out
is the output
of the future. $mutexref
is the intermediate, unlocked, form.
The first lifetime in the generic arguments (if there are any
lifetimes) becomes a bound on LockFuture
.
$t: $bounds
is expanded as $t : std::marker::Send + $bounds
.