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}