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(Debug, PartialEq, Eq, Copy, 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}