sync_linux_no_libc/sync/
condvar.rs

1use core::fmt;
2use core::time::Duration;
3use crate::sync::mutex;
4use crate::sync::mutex::MutexGuard;
5use crate::sys;
6
7/// A type indicating whether a timed wait on a condition variable returned
8/// due to a time out or not.
9///
10/// It is returned by the [`wait_timeout`] method.
11///
12/// [`wait_timeout`]: Condvar::wait_timeout
13#[derive(Debug, PartialEq, Eq, Copy, Clone)]
14pub struct WaitTimeoutResult(bool);
15
16impl WaitTimeoutResult {
17    /// Returns `true` if the wait was known to have timed out.
18    ///
19    /// # Examples
20    ///
21    /// This example spawns a thread which will sleep 20 milliseconds before
22    /// updating a boolean value and then notifying the condvar.
23    ///
24    /// The main thread will wait with a 10 millisecond timeout on the condvar
25    /// and will leave the loop upon timeout.
26    ///
27    /// ```
28    /// use sync_linux_no_libc::sync::{Condvar, Mutex};
29    /// use std::sync::{Arc};
30    /// use std::thread;
31    /// use std::time::Duration;
32    ///
33    /// let pair = Arc::new((Mutex::new(false), Condvar::new()));
34    /// let pair2 = Arc::clone(&pair);
35    ///
36    /// # let handle =
37    /// thread::spawn(move || {
38    ///     let (lock, cvar) = &*pair2;
39    ///
40    ///     // Let's wait 20 milliseconds before notifying the condvar.
41    ///     thread::sleep(Duration::from_millis(20));
42    ///
43    ///     let mut started = lock.lock();
44    ///     // We update the boolean value.
45    ///     *started = true;
46    ///     cvar.notify_one();
47    /// });
48    ///
49    /// // Wait for the thread to start up.
50    /// let (lock, cvar) = &*pair;
51    /// loop {
52    ///     // Let's put a timeout on the condvar's wait.
53    ///     let result = cvar.wait_timeout(lock.lock(), Duration::from_millis(10));
54    ///     // 10 milliseconds have passed.
55    ///     if result.1.timed_out() {
56    ///         // timed out now and we can leave.
57    ///         break
58    ///     }
59    /// }
60    /// # // Prevent leaks for Miri.
61    /// # let _ = handle.join();
62    /// ```
63    #[must_use]
64    pub fn timed_out(&self) -> bool {
65        self.0
66    }
67}
68
69/// A Condition Variable
70///
71/// Condition variables represent the ability to block a thread such that it
72/// consumes no CPU time while waiting for an event to occur. Condition
73/// variables are typically associated with a boolean predicate (a condition)
74/// and a mutex. The predicate is always verified inside of the mutex before
75/// determining that a thread must block.
76///
77/// Functions in this module will block the current **thread** of execution.
78/// Note that any attempt to use multiple mutexes on the same condition
79/// variable may result in a runtime panic.
80///
81/// # Examples
82///
83/// ```
84/// use sync_linux_no_libc::sync::{Condvar, Mutex};
85/// use std::sync::{Arc};
86/// use std::thread;
87///
88/// let pair = Arc::new((Mutex::new(false), Condvar::new()));
89/// let pair2 = Arc::clone(&pair);
90///
91/// // Inside of our lock, spawn a new thread, and then wait for it to start.
92/// thread::spawn(move || {
93///     let (lock, cvar) = &*pair2;
94///     let mut started = lock.lock();
95///     *started = true;
96///     // We notify the condvar that the value has changed.
97///     cvar.notify_one();
98/// });
99///
100/// // Wait for the thread to start up.
101/// let (lock, cvar) = &*pair;
102/// let mut started = lock.lock();
103/// while !*started {
104///     started = cvar.wait(started);
105/// }
106/// ```
107pub struct Condvar {
108    inner: sys::Condvar,
109}
110
111impl Condvar {
112    /// Creates a new condition variable which is ready to be waited on and
113    /// notified.
114    ///
115    /// # Examples
116    ///
117    /// ```
118    /// use sync_linux_no_libc::sync::{Condvar};
119    ///
120    /// let condvar = Condvar::new();
121    /// ```
122    #[must_use]
123    #[inline]
124    pub const fn new() -> Condvar {
125        Condvar { inner: sys::Condvar::new() }
126    }
127
128    /// Blocks the current thread until this condition variable receives a
129    /// notification.
130    ///
131    /// This function will atomically unlock the mutex specified (represented by
132    /// `guard`) and block the current thread. This means that any calls
133    /// to [`notify_one`] or [`notify_all`] which happen logically after the
134    /// mutex is unlocked are candidates to wake this thread up. When this
135    /// function call returns, the lock specified will have been re-acquired.
136    ///
137    /// Note that this function is susceptible to spurious wakeups. Condition
138    /// variables normally have a boolean predicate associated with them, and
139    /// the predicate must always be checked each time this function returns to
140    /// protect against spurious wakeups.
141    ///
142    /// # Panics
143    ///
144    /// This function may [`panic!`] if it is used with more than one mutex
145    /// over time.
146    ///
147    /// [`notify_one`]: Self::notify_one
148    /// [`notify_all`]: Self::notify_all
149    /// [`Mutex`]: super::Mutex
150    ///
151    /// # Examples
152    ///
153    /// ```
154    /// use sync_linux_no_libc::sync::{Condvar, Mutex};
155    /// use std::sync::{Arc};
156    /// use std::thread;
157    ///
158    /// let pair = Arc::new((Mutex::new(false), Condvar::new()));
159    /// let pair2 = Arc::clone(&pair);
160    ///
161    /// thread::spawn(move || {
162    ///     let (lock, cvar) = &*pair2;
163    ///     let mut started = lock.lock();
164    ///     *started = true;
165    ///     // We notify the condvar that the value has changed.
166    ///     cvar.notify_one();
167    /// });
168    ///
169    /// // Wait for the thread to start up.
170    /// let (lock, cvar) = &*pair;
171    /// let mut started = lock.lock();
172    /// // As long as the value inside the `Mutex<bool>` is `false`, we wait.
173    /// while !*started {
174    ///     started = cvar.wait(started);
175    /// }
176    /// ```
177    pub fn wait<'a, T>(&self, guard: MutexGuard<'a, T>) -> MutexGuard<'a, T> {
178        unsafe {
179            let lock = mutex::guard_lock(&guard);
180            self.inner.wait(lock);
181        };
182        guard
183    }
184
185    /// Blocks the current thread until the provided condition becomes false.
186    ///
187    /// `condition` is checked immediately; if not met (returns `true`), this
188    /// will [`wait`] for the next notification then check again. This repeats
189    /// until `condition` returns `false`, in which case this function returns.
190    ///
191    /// This function will atomically unlock the mutex specified (represented by
192    /// `guard`) and block the current thread. This means that any calls
193    /// to [`notify_one`] or [`notify_all`] which happen logically after the
194    /// mutex is unlocked are candidates to wake this thread up. When this
195    /// function call returns, the lock specified will have been re-acquired.
196    ///
197    /// [`wait`]: Self::wait
198    /// [`notify_one`]: Self::notify_one
199    /// [`notify_all`]: Self::notify_all
200    /// [`Mutex`]: super::Mutex
201    ///
202    /// # Examples
203    ///
204    /// ```
205    /// use sync_linux_no_libc::sync::{Condvar, Mutex};
206    /// use std::sync::{Arc};
207    /// use std::thread;
208    ///
209    /// let pair = Arc::new((Mutex::new(true), Condvar::new()));
210    /// let pair2 = Arc::clone(&pair);
211    ///
212    /// thread::spawn(move || {
213    ///     let (lock, cvar) = &*pair2;
214    ///     let mut pending = lock.lock();
215    ///     *pending = false;
216    ///     // We notify the condvar that the value has changed.
217    ///     cvar.notify_one();
218    /// });
219    ///
220    /// // Wait for the thread to start up.
221    /// let (lock, cvar) = &*pair;
222    /// // As long as the value inside the `Mutex<bool>` is `true`, we wait.
223    /// let _guard = cvar.wait_while(lock.lock(), |pending| { *pending });
224    /// ```
225    pub fn wait_while<'a, T, F>(
226        &self,
227        mut guard: MutexGuard<'a, T>,
228        mut condition: F,
229    ) -> MutexGuard<'a, T>
230    where
231        F: FnMut(&mut T) -> bool,
232    {
233        while condition(&mut *guard) {
234            guard = self.wait(guard);
235        }
236        guard
237    }
238
239    /// Waits on this condition variable for a notification, timing out after a
240    /// specified duration.
241    ///
242    /// The semantics of this function are equivalent to [`wait`] except that
243    /// the thread will be blocked for roughly no longer than `dur`. This
244    /// method should not be used for precise timing due to anomalies such as
245    /// preemption or platform differences that might not cause the maximum
246    /// amount of time waited to be precisely `dur`.
247    ///
248    /// Note that the best effort is made to ensure that the time waited is
249    /// measured with a monotonic clock, and not affected by the changes made to
250    /// the system time. This function is susceptible to spurious wakeups.
251    /// Condition variables normally have a boolean predicate associated with
252    /// them, and the predicate must always be checked each time this function
253    /// returns to protect against spurious wakeups. Additionally, it is
254    /// typically desirable for the timeout to not exceed some duration in
255    /// spite of spurious wakes, thus the sleep-duration is decremented by the
256    /// amount slept.
257    ///
258    /// The returned [`WaitTimeoutResult`] value indicates if the timeout is
259    /// known to have elapsed.
260    ///
261    /// Like [`wait`], the lock specified will be re-acquired when this function
262    /// returns, regardless of whether the timeout elapsed or not.
263    ///
264    /// [`wait`]: Self::wait
265    ///
266    /// # Examples
267    ///
268    /// ```
269    /// use sync_linux_no_libc::sync::{Condvar, Mutex};
270    /// use std::sync::{Arc};
271    /// use std::thread;
272    /// use std::time::Duration;
273    ///
274    /// let pair = Arc::new((Mutex::new(false), Condvar::new()));
275    /// let pair2 = Arc::clone(&pair);
276    ///
277    /// thread::spawn(move || {
278    ///     let (lock, cvar) = &*pair2;
279    ///     let mut started = lock.lock();
280    ///     *started = true;
281    ///     // We notify the condvar that the value has changed.
282    ///     cvar.notify_one();
283    /// });
284    ///
285    /// // wait for the thread to start up
286    /// let (lock, cvar) = &*pair;
287    /// let mut started = lock.lock();
288    /// // as long as the value inside the `Mutex<bool>` is `false`, we wait
289    /// loop {
290    ///     let result = cvar.wait_timeout(started, Duration::from_millis(10));
291    ///     // 10 milliseconds have passed, or maybe the value changed!
292    ///     started = result.0;
293    ///     if *started == true {
294    ///         // We received the notification and the value has been updated, we can leave.
295    ///         break
296    ///     }
297    /// }
298    /// ```
299    pub fn wait_timeout<'a, T>(
300        &self,
301        guard: MutexGuard<'a, T>,
302        dur: Duration,
303    ) -> (MutexGuard<'a, T>, WaitTimeoutResult) {
304        let result = unsafe {
305            let lock = mutex::guard_lock(&guard);
306            let success = self.inner.wait_timeout(lock, dur);
307            WaitTimeoutResult(!success)
308        };
309        (guard, result)
310    }
311
312    /// Wakes up one blocked thread on this condvar.
313    ///
314    /// If there is a blocked thread on this condition variable, then it will
315    /// be woken up from its call to [`wait`] or [`wait_timeout`]. Calls to
316    /// `notify_one` are not buffered in any way.
317    ///
318    /// To wake up all threads, see [`notify_all`].
319    ///
320    /// [`wait`]: Self::wait
321    /// [`wait_timeout`]: Self::wait_timeout
322    /// [`notify_all`]: Self::notify_all
323    ///
324    /// # Examples
325    ///
326    /// ```
327    /// use sync_linux_no_libc::sync::{Condvar, Mutex};
328    /// use std::sync::{Arc};
329    /// use std::thread;
330    ///
331    /// let pair = Arc::new((Mutex::new(false), Condvar::new()));
332    /// let pair2 = Arc::clone(&pair);
333    ///
334    /// thread::spawn(move || {
335    ///     let (lock, cvar) = &*pair2;
336    ///     let mut started = lock.lock();
337    ///     *started = true;
338    ///     // We notify the condvar that the value has changed.
339    ///     cvar.notify_one();
340    /// });
341    ///
342    /// // Wait for the thread to start up.
343    /// let (lock, cvar) = &*pair;
344    /// let mut started = lock.lock();
345    /// // As long as the value inside the `Mutex<bool>` is `false`, we wait.
346    /// while !*started {
347    ///     started = cvar.wait(started);
348    /// }
349    /// ```
350    pub fn notify_one(&self) {
351        self.inner.notify_one()
352    }
353
354    /// Wakes up all blocked threads on this condvar.
355    ///
356    /// This method will ensure that any current waiters on the condition
357    /// variable are awoken. Calls to `notify_all()` are not buffered in any
358    /// way.
359    ///
360    /// To wake up only one thread, see [`notify_one`].
361    ///
362    /// [`notify_one`]: Self::notify_one
363    ///
364    /// # Examples
365    ///
366    /// ```
367    /// use sync_linux_no_libc::sync::{Condvar, Mutex};
368    /// use std::sync::{Arc};
369    /// use std::thread;
370    ///
371    /// let pair = Arc::new((Mutex::new(false), Condvar::new()));
372    /// let pair2 = Arc::clone(&pair);
373    ///
374    /// thread::spawn(move || {
375    ///     let (lock, cvar) = &*pair2;
376    ///     let mut started = lock.lock();
377    ///     *started = true;
378    ///     // We notify the condvar that the value has changed.
379    ///     cvar.notify_all();
380    /// });
381    ///
382    /// // Wait for the thread to start up.
383    /// let (lock, cvar) = &*pair;
384    /// let mut started = lock.lock();
385    /// // As long as the value inside the `Mutex<bool>` is `false`, we wait.
386    /// while !*started {
387    ///     started = cvar.wait(started);
388    /// }
389    /// ```
390    pub fn notify_all(&self) {
391        self.inner.notify_all()
392    }
393}
394
395impl fmt::Debug for Condvar {
396    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
397        f.debug_struct("Condvar").finish_non_exhaustive()
398    }
399}
400
401impl Default for Condvar {
402    /// Creates a `Condvar` which is ready to be waited on and notified.
403    fn default() -> Condvar {
404        Condvar::new()
405    }
406}