Skip to main content

lock_api/
condvar.rs

1// Copyright 2016 Amanieu d'Antras
2//
3// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
4// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
5// http://opensource.org/licenses/MIT>, at your option. This file may not be
6// copied, modified, or distributed except according to those terms.
7
8use crate::mutex::{MutexGuard, RawMutex, RawMutexTimed};
9use core::{fmt, ops::DerefMut};
10
11/// Provides the inner implementation for a [`Condvar`] over a particular
12/// type of [`RawMutex`].
13///
14/// # Safety
15///
16/// Implementions must ensure that [`wait`] is safe to call, regardless of the
17/// specific [`RawMutex`] instance provided. If an implementation only supports
18/// one instance at a time, [`wait`] may panic.
19///
20/// [`wait`]: RawCondvar::wait
21pub unsafe trait RawCondvar {
22    /// Initial value for a new condvar.
23    // A “non-constant” const item is a legacy way to supply an initialized value to downstream
24    // static items. Can hopefully be replaced with `const fn new() -> Self` at some point.
25    #[allow(clippy::declare_interior_mutable_const)]
26    const INIT: Self;
27
28    /// The type of [`RawMutex`] this condvar can work with.
29    type RawMutex: RawMutex;
30
31    /// Wait until the provided [`RawMutex`] is available.
32    ///
33    /// # Safety
34    ///
35    /// Caller must ensure the provided `mutex` is locked, and that they are the
36    /// owner of said lock for the duration of this call.
37    ///
38    /// # Panics
39    ///
40    /// Implementations are permitted to panic if requested to wait on two distinct
41    /// [`RawMutex`]s simultaneously.
42    unsafe fn wait(&self, mutex: &Self::RawMutex);
43
44    /// Notify a single waiting thread.
45    fn notify_one(&self) -> bool;
46
47    /// Notify all waiting threads.
48    fn notify_all(&self) -> usize;
49}
50
51/// Additional methods for [`RawCondvar`] which support timeouts.
52///
53/// # Safety
54///
55/// Implementions must ensure that [`wait_for`] and [`wait_until`] are safe to call,
56/// regardless of the specific [`RawMutex`] instance provided. If an implementation
57/// only supports one instance at a time, waiting may panic.
58///
59/// [`wait_for`]: RawCondvar::wait_for
60/// [`wait_until`]: RawCondvar::wait_until
61pub unsafe trait RawCondvarTimed: RawCondvar
62where
63    Self::RawMutex: RawMutexTimed,
64{
65    /// Attmped to convert the provided [`Duration`](RawMutexTimed::Duration) into
66    /// an [`Instant`](RawMutexTimed::Instant). Returns [`None`] if the provided
67    /// duration is further into the future than can be represented by an instant.
68    fn checked_duration_to_instant(
69        timeout: &<Self::RawMutex as RawMutexTimed>::Duration,
70    ) -> Option<<Self::RawMutex as RawMutexTimed>::Instant>;
71
72    /// Wait until the provided [`RawMutex`] is available until the provided
73    /// timeout is reached.
74    ///
75    /// # Safety
76    ///
77    /// Caller must ensure the provided `mutex` is locked, and that they are the
78    /// owner of said lock for the duration of this call.
79    ///
80    /// # Panics
81    ///
82    /// Implementations are permitted to panic if requested to wait on two distinct
83    /// [`RawMutex`]s simultaneously.
84    unsafe fn wait_until(
85        &self,
86        mutex: &Self::RawMutex,
87        timeout: &<Self::RawMutex as RawMutexTimed>::Instant,
88    ) -> bool;
89
90    /// Wait until the provided [`RawMutex`] is available until the provided
91    /// timeout is reached.
92    ///
93    /// # Safety
94    ///
95    /// Caller must ensure the provided `mutex` is locked, and that they are the
96    /// owner of said lock for the duration of this call.
97    ///
98    /// # Panics
99    ///
100    /// Implementations are permitted to panic if requested to wait on two distinct
101    /// [`RawMutex`]s simultaneously.
102    unsafe fn wait_for(
103        &self,
104        mutex: &Self::RawMutex,
105        timeout: &<Self::RawMutex as RawMutexTimed>::Duration,
106    ) -> bool {
107        // SAFETY: `RawCondvar::wait` and `RawCondvarTimed::wait_until` have the
108        // same safety condition as this function, which is assured by the caller.
109        unsafe {
110            match Self::checked_duration_to_instant(timeout) {
111                Some(timeout) => self.wait_until(mutex, &timeout),
112                None => {
113                    // If the timeout could not be computed, we know the result  must
114                    // be `false`, indicating we did not timeout.
115                    <Self as RawCondvar>::wait(&self, mutex);
116                    false
117                }
118            }
119        }
120    }
121}
122
123/// A type indicating whether a timed wait on a condition variable returned
124/// due to a time out or not.
125#[derive(Debug, PartialEq, Eq, Copy, Clone)]
126pub struct WaitTimeoutResult(bool);
127
128impl WaitTimeoutResult {
129    /// Returns whether the wait was known to have timed out.
130    #[inline]
131    pub fn timed_out(self) -> bool {
132        self.0
133    }
134}
135
136/// A Condition Variable
137///
138/// Condition variables represent the ability to block a thread such that it
139/// consumes no CPU time while waiting for an event to occur. Condition
140/// variables are typically associated with a boolean predicate (a condition)
141/// and a mutex. The predicate is always verified inside of the mutex before
142/// determining that thread must block.
143pub struct Condvar<C> {
144    inner: C,
145}
146
147impl<C: RawCondvar> Condvar<C> {
148    /// Creates a new condition variable which is ready to be waited on and
149    /// notified.
150    #[inline]
151    pub const fn new() -> Condvar<C> {
152        Condvar { inner: C::INIT }
153    }
154
155    /// Returns the underlying raw condvar object.
156    ///
157    /// Note that you will most likely need to import the `RawCondvar` trait from
158    /// `lock_api` to be able to call functions on the raw condvar.
159    #[inline]
160    pub fn raw(&self) -> &C {
161        &self.inner
162    }
163
164    /// Wakes up one blocked thread on this condvar.
165    ///
166    /// Returns whether a thread was woken up.
167    ///
168    /// If there is a blocked thread on this condition variable, then it will
169    /// be woken up from its call to `wait` or `wait_timeout`.
170    ///
171    /// To wake up all threads, see `notify_all()`.
172    #[inline]
173    pub fn notify_one(&self) -> bool {
174        self.inner.notify_one()
175    }
176
177    /// Wakes up all blocked threads on this condvar.
178    ///
179    /// Returns the number of threads woken up.
180    ///
181    /// This method will ensure that any current waiters on the condition
182    /// variable are awoken.
183    ///
184    /// To wake up only one thread, see `notify_one()`.
185    #[inline]
186    pub fn notify_all(&self) -> usize {
187        self.inner.notify_all()
188    }
189
190    /// Blocks the current thread until this condition variable receives a
191    /// notification.
192    ///
193    /// This function will unlock the mutex specified (represented by
194    /// `mutex_guard`) and block the current thread. This means that any calls
195    /// to `notify_*()` which happen logically after the mutex is unlocked are
196    /// candidates to wake this thread up. When this function call returns, the
197    /// lock specified will have been re-acquired.
198    ///
199    /// # Panics
200    ///
201    /// The underlying [`RawCondvar`] implementation provided by `C` is permitted
202    /// to panic if requested to wait on two distinct [`MutexGuard`]s simultaneously.
203    #[inline]
204    pub fn wait<T: ?Sized>(&self, mutex_guard: &mut MutexGuard<'_, C::RawMutex, T>) {
205        unsafe {
206            self.inner.wait(MutexGuard::mutex(mutex_guard).raw());
207        }
208    }
209
210    /// Blocks the current thread until this condition variable receives a
211    /// notification. If the provided condition evaluates to `false`, then the
212    /// thread is no longer blocked and the operation is completed. If the
213    /// condition evaluates to `true`, then the thread is blocked again and
214    /// waits for another notification before repeating this process.
215    ///
216    /// This function will unlock the mutex specified (represented by
217    /// `mutex_guard`) and block the current thread. This means that any calls
218    /// to `notify_*()` which happen logically after the mutex is unlocked are
219    /// candidates to wake this thread up. When this function call returns, the
220    /// lock specified will have been re-acquired.
221    ///
222    /// # Panics
223    ///
224    /// The underlying [`RawCondvar`] implementation provided by `C` is permitted
225    /// to panic if requested to wait on two distinct [`MutexGuard`]s simultaneously.
226    #[inline]
227    pub fn wait_while<T, F>(
228        &self,
229        mutex_guard: &mut MutexGuard<'_, C::RawMutex, T>,
230        mut condition: F,
231    ) where
232        T: ?Sized,
233        F: FnMut(&mut T) -> bool,
234    {
235        while condition(mutex_guard.deref_mut()) {
236            unsafe {
237                self.inner.wait(MutexGuard::mutex(mutex_guard).raw());
238            }
239        }
240    }
241}
242
243impl<R: RawMutexTimed, C: RawCondvarTimed<RawMutex = R>> Condvar<C> {
244    /// Waits on this condition variable for a notification, timing out after
245    /// the specified time instant.
246    ///
247    /// The semantics of this function are equivalent to `wait()` except that
248    /// the thread will be blocked roughly until `timeout` is reached. This
249    /// method should not be used for precise timing due to anomalies such as
250    /// preemption or platform differences that may not cause the maximum
251    /// amount of time waited to be precisely `timeout`.
252    ///
253    /// Note that the best effort is made to ensure that the time waited is
254    /// measured with a monotonic clock, and not affected by the changes made to
255    /// the system time.
256    ///
257    /// The returned `WaitTimeoutResult` value indicates if the timeout is
258    /// known to have elapsed.
259    ///
260    /// Like `wait`, the lock specified will be re-acquired when this function
261    /// returns, regardless of whether the timeout elapsed or not.
262    ///
263    /// # Panics
264    ///
265    /// The underlying [`RawCondvar`] implementation provided by `C` is permitted
266    /// to panic if requested to wait on two distinct [`MutexGuard`]s simultaneously.
267    #[inline]
268    pub fn wait_until<T: ?Sized>(
269        &self,
270        mutex_guard: &mut MutexGuard<'_, C::RawMutex, T>,
271        timeout: <C::RawMutex as RawMutexTimed>::Instant,
272    ) -> WaitTimeoutResult {
273        WaitTimeoutResult(unsafe {
274            self.inner
275                .wait_until(MutexGuard::mutex(mutex_guard).raw(), &timeout)
276        })
277    }
278
279    /// Waits on this condition variable for a notification, timing out after a
280    /// specified duration.
281    ///
282    /// The semantics of this function are equivalent to `wait()` except that
283    /// the thread will be blocked for roughly no longer than `timeout`. This
284    /// method should not be used for precise timing due to anomalies such as
285    /// preemption or platform differences that may not cause the maximum
286    /// amount of time waited to be precisely `timeout`.
287    ///
288    /// Note that the best effort is made to ensure that the time waited is
289    /// measured with a monotonic clock, and not affected by the changes made to
290    /// the system time.
291    ///
292    /// The returned `WaitTimeoutResult` value indicates if the timeout is
293    /// known to have elapsed.
294    ///
295    /// Like `wait`, the lock specified will be re-acquired when this function
296    /// returns, regardless of whether the timeout elapsed or not.
297    ///
298    /// # Panics
299    ///
300    /// The underlying [`RawCondvar`] implementation provided by `C` is permitted
301    /// to panic if requested to wait on two distinct [`MutexGuard`]s simultaneously.
302    #[inline]
303    pub fn wait_for<T: ?Sized>(
304        &self,
305        mutex_guard: &mut MutexGuard<'_, C::RawMutex, T>,
306        timeout: <C::RawMutex as RawMutexTimed>::Duration,
307    ) -> WaitTimeoutResult {
308        WaitTimeoutResult(unsafe {
309            self.inner
310                .wait_for(MutexGuard::mutex(mutex_guard).raw(), &timeout)
311        })
312    }
313
314    /// Waits on this condition variable for a notification, timing out after
315    /// the specified time instant. If the provided condition evaluates to
316    /// `false`, then the thread is no longer blocked and the operation is
317    /// completed. If the condition evaluates to `true`, then the thread is
318    /// blocked again and waits for another notification before repeating
319    /// this process.
320    ///
321    /// The semantics of this function are equivalent to `wait()` except that
322    /// the thread will be blocked roughly until `timeout` is reached. This
323    /// method should not be used for precise timing due to anomalies such as
324    /// preemption or platform differences that may not cause the maximum
325    /// amount of time waited to be precisely `timeout`.
326    ///
327    /// Note that the best effort is made to ensure that the time waited is
328    /// measured with a monotonic clock, and not affected by the changes made to
329    /// the system time.
330    ///
331    /// The returned `WaitTimeoutResult` value indicates if the timeout is
332    /// known to have elapsed.
333    ///
334    /// Like `wait`, the lock specified will be re-acquired when this function
335    /// returns, regardless of whether the timeout elapsed or not.
336    ///
337    /// # Panics
338    ///
339    /// The underlying [`RawCondvar`] implementation provided by `C` is permitted
340    /// to panic if requested to wait on two distinct [`MutexGuard`]s simultaneously.
341    #[inline]
342    pub fn wait_while_until<T, F>(
343        &self,
344        mutex_guard: &mut MutexGuard<'_, C::RawMutex, T>,
345        mut condition: F,
346        timeout: <C::RawMutex as RawMutexTimed>::Instant,
347    ) -> WaitTimeoutResult
348    where
349        T: ?Sized,
350        F: FnMut(&mut T) -> bool,
351    {
352        let mut result = WaitTimeoutResult(false);
353
354        while !result.timed_out() && condition(mutex_guard.deref_mut()) {
355            result = WaitTimeoutResult(unsafe {
356                self.inner
357                    .wait_until(MutexGuard::mutex(mutex_guard).raw(), &timeout)
358            });
359        }
360
361        result
362    }
363
364    /// Waits on this condition variable for a notification, timing out after a
365    /// specified duration. If the provided condition evaluates to `false`,
366    /// then the thread is no longer blocked and the operation is completed.
367    /// If the condition evaluates to `true`, then the thread is blocked again
368    /// and waits for another notification before repeating this process.
369    ///
370    /// The semantics of this function are equivalent to `wait()` except that
371    /// the thread will be blocked for roughly no longer than `timeout`. This
372    /// method should not be used for precise timing due to anomalies such as
373    /// preemption or platform differences that may not cause the maximum
374    /// amount of time waited to be precisely `timeout`.
375    ///
376    /// Note that the best effort is made to ensure that the time waited is
377    /// measured with a monotonic clock, and not affected by the changes made to
378    /// the system time.
379    ///
380    /// The returned `WaitTimeoutResult` value indicates if the timeout is
381    /// known to have elapsed.
382    ///
383    /// Like `wait`, the lock specified will be re-acquired when this function
384    /// returns, regardless of whether the timeout elapsed or not.
385    ///
386    /// # Panics
387    ///
388    /// The underlying [`RawCondvar`] implementation provided by `C` is permitted
389    /// to panic if requested to wait on two distinct [`MutexGuard`]s simultaneously.
390    #[inline]
391    pub fn wait_while_for<T: ?Sized, F>(
392        &self,
393        mutex_guard: &mut MutexGuard<'_, C::RawMutex, T>,
394        condition: F,
395        timeout: <C::RawMutex as RawMutexTimed>::Duration,
396    ) -> WaitTimeoutResult
397    where
398        F: FnMut(&mut T) -> bool,
399    {
400        match C::checked_duration_to_instant(&timeout) {
401            Some(timeout) => self.wait_while_until(mutex_guard, condition, timeout),
402            None => {
403                // If the timeout could not be computed, we know the `WaitTimeoutResult`
404                // must be `false`, indicating we did not timeout.
405                self.wait_while(mutex_guard, condition);
406                WaitTimeoutResult(false)
407            }
408        }
409    }
410}
411
412impl<C: RawCondvar> Default for Condvar<C> {
413    #[inline]
414    fn default() -> Condvar<C> {
415        Condvar::new()
416    }
417}
418
419impl<C> fmt::Debug for Condvar<C> {
420    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
421        f.pad("Condvar { .. }")
422    }
423}