Macro async_condvar_fair::RelockMutexGuard [−][src]
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_lf: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<'l,T>( awkward::MutexGuard<'l,T>, &'l awkward::Mutex<T>, ); impl<'l,T> RelockMutexGuard for AwkwardGuardForCondvar<'l,T> {/*...*/} async fn wait(&self, AwkwardGuardForCondvar<'l,T>) -> awkward::MutexGuard<'l,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<'l,T> RelockMutexGuard for (MutexGuard<'l,T>, &'l Mutex<T>) {/*...*/} async fn wait(&self, (MutexGuard<'l,T>, &'l Mutex<T>)) -> MutexGuard<'l,T>; //roughly
Explicit type and lifetime parameters
The other four forms all assume that the unlocked mutex is &'l ...
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.
$t: $bounds
is expanded as $t : std::marker::Send + $bounds
.