Skip to main content

lock_api/
condvar.rs

1use crate::mutex::{MutexGuard, RawMutex, RawMutexTimed};
2use core::{fmt, ops::DerefMut};
3
4/// Basic operations for a condition variable associated with a particular
5/// [`RawMutex`] implementation.
6///
7/// Types implementing this trait can be used by [`Condvar`] to form a safe
8/// condition variable type.
9///
10/// Implementations must document whether they guarantee no spurious wakeups. If
11/// they do, notification methods report exactly how many threads were notified.
12/// Otherwise, notification methods must return `false` or `0`, and all
13/// non-timeout wakeups are treated as potentially spurious. In either case,
14/// notification methods must still wake waiting threads as specified.
15///
16/// # Safety
17///
18/// [`wait`](RawCondvar::wait) must atomically unlock the mutex and begin
19/// waiting with respect to calls to [`notify_one`](RawCondvar::notify_one) and
20/// [`notify_all`](RawCondvar::notify_all), then re-lock the mutex before it
21/// returns.
22///
23/// If a wait panics after unlocking the mutex, it must re-lock the mutex before
24/// unwinding, and replace `guard` with the state of the new acquisition.
25/// `guard` must remain valid on both normal return and unwind. If restoring
26/// ownership or valid guard state fails, the implementation must abort.
27/// Implementations which cannot wait on distinct mutexes simultaneously may
28/// panic when asked to do so, but must not cause undefined behavior.
29pub unsafe trait RawCondvar {
30    /// Initial value for a new condvar.
31    // A “non-constant” const item is a legacy way to supply an initialized value to downstream
32    // static items. Can hopefully be replaced with `const fn new() -> Self` at some point.
33    #[allow(clippy::declare_interior_mutable_const)]
34    const INIT: Self;
35
36    /// The type of [`RawMutex`] this condvar can work with.
37    type RawMutex: RawMutex;
38
39    /// Atomically unlocks the mutex and waits, then re-locks the mutex before
40    /// returning.
41    ///
42    /// This method may return spuriously. See the implementation's
43    /// documentation for any stronger guarantees.
44    ///
45    /// # Safety
46    ///
47    /// The caller must logically hold `mutex`, with `guard` containing the
48    /// state of that acquisition. The protected data must not be accessed from
49    /// the time this method unlocks the mutex until it re-locks it.
50    unsafe fn wait(&self, mutex: &Self::RawMutex, guard: &mut <Self::RawMutex as RawMutex>::Guard);
51
52    /// Notifies one waiting thread.
53    ///
54    /// If the implementation guarantees no spurious wakeups, returns whether
55    /// this call notified a waiting thread. Otherwise, must return `false`.
56    /// Threads whose waits end because of a timeout are not counted.
57    fn notify_one(&self) -> bool;
58
59    /// Notifies all waiting threads.
60    ///
61    /// If the implementation guarantees no spurious wakeups, returns the exact
62    /// number of waiting threads this call notified. Otherwise, must return `0`.
63    /// Threads whose waits end because of a timeout are not counted.
64    fn notify_all(&self) -> usize;
65}
66
67/// Additional methods for condition variables which support timeouts.
68///
69/// # Safety
70///
71/// Implementations must uphold the safety requirements of [`RawCondvar`] for
72/// the additional methods provided by this trait.
73pub unsafe trait RawCondvarTimed: RawCondvar
74where
75    Self::RawMutex: RawMutexTimed,
76{
77    /// Converts a relative timeout into an absolute timeout.
78    ///
79    /// Returns `None` if the resulting instant cannot be represented.
80    fn checked_duration_to_instant(
81        timeout: &<Self::RawMutex as RawMutexTimed>::Duration,
82    ) -> Option<<Self::RawMutex as RawMutexTimed>::Instant>;
83
84    /// Atomically unlocks the mutex and waits for a notification, a spurious
85    /// wakeup, or the timeout to be reached, then re-locks the mutex before
86    /// returning.
87    ///
88    /// Returns `true` if the wait is known to have timed out and `false`
89    /// otherwise. See the [`RawCondvar`] implementation's documentation for
90    /// whether a `false` result guarantees that a notification was received.
91    ///
92    /// A timed-out wait must not return before `timeout`, but may return later
93    /// due to scheduling or platform-specific behavior.
94    ///
95    /// # Safety
96    ///
97    /// The caller must uphold the safety requirements of
98    /// [`RawCondvar::wait`].
99    unsafe fn wait_until(
100        &self,
101        mutex: &Self::RawMutex,
102        guard: &mut <Self::RawMutex as RawMutex>::Guard,
103        timeout: &<Self::RawMutex as RawMutexTimed>::Instant,
104    ) -> bool;
105
106    /// Atomically unlocks the mutex and waits for a notification, a spurious
107    /// wakeup, or the timeout to be reached, then re-locks the mutex before
108    /// returning.
109    ///
110    /// Returns `true` if the wait is known to have timed out and `false`
111    /// otherwise. See the [`RawCondvar`] implementation's documentation for
112    /// whether a `false` result guarantees that a notification was received.
113    ///
114    /// A timed-out wait must not return before `timeout` has elapsed, but may
115    /// return later due to scheduling or platform-specific behavior.
116    ///
117    /// # Safety
118    ///
119    /// The caller must uphold the safety requirements of
120    /// [`RawCondvar::wait`].
121    unsafe fn wait_for(
122        &self,
123        mutex: &Self::RawMutex,
124        guard: &mut <Self::RawMutex as RawMutex>::Guard,
125        timeout: &<Self::RawMutex as RawMutexTimed>::Duration,
126    ) -> bool {
127        // SAFETY: `RawCondvar::wait` and `RawCondvarTimed::wait_until` have the
128        // same safety condition as this function, which is assured by the caller.
129        unsafe {
130            match Self::checked_duration_to_instant(timeout) {
131                Some(timeout) => self.wait_until(mutex, guard, &timeout),
132                None => {
133                    // No absolute deadline can be constructed, so fall back to
134                    // an untimed wait.
135                    <Self as RawCondvar>::wait(self, mutex, guard);
136                    false
137                }
138            }
139        }
140    }
141}
142
143/// A type indicating whether a timed wait on a condition variable timed out.
144#[derive(#[automatically_derived]impl ::core::fmt::Debug for WaitTimeoutResult {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_tuple_field1_finish(f,
            "WaitTimeoutResult", &&self.0)
    }
}Debug, #[automatically_derived]impl ::core::marker::StructuralPartialEq for
    WaitTimeoutResult {
}
#[automatically_derived]impl ::core::cmp::PartialEq for WaitTimeoutResult {
    #[inline]
    fn eq(&self, other: &Self) -> bool { self.0 == other.0 }
}PartialEq, #[automatically_derived]impl ::core::cmp::Eq for WaitTimeoutResult {
    #[inline]
    #[doc(hidden)]
    #[coverage(off)]
    fn assert_fields_are_eq(&self) {
        let _: ::core::cmp::AssertParamIsEq<bool>;
    }
}Eq, #[automatically_derived]impl ::core::marker::Copy for WaitTimeoutResult { }Copy, #[automatically_derived]#[doc(hidden)]unsafe impl ::core::clone::TrivialClone
    for WaitTimeoutResult {
}
#[automatically_derived]impl ::core::clone::Clone for WaitTimeoutResult {
    #[inline]
    fn clone(&self) -> Self {
        let _: ::core::clone::AssertParamIsClone<bool>;
        *self
    }
}Clone)]
145pub struct WaitTimeoutResult(bool);
146
147impl WaitTimeoutResult {
148    /// Returns `true` if the wait was known to have timed out.
149    #[inline]
150    pub const fn timed_out(self) -> bool {
151        self.0
152    }
153}
154
155/// A condition variable.
156///
157/// Condition variables represent the ability to block a thread such that it
158/// consumes no CPU time while waiting for an event to occur. Condition
159/// variables are typically associated with a boolean predicate (a condition)
160/// and a mutex. The predicate is always verified inside of the mutex before
161/// determining that thread must block.
162///
163/// If the underlying [`RawCondvar`] guarantees no spurious wakeups, notification
164/// return values are exact. Otherwise, they are always `false` or `0`. Consult
165/// the documentation of `C` for whether it provides this guarantee.
166pub struct Condvar<C> {
167    inner: C,
168}
169
170impl<C: RawCondvar> Condvar<C> {
171    /// Creates a new condition variable which is ready to be waited on and
172    /// notified.
173    #[inline]
174    pub const fn new() -> Condvar<C> {
175        Condvar { inner: C::INIT }
176    }
177
178    /// Returns the underlying raw condvar object.
179    ///
180    /// Note that you will most likely need to import the `RawCondvar` trait from
181    /// `lock_api` to be able to call functions on the raw condvar.
182    #[inline]
183    pub fn raw(&self) -> &C {
184        &self.inner
185    }
186
187    /// Wakes up one blocked thread on this condvar.
188    ///
189    /// If `C` guarantees no spurious wakeups, returns whether this call notified
190    /// a waiting thread. Otherwise, always returns `false`. Threads whose waits
191    /// end because of a timeout are not counted. Consult the documentation of
192    /// `C` for whether it provides this guarantee.
193    ///
194    /// If there is a blocked thread on this condition variable, then it will
195    /// be woken up from its call to [`wait`](Self::wait),
196    /// [`wait_for`](Self::wait_for), or [`wait_until`](Self::wait_until).
197    /// Calls to `notify_one` are not buffered in any way.
198    ///
199    /// To wake up all threads, see [`notify_all`](Self::notify_all).
200    #[inline]
201    pub fn notify_one(&self) -> bool {
202        self.inner.notify_one()
203    }
204
205    /// Wakes up all blocked threads on this condvar.
206    ///
207    /// If `C` guarantees no spurious wakeups, returns the exact number of
208    /// waiting threads this call notified. Otherwise, always returns `0`.
209    /// Threads whose waits end because of a timeout are not counted. Consult
210    /// the documentation of `C` for whether it provides this guarantee.
211    ///
212    /// This method will ensure that any current waiters on the condition
213    /// variable are awoken. Calls to `notify_all` are not buffered in any way.
214    ///
215    /// To wake up only one thread, see [`notify_one`](Self::notify_one).
216    #[inline]
217    pub fn notify_all(&self) -> usize {
218        self.inner.notify_all()
219    }
220
221    /// Blocks the current thread until this condition variable receives a
222    /// notification.
223    ///
224    /// This function will atomically unlock the mutex specified (represented by
225    /// `mutex_guard`) and block the current thread. This means that calls to
226    /// [`notify_one`](Self::notify_one) or [`notify_all`](Self::notify_all)
227    /// which happen logically after the mutex is unlocked are candidates to
228    /// wake this thread. When this function returns, the lock will have been
229    /// re-acquired.
230    ///
231    /// This function may return spuriously depending on the underlying
232    /// [`RawCondvar`]. Consult the documentation of `C` for its guarantees.
233    ///
234    /// # Panics
235    ///
236    /// The underlying [`RawCondvar`] implementation may panic if another thread
237    /// is waiting on this condition variable with a different mutex.
238    #[inline]
239    pub fn wait<T: ?Sized>(&self, mutex_guard: &mut MutexGuard<'_, C::RawMutex, T>) {
240        // SAFETY: The exclusively borrowed guard holds the mutex; wait restores it.
241        unsafe {
242            self.inner
243                .wait(MutexGuard::mutex(mutex_guard).raw(), &mut mutex_guard.guard);
244        }
245    }
246
247    /// Blocks the current thread until the provided condition becomes false.
248    ///
249    /// `condition` is checked immediately. If it returns `true`, this function
250    /// waits and checks the condition again whenever the wait returns. This
251    /// repeats until `condition` returns `false`.
252    ///
253    /// This function will atomically unlock the mutex specified (represented by
254    /// `mutex_guard`) and block the current thread. This means that any calls
255    /// to `notify_*()` which happen logically after the mutex is unlocked are
256    /// candidates to wake this thread up. When this function call returns, the
257    /// lock specified will have been re-acquired.
258    ///
259    /// # Panics
260    ///
261    /// The underlying [`RawCondvar`] implementation may panic if another thread
262    /// is waiting on this condition variable with a different mutex.
263    #[inline]
264    pub fn wait_while<T, F>(
265        &self,
266        mutex_guard: &mut MutexGuard<'_, C::RawMutex, T>,
267        mut condition: F,
268    ) where
269        T: ?Sized,
270        F: FnMut(&mut T) -> bool,
271    {
272        while condition(mutex_guard.deref_mut()) {
273            // SAFETY: The exclusively borrowed guard holds the mutex; wait restores it.
274            unsafe {
275                self.inner
276                    .wait(MutexGuard::mutex(mutex_guard).raw(), &mut mutex_guard.guard);
277            }
278        }
279    }
280}
281
282impl<R: RawMutexTimed, C: RawCondvarTimed<RawMutex = R>> Condvar<C> {
283    /// Waits on this condition variable for a notification, timing out after
284    /// the specified time instant.
285    ///
286    /// The semantics of this function are equivalent to [`wait`](Self::wait),
287    /// except that it stops waiting after `timeout` is reached. A notification
288    /// or spurious wakeup may make the function return earlier. If the
289    /// operation times out, it will not return before `timeout`, but it may
290    /// return later because of scheduling or platform-specific behavior.
291    ///
292    /// Note that the best effort is made to ensure that the time waited is
293    /// measured with a monotonic clock, and not affected by the changes made to
294    /// the system time.
295    ///
296    /// The returned [`WaitTimeoutResult`] indicates whether the wait is known
297    /// to have timed out. Consult the documentation of `C` for whether a result
298    /// which is not timed out guarantees that a notification was received.
299    ///
300    /// Like [`wait`](Self::wait), the lock will be re-acquired before this
301    /// function returns, regardless of whether the timeout elapsed.
302    ///
303    /// # Panics
304    ///
305    /// The underlying [`RawCondvar`] implementation may panic if another thread
306    /// is waiting on this condition variable with a different mutex.
307    #[inline]
308    pub fn wait_until<T: ?Sized>(
309        &self,
310        mutex_guard: &mut MutexGuard<'_, C::RawMutex, T>,
311        timeout: <C::RawMutex as RawMutexTimed>::Instant,
312    ) -> WaitTimeoutResult {
313        // SAFETY: The exclusively borrowed guard holds the mutex; wait restores it.
314        WaitTimeoutResult(unsafe {
315            self.inner.wait_until(
316                MutexGuard::mutex(mutex_guard).raw(),
317                &mut mutex_guard.guard,
318                &timeout,
319            )
320        })
321    }
322
323    /// Waits on this condition variable for a notification, timing out after a
324    /// specified duration.
325    ///
326    /// The semantics of this function are equivalent to [`wait`](Self::wait),
327    /// except that it stops waiting after the specified duration. A
328    /// notification or spurious wakeup may make the function return earlier.
329    /// If the operation times out, it will not return before `timeout` has
330    /// elapsed, but it may return later because of scheduling or
331    /// platform-specific behavior.
332    ///
333    /// Note that the best effort is made to ensure that the time waited is
334    /// measured with a monotonic clock, and not affected by the changes made to
335    /// the system time.
336    ///
337    /// The returned [`WaitTimeoutResult`] indicates whether the wait is known
338    /// to have timed out. Consult the documentation of `C` for whether a result
339    /// which is not timed out guarantees that a notification was received.
340    ///
341    /// Like [`wait`](Self::wait), the lock will be re-acquired before this
342    /// function returns, regardless of whether the timeout elapsed.
343    ///
344    /// # Panics
345    ///
346    /// The underlying [`RawCondvar`] implementation may panic if another thread
347    /// is waiting on this condition variable with a different mutex.
348    #[inline]
349    pub fn wait_for<T: ?Sized>(
350        &self,
351        mutex_guard: &mut MutexGuard<'_, C::RawMutex, T>,
352        timeout: <C::RawMutex as RawMutexTimed>::Duration,
353    ) -> WaitTimeoutResult {
354        // SAFETY: The exclusively borrowed guard holds the mutex; wait restores it.
355        WaitTimeoutResult(unsafe {
356            self.inner.wait_for(
357                MutexGuard::mutex(mutex_guard).raw(),
358                &mut mutex_guard.guard,
359                &timeout,
360            )
361        })
362    }
363
364    /// Waits on this condition variable for the provided condition to become
365    /// false, timing out after the specified time instant.
366    ///
367    /// The semantics of this function are equivalent to
368    /// [`wait_while`](Self::wait_while), except that it stops waiting after
369    /// `timeout` is reached. If the operation times out, it will not return
370    /// before `timeout`, but it may return later because of scheduling or
371    /// platform-specific behavior.
372    ///
373    /// Note that the best effort is made to ensure that the time waited is
374    /// measured with a monotonic clock, and not affected by the changes made to
375    /// the system time.
376    ///
377    /// The returned [`WaitTimeoutResult`] indicates whether the wait timed out
378    /// while the condition remained `true`.
379    ///
380    /// Like [`wait_while`](Self::wait_while), the lock will be re-acquired
381    /// before this function returns, regardless of whether the timeout elapsed.
382    ///
383    /// # Panics
384    ///
385    /// The underlying [`RawCondvar`] implementation may panic if another thread
386    /// is waiting on this condition variable with a different mutex.
387    #[inline]
388    pub fn wait_while_until<T, F>(
389        &self,
390        mutex_guard: &mut MutexGuard<'_, C::RawMutex, T>,
391        mut condition: F,
392        timeout: <C::RawMutex as RawMutexTimed>::Instant,
393    ) -> WaitTimeoutResult
394    where
395        T: ?Sized,
396        F: FnMut(&mut T) -> bool,
397    {
398        let mut result = WaitTimeoutResult(false);
399
400        loop {
401            if !condition(mutex_guard.deref_mut()) {
402                return WaitTimeoutResult(false);
403            }
404            if result.timed_out() {
405                return result;
406            }
407            // SAFETY: The exclusively borrowed guard holds the mutex; wait restores it.
408            result = WaitTimeoutResult(unsafe {
409                self.inner.wait_until(
410                    MutexGuard::mutex(mutex_guard).raw(),
411                    &mut mutex_guard.guard,
412                    &timeout,
413                )
414            });
415        }
416    }
417
418    /// Waits on this condition variable for the provided condition to become
419    /// false, timing out after a specified duration.
420    ///
421    /// The semantics of this function are equivalent to
422    /// [`wait_while`](Self::wait_while), except that it stops waiting after the
423    /// specified duration. If the operation times out, it will not return
424    /// before `timeout` has elapsed, but it may return later because of
425    /// scheduling or platform-specific behavior.
426    ///
427    /// Note that the best effort is made to ensure that the time waited is
428    /// measured with a monotonic clock, and not affected by the changes made to
429    /// the system time.
430    ///
431    /// The returned [`WaitTimeoutResult`] indicates whether the wait timed out
432    /// while the condition remained `true`.
433    ///
434    /// Like [`wait_while`](Self::wait_while), the lock will be re-acquired
435    /// before this function returns, regardless of whether the timeout elapsed.
436    ///
437    /// # Panics
438    ///
439    /// The underlying [`RawCondvar`] implementation may panic if another thread
440    /// is waiting on this condition variable with a different mutex.
441    #[inline]
442    pub fn wait_while_for<T: ?Sized, F>(
443        &self,
444        mutex_guard: &mut MutexGuard<'_, C::RawMutex, T>,
445        condition: F,
446        timeout: <C::RawMutex as RawMutexTimed>::Duration,
447    ) -> WaitTimeoutResult
448    where
449        F: FnMut(&mut T) -> bool,
450    {
451        match C::checked_duration_to_instant(&timeout) {
452            Some(timeout) => self.wait_while_until(mutex_guard, condition, timeout),
453            None => {
454                // No absolute deadline can be constructed, so fall back to an
455                // untimed wait.
456                self.wait_while(mutex_guard, condition);
457                WaitTimeoutResult(false)
458            }
459        }
460    }
461}
462
463impl<C: RawCondvar> Default for Condvar<C> {
464    #[inline]
465    fn default() -> Condvar<C> {
466        Condvar::new()
467    }
468}
469
470impl<C> fmt::Debug for Condvar<C> {
471    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
472        f.pad("Condvar { .. }")
473    }
474}