rustix_futex_sync/condvar.rs
1//! The following is derived from Rust's
2//! library/std/src/sync/condvar.rs at revision
3//! 22a5267c83a3e17f2b763279eb24bb632c45dc6b.
4
5/*
6#[cfg(test)]
7mod tests;
8*/
9
10use core::fmt;
11use crate::generic::{MutexGuard, RawCondvar};
12use core::time::Duration;
13
14/// A type indicating whether a timed wait on a condition variable returned
15/// due to a time out or not.
16///
17/// It is returned by the [`wait_timeout`] method.
18///
19/// [`wait_timeout`]: Condvar::wait_timeout
20#[derive(Debug, PartialEq, Eq, Copy, Clone)]
21//#[stable(feature = "wait_timeout", since = "1.5.0")]
22pub struct WaitTimeoutResult(bool);
23
24impl WaitTimeoutResult {
25 /// Returns `true` if the wait was known to have timed out.
26 ///
27 /// # Examples
28 ///
29 /// This example spawns a thread which will sleep 20 milliseconds before
30 /// updating a boolean value and then notifying the condvar.
31 ///
32 /// The main thread will wait with a 10 millisecond timeout on the condvar
33 /// and will leave the loop upon timeout.
34 ///
35 /// ```
36 /// use std::sync::Arc;
37 /// use rustix_futex_sync::{Condvar, Mutex};
38 /// use std::thread;
39 /// use std::time::Duration;
40 ///
41 /// let pair = Arc::new((Mutex::new(false), Condvar::new()));
42 /// let pair2 = Arc::clone(&pair);
43 ///
44 /// # let t =
45 /// thread::spawn(move || {
46 /// let (lock, cvar) = &*pair2;
47 ///
48 /// // Let's wait 20 milliseconds before notifying the condvar.
49 /// thread::sleep(Duration::from_millis(20));
50 ///
51 /// let mut started = lock.lock();
52 /// // We update the boolean value.
53 /// *started = true;
54 /// cvar.notify_one();
55 /// });
56 ///
57 /// // Wait for the thread to start up.
58 /// let (lock, cvar) = &*pair;
59 /// loop {
60 /// // Let's put a timeout on the condvar's wait.
61 /// let result = cvar.wait_timeout(lock.lock(), Duration::from_millis(10));
62 /// // 10 milliseconds have passed.
63 /// if result.1.timed_out() {
64 /// // timed out now and we can leave.
65 /// break
66 /// }
67 /// }
68 /// # t.join();
69 /// ```
70 #[must_use]
71 //#[stable(feature = "wait_timeout", since = "1.5.0")]
72 pub fn timed_out(&self) -> bool {
73 self.0
74 }
75}
76
77/// A Condition Variable
78///
79/// Condition variables represent the ability to block a thread such that it
80/// consumes no CPU time while waiting for an event to occur. Condition
81/// variables are typically associated with a boolean predicate (a condition)
82/// and a mutex. The predicate is always verified inside of the mutex before
83/// determining that a thread must block.
84///
85/// Functions in this module will block the current **thread** of execution.
86/// Note that any attempt to use multiple mutexes on the same condition
87/// variable may result in a runtime panic.
88///
89/// # Examples
90///
91/// ```
92/// use std::sync::Arc;
93/// use rustix_futex_sync::{Mutex, Condvar};
94/// use std::thread;
95///
96/// let pair = Arc::new((Mutex::new(false), Condvar::new()));
97/// let pair2 = Arc::clone(&pair);
98///
99/// // Inside of our lock, spawn a new thread, and then wait for it to start.
100/// thread::spawn(move|| {
101/// let (lock, cvar) = &*pair2;
102/// let mut started = lock.lock();
103/// *started = true;
104/// // We notify the condvar that the value has changed.
105/// cvar.notify_one();
106/// });
107///
108/// // Wait for the thread to start up.
109/// let (lock, cvar) = &*pair;
110/// let mut started = lock.lock();
111/// while !*started {
112/// started = cvar.wait(started);
113/// }
114/// ```
115//#[stable(feature = "rust1", since = "1.0.0")]
116#[repr(transparent)]
117pub struct Condvar<const SHM: bool> {
118 inner: RawCondvar<SHM>,
119}
120
121impl<const SHM: bool> Condvar<SHM> {
122 /// Creates a new condition variable which is ready to be waited on and
123 /// notified.
124 ///
125 /// # Examples
126 ///
127 /// ```
128 /// use rustix_futex_sync::Condvar;
129 ///
130 /// let condvar = Condvar::new();
131 /// ```
132 //#[stable(feature = "rust1", since = "1.0.0")]
133 //#[rustc_const_stable(feature = "const_locks", since = "1.63.0")]
134 #[must_use]
135 #[inline]
136 pub const fn new() -> Self {
137 Condvar { inner: RawCondvar::new() }
138 }
139
140 /// Blocks the current thread until this condition variable receives a
141 /// notification.
142 ///
143 /// This function will atomically unlock the mutex specified (represented by
144 /// `guard`) and block the current thread. This means that any calls
145 /// to [`notify_one`] or [`notify_all`] which happen logically after the
146 /// mutex is unlocked are candidates to wake this thread up. When this
147 /// function call returns, the lock specified will have been re-acquired.
148 ///
149 /// Note that this function is susceptible to spurious wakeups. Condition
150 /// variables normally have a boolean predicate associated with them, and
151 /// the predicate must always be checked each time this function returns to
152 /// protect against spurious wakeups.
153 ///
154 /// # Errors
155 ///
156 /// This function will return an error if the mutex being waited on is
157 /// poisoned when this thread re-acquires the lock. For more information,
158 /// see information about [poisoning] on the [`Mutex`] type.
159 ///
160 /// # Panics
161 ///
162 /// This function may [`panic!`] if it is used with more than one mutex
163 /// over time.
164 ///
165 /// [`notify_one`]: Self::notify_one
166 /// [`notify_all`]: Self::notify_all
167 /// [poisoning]: super::Mutex#poisoning
168 /// [`Mutex`]: super::Mutex
169 ///
170 /// # Examples
171 ///
172 /// ```
173 /// use std::sync::Arc;
174 /// use rustix_futex_sync::{Mutex, Condvar};
175 /// use std::thread;
176 ///
177 /// let pair = Arc::new((Mutex::new(false), Condvar::new()));
178 /// let pair2 = Arc::clone(&pair);
179 ///
180 /// thread::spawn(move|| {
181 /// let (lock, cvar) = &*pair2;
182 /// let mut started = lock.lock();
183 /// *started = true;
184 /// // We notify the condvar that the value has changed.
185 /// cvar.notify_one();
186 /// });
187 ///
188 /// // Wait for the thread to start up.
189 /// let (lock, cvar) = &*pair;
190 /// let mut started = lock.lock();
191 /// // As long as the value inside the `Mutex<bool>` is `false`, we wait.
192 /// while !*started {
193 /// started = cvar.wait(started);
194 /// }
195 /// ```
196 //#[stable(feature = "rust1", since = "1.0.0")]
197 pub fn wait<'a, T>(&self, guard: MutexGuard<'a, T, SHM>) -> MutexGuard<'a, T, SHM> {
198 unsafe {
199 self.inner.wait(MutexGuard::mutex(&guard).raw());
200 }
201 guard
202 }
203
204 /// Blocks the current thread until this condition variable receives a
205 /// notification and the provided condition is false.
206 ///
207 /// This function will atomically unlock the mutex specified (represented by
208 /// `guard`) and block the current thread. This means that any calls
209 /// to [`notify_one`] or [`notify_all`] which happen logically after the
210 /// mutex is unlocked are candidates to wake this thread up. When this
211 /// function call returns, the lock specified will have been re-acquired.
212 ///
213 /// # Errors
214 ///
215 /// This function will return an error if the mutex being waited on is
216 /// poisoned when this thread re-acquires the lock. For more information,
217 /// see information about [poisoning] on the [`Mutex`] type.
218 ///
219 /// [`notify_one`]: Self::notify_one
220 /// [`notify_all`]: Self::notify_all
221 /// [poisoning]: super::Mutex#poisoning
222 /// [`Mutex`]: super::Mutex
223 ///
224 /// # Examples
225 ///
226 /// ```
227 /// use std::sync::Arc;
228 /// use rustix_futex_sync::{Mutex, Condvar};
229 /// use std::thread;
230 ///
231 /// let pair = Arc::new((Mutex::new(true), Condvar::new()));
232 /// let pair2 = Arc::clone(&pair);
233 ///
234 /// thread::spawn(move|| {
235 /// let (lock, cvar) = &*pair2;
236 /// let mut pending = lock.lock();
237 /// *pending = false;
238 /// // We notify the condvar that the value has changed.
239 /// cvar.notify_one();
240 /// });
241 ///
242 /// // Wait for the thread to start up.
243 /// let (lock, cvar) = &*pair;
244 /// // As long as the value inside the `Mutex<bool>` is `true`, we wait.
245 /// let _guard = cvar.wait_while(lock.lock(), |pending| { *pending });
246 /// ```
247 //#[stable(feature = "wait_until", since = "1.42.0")]
248 pub fn wait_while<'a, T, F>(
249 &self,
250 mut guard: MutexGuard<'a, T, SHM>,
251 mut condition: F,
252 ) -> MutexGuard<'a, T, SHM>
253 where
254 F: FnMut(&mut T) -> bool,
255 {
256 while condition(&mut *guard) {
257 guard = self.wait(guard);
258 }
259 guard
260 }
261
262 /// Waits on this condition variable for a notification, timing out after a
263 /// specified duration.
264 ///
265 /// The semantics of this function are equivalent to [`wait`]
266 /// except that the thread will be blocked for roughly no longer
267 /// than `ms` milliseconds. This method should not be used for
268 /// precise timing due to anomalies such as preemption or platform
269 /// differences that might not cause the maximum amount of time
270 /// waited to be precisely `ms`.
271 ///
272 /// Note that the best effort is made to ensure that the time waited is
273 /// measured with a monotonic clock, and not affected by the changes made to
274 /// the system time.
275 ///
276 /// The returned boolean is `false` only if the timeout is known
277 /// to have elapsed.
278 ///
279 /// Like [`wait`], the lock specified will be re-acquired when this function
280 /// returns, regardless of whether the timeout elapsed or not.
281 ///
282 /// [`wait`]: Self::wait
283 ///
284 /// # Examples
285 ///
286 /// ```
287 /// use std::sync::Arc;
288 /// use rustix_futex_sync::{Mutex, Condvar};
289 /// use std::thread;
290 ///
291 /// let pair = Arc::new((Mutex::new(false), Condvar::new()));
292 /// let pair2 = Arc::clone(&pair);
293 ///
294 /// thread::spawn(move|| {
295 /// let (lock, cvar) = &*pair2;
296 /// let mut started = lock.lock();
297 /// *started = true;
298 /// // We notify the condvar that the value has changed.
299 /// cvar.notify_one();
300 /// });
301 ///
302 /// // Wait for the thread to start up.
303 /// let (lock, cvar) = &*pair;
304 /// let mut started = lock.lock();
305 /// // As long as the value inside the `Mutex<bool>` is `false`, we wait.
306 /// loop {
307 /// let result = cvar.wait_timeout_ms(started, 10);
308 /// // 10 milliseconds have passed, or maybe the value changed!
309 /// started = result.0;
310 /// if *started == true {
311 /// // We received the notification and the value has been updated, we can leave.
312 /// break
313 /// }
314 /// }
315 /// ```
316 //#[stable(feature = "rust1", since = "1.0.0")]
317 #[deprecated(since = "1.6.0", note = "replaced by `rustix_futex_sync::Condvar::wait_timeout`")]
318 pub fn wait_timeout_ms<'a, T>(
319 &self,
320 guard: MutexGuard<'a, T, SHM>,
321 ms: u32,
322 ) -> (MutexGuard<'a, T, SHM>, bool) {
323 let res = self.wait_timeout(guard, Duration::from_millis(ms as u64));
324 (res.0, !res.1.timed_out())
325 }
326
327 /// Waits on this condition variable for a notification, timing out after a
328 /// specified duration.
329 ///
330 /// The semantics of this function are equivalent to [`wait`] except that
331 /// the thread will be blocked for roughly no longer than `dur`. This
332 /// method should not be used for precise timing due to anomalies such as
333 /// preemption or platform differences that might not cause the maximum
334 /// amount of time waited to be precisely `dur`.
335 ///
336 /// Note that the best effort is made to ensure that the time waited is
337 /// measured with a monotonic clock, and not affected by the changes made to
338 /// the system time. This function is susceptible to spurious wakeups.
339 /// Condition variables normally have a boolean predicate associated with
340 /// them, and the predicate must always be checked each time this function
341 /// returns to protect against spurious wakeups. Additionally, it is
342 /// typically desirable for the timeout to not exceed some duration in
343 /// spite of spurious wakes, thus the sleep-duration is decremented by the
344 /// amount slept. Alternatively, use the `wait_timeout_while` method
345 /// to wait with a timeout while a predicate is true.
346 ///
347 /// The returned [`WaitTimeoutResult`] value indicates if the timeout is
348 /// known to have elapsed.
349 ///
350 /// Like [`wait`], the lock specified will be re-acquired when this function
351 /// returns, regardless of whether the timeout elapsed or not.
352 ///
353 /// [`wait`]: Self::wait
354 /// [`wait_timeout_while`]: Self::wait_timeout_while
355 ///
356 /// # Examples
357 ///
358 /// ```
359 /// use std::sync::Arc;
360 /// use rustix_futex_sync::{Mutex, Condvar};
361 /// use std::thread;
362 /// use std::time::Duration;
363 ///
364 /// let pair = Arc::new((Mutex::new(false), Condvar::new()));
365 /// let pair2 = Arc::clone(&pair);
366 ///
367 /// thread::spawn(move|| {
368 /// let (lock, cvar) = &*pair2;
369 /// let mut started = lock.lock();
370 /// *started = true;
371 /// // We notify the condvar that the value has changed.
372 /// cvar.notify_one();
373 /// });
374 ///
375 /// // wait for the thread to start up
376 /// let (lock, cvar) = &*pair;
377 /// let mut started = lock.lock();
378 /// // as long as the value inside the `Mutex<bool>` is `false`, we wait
379 /// loop {
380 /// let result = cvar.wait_timeout(started, Duration::from_millis(10));
381 /// // 10 milliseconds have passed, or maybe the value changed!
382 /// started = result.0;
383 /// if *started == true {
384 /// // We received the notification and the value has been updated, we can leave.
385 /// break
386 /// }
387 /// }
388 /// ```
389 //#[stable(feature = "wait_timeout", since = "1.5.0")]
390 pub fn wait_timeout<'a, T>(
391 &self,
392 guard: MutexGuard<'a, T, SHM>,
393 dur: Duration,
394 ) -> (MutexGuard<'a, T, SHM>, WaitTimeoutResult) {
395 let result = unsafe {
396 self.inner
397 .wait_timeout(MutexGuard::mutex(&guard).raw(), dur)
398 };
399 (guard, WaitTimeoutResult(!result))
400 }
401
402 /// Waits on this condition variable for a notification, timing out after a
403 /// specified duration.
404 ///
405 /// The semantics of this function are equivalent to [`wait_while`] except
406 /// that the thread will be blocked for roughly no longer than `dur`. This
407 /// method should not be used for precise timing due to anomalies such as
408 /// preemption or platform differences that might not cause the maximum
409 /// amount of time waited to be precisely `dur`.
410 ///
411 /// Note that the best effort is made to ensure that the time waited is
412 /// measured with a monotonic clock, and not affected by the changes made to
413 /// the system time.
414 ///
415 /// The returned [`WaitTimeoutResult`] value indicates if the timeout is
416 /// known to have elapsed without the condition being met.
417 ///
418 /// Like [`wait_while`], the lock specified will be re-acquired when this
419 /// function returns, regardless of whether the timeout elapsed or not.
420 ///
421 /// [`wait_while`]: Self::wait_while
422 /// [`wait_timeout`]: Self::wait_timeout
423 ///
424 /// # Examples
425 ///
426 /// ```
427 /// use std::sync::Arc;
428 /// use rustix_futex_sync::{Mutex, Condvar};
429 /// use std::thread;
430 /// use std::time::Duration;
431 ///
432 /// let pair = Arc::new((Mutex::new(true), Condvar::new()));
433 /// let pair2 = Arc::clone(&pair);
434 ///
435 /// thread::spawn(move|| {
436 /// let (lock, cvar) = &*pair2;
437 /// let mut pending = lock.lock();
438 /// *pending = false;
439 /// // We notify the condvar that the value has changed.
440 /// cvar.notify_one();
441 /// });
442 ///
443 /// // wait for the thread to start up
444 /// let (lock, cvar) = &*pair;
445 /// let result = cvar.wait_timeout_while(
446 /// lock.lock(),
447 /// Duration::from_millis(100),
448 /// |&mut pending| pending,
449 /// );
450 /// if result.1.timed_out() {
451 /// // timed-out without the condition ever evaluating to false.
452 /// }
453 /// // access the locked mutex via result.0
454 /// ```
455 //#[stable(feature = "wait_timeout_until", since = "1.42.0")]
456 pub fn wait_timeout_while<'a, T, F>(
457 &self,
458 mut guard: MutexGuard<'a, T, SHM>,
459 dur: Duration,
460 mut condition: F,
461 ) -> (MutexGuard<'a, T, SHM>, WaitTimeoutResult)
462 where
463 F: FnMut(&mut T) -> bool,
464 {
465 let start = rustix::time::clock_gettime(rustix::time::ClockId::Monotonic);
466 loop {
467 if !condition(&mut *guard) {
468 return (guard, WaitTimeoutResult(false));
469 }
470 let now = rustix::time::clock_gettime(rustix::time::ClockId::Monotonic);
471
472 let elapsed = {
473 if now.tv_sec > start.tv_sec
474 || (now.tv_sec == start.tv_sec && now.tv_nsec >= start.tv_nsec)
475 {
476 let (secs, nsec) = if now.tv_nsec >= start.tv_nsec {
477 (now.tv_sec - start.tv_sec, now.tv_nsec - start.tv_nsec)
478 } else {
479 (
480 now.tv_sec - start.tv_sec - 1,
481 now.tv_nsec + 1_000_000_000 - start.tv_nsec,
482 )
483 };
484
485 Duration::new(secs.try_into().unwrap(), nsec as _)
486 } else {
487 Duration::new(0, 0)
488 }
489 };
490
491 let timeout = match dur.checked_sub(elapsed) {
492 Some(timeout) => timeout,
493 None => return (guard, WaitTimeoutResult(true)),
494 };
495 guard = self.wait_timeout(guard, timeout).0;
496 }
497 }
498
499 /// Wakes up one blocked thread on this condvar.
500 ///
501 /// If there is a blocked thread on this condition variable, then it will
502 /// be woken up from its call to [`wait`] or [`wait_timeout`]. Calls to
503 /// `notify_one` are not buffered in any way.
504 ///
505 /// To wake up all threads, see [`notify_all`].
506 ///
507 /// [`wait`]: Self::wait
508 /// [`wait_timeout`]: Self::wait_timeout
509 /// [`notify_all`]: Self::notify_all
510 ///
511 /// # Examples
512 ///
513 /// ```
514 /// use std::sync::Arc;
515 /// use rustix_futex_sync::{Mutex, Condvar};
516 /// use std::thread;
517 ///
518 /// let pair = Arc::new((Mutex::new(false), Condvar::new()));
519 /// let pair2 = Arc::clone(&pair);
520 ///
521 /// thread::spawn(move|| {
522 /// let (lock, cvar) = &*pair2;
523 /// let mut started = lock.lock();
524 /// *started = true;
525 /// // We notify the condvar that the value has changed.
526 /// cvar.notify_one();
527 /// });
528 ///
529 /// // Wait for the thread to start up.
530 /// let (lock, cvar) = &*pair;
531 /// let mut started = lock.lock();
532 /// // As long as the value inside the `Mutex<bool>` is `false`, we wait.
533 /// while !*started {
534 /// started = cvar.wait(started);
535 /// }
536 /// ```
537 //#[stable(feature = "rust1", since = "1.0.0")]
538 pub fn notify_one(&self) {
539 self.inner.notify_one()
540 }
541
542 /// Wakes up all blocked threads on this condvar.
543 ///
544 /// This method will ensure that any current waiters on the condition
545 /// variable are awoken. Calls to `notify_all()` are not buffered in any
546 /// way.
547 ///
548 /// To wake up only one thread, see [`notify_one`].
549 ///
550 /// [`notify_one`]: Self::notify_one
551 ///
552 /// # Examples
553 ///
554 /// ```
555 /// use std::sync::Arc;
556 /// use rustix_futex_sync::{Mutex, Condvar};
557 /// use std::thread;
558 ///
559 /// let pair = Arc::new((Mutex::new(false), Condvar::new()));
560 /// let pair2 = Arc::clone(&pair);
561 ///
562 /// thread::spawn(move|| {
563 /// let (lock, cvar) = &*pair2;
564 /// let mut started = lock.lock();
565 /// *started = true;
566 /// // We notify the condvar that the value has changed.
567 /// cvar.notify_all();
568 /// });
569 ///
570 /// // Wait for the thread to start up.
571 /// let (lock, cvar) = &*pair;
572 /// let mut started = lock.lock();
573 /// // As long as the value inside the `Mutex<bool>` is `false`, we wait.
574 /// while !*started {
575 /// started = cvar.wait(started);
576 /// }
577 /// ```
578 //#[stable(feature = "rust1", since = "1.0.0")]
579 pub fn notify_all(&self) {
580 self.inner.notify_all()
581 }
582}
583
584//#[stable(feature = "std_debug", since = "1.16.0")]
585impl<const SHM: bool> fmt::Debug for Condvar<SHM> {
586 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
587 f.debug_struct("Condvar").finish_non_exhaustive()
588 }
589}
590
591//#[stable(feature = "condvar_default", since = "1.10.0")]
592impl<const SHM: bool> Default for Condvar<SHM> {
593 /// Creates a `Condvar` which is ready to be waited on and notified.
594 fn default() -> Self {
595 Condvar::new()
596 }
597}