posix_sync/condvar/owned.rs
1use std::cell::UnsafeCell;
2use std::fmt::{self, Debug};
3use std::marker::{PhantomData, Unpin};
4use std::mem::MaybeUninit;
5use std::sync::atomic::{AtomicUsize, Ordering};
6use std::time::Duration;
7
8use libc::pthread_cond_t;
9
10use super::builders::CondvarBuilder;
11use super::{
12 notify_all_on, notify_one_on, wait_on, wait_on_for, CondvarClock, CondvarSignalError,
13 CondvarWaitError, RawCondvarAlloc, WaitOutcome,
14};
15use crate::mutex::guards::MutexGuard;
16use crate::utils::{AsRawUnderlying, Sealed};
17
18/// A condvar that owns its underlying raw condvar, which is destroyed when `OwnedCondvar` is
19/// dropped. If you want to construct a condvar in shared memory for IPC, use a
20/// [`BorrowedCondvar`](crate::condvar::BorrowedCondvar) instead.
21///
22/// The methods are safe because the allocation belongs to this object, and because the POSIX
23/// invariant that every concurrent waiter passes a guard belonging to the same mutex is checked
24/// at runtime rather than left to the caller.
25pub struct OwnedCondvar {
26 raw: HeapRawCondvar,
27 clock: CondvarClock,
28
29 /// The address of the mutex the first waiter used, or 0 if there has not been one yet.
30 mutex: AtomicUsize,
31
32 /// The `*const UnsafeCell` is to prevent the type from being `UnwindSafe` and `RefUnwindSafe`.
33 _phantom: PhantomData<*const UnsafeCell<()>>,
34}
35
36impl Sealed for OwnedCondvar {}
37unsafe impl Send for OwnedCondvar {}
38unsafe impl Sync for OwnedCondvar {}
39impl Unpin for OwnedCondvar {}
40
41impl AsRawUnderlying for OwnedCondvar {
42 type Underlying = pthread_cond_t;
43
44 fn as_raw_underlying(&self) -> *mut pthread_cond_t {
45 self.raw.as_raw_underlying()
46 }
47}
48
49impl Debug for OwnedCondvar {
50 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
51 f.debug_struct("OwnedCondvar")
52 .field("clock", &self.clock)
53 .finish_non_exhaustive()
54 }
55}
56
57impl Default for OwnedCondvar {
58 /// Equivalent to `CondvarBuilder::new().build_owned()`.
59 fn default() -> Self {
60 CondvarBuilder::new().build_owned()
61 }
62}
63
64impl Drop for OwnedCondvar {
65 /// Calls [`pthread_cond_destroy`](https://man7.org/linux/man-pages/man3/pthread_cond_destroy.3p.html)
66 /// on the underlying condvar.
67 ///
68 /// Every waiter holds a `&self`, so by the time this runs there cannot be one left.
69 #[inline]
70 fn drop(&mut self) {
71 unsafe {
72 let r = libc::pthread_cond_destroy(self.as_raw_underlying());
73 debug_assert_eq!(r, 0);
74 }
75 }
76}
77
78impl OwnedCondvar {
79 /// Creates a condvar with the default attributes. Use [`CondvarBuilder`] to set any of the
80 /// others.
81 #[inline]
82 pub fn new() -> Self {
83 Self::default()
84 }
85
86 /// Allocates the storage without initialising it. Only [`CondvarBuilder::build_owned`], which
87 /// initialises it immediately afterwards, may call this.
88 #[inline]
89 pub(super) fn new_uninit(clock: CondvarClock) -> Self {
90 Self {
91 raw: HeapRawCondvar::new(),
92 clock,
93 mutex: AtomicUsize::new(0),
94 _phantom: PhantomData,
95 }
96 }
97
98 /// The clock this condvar resolves the deadlines of its timed waits against.
99 #[inline]
100 pub fn clock(&self) -> CondvarClock {
101 self.clock
102 }
103
104 /// Wakes one thread waiting on this condvar, if there is one. This is
105 /// [`pthread_cond_signal`](https://man7.org/linux/man-pages/man3/pthread_cond_signal.3p.html).
106 ///
107 /// # Errors
108 /// See [`CondvarSignalError`]
109 #[inline]
110 pub fn notify_one(&self) -> Result<(), CondvarSignalError> {
111 unsafe { notify_one_on(self.as_raw_underlying()) }
112 }
113
114 /// Wakes every thread waiting on this condvar. This is
115 /// [`pthread_cond_broadcast`](https://man7.org/linux/man-pages/man3/pthread_cond_broadcast.3p.html).
116 ///
117 /// # Errors
118 /// See [`CondvarSignalError`]
119 #[inline]
120 pub fn notify_all(&self) -> Result<(), CondvarSignalError> {
121 unsafe { notify_all_on(self.as_raw_underlying()) }
122 }
123
124 /// Releases the mutex `guard` belongs to, blocks until this condvar is notified, and takes the
125 /// mutex again before returning. The guard is still valid afterwards, so the critical section
126 /// simply proceeds.
127 ///
128 /// Wakeups are permitted to be spurious, so this should be wrapped in a loop that re-checks
129 /// the predicate:
130 ///
131 /// ```no_run
132 /// # use posix_sync::condvar::OwnedCondvar;
133 /// # use posix_sync::mutex::{OwnedMutex, robustness_markers::Standard};
134 /// # fn predicate_holds() -> bool { true }
135 /// # let mtx = OwnedMutex::<Standard>::new();
136 /// # let cv = OwnedCondvar::new();
137 /// let mut guard = mtx.lock().unwrap();
138 /// while !predicate_holds() {
139 /// cv.wait(&mut guard).unwrap();
140 /// }
141 /// ```
142 ///
143 /// # Panics
144 /// Panics if a previous wait on this condvar used a guard belonging to a different mutex.
145 ///
146 /// # Errors
147 /// See [`CondvarWaitError`]
148 #[inline]
149 pub fn wait<G>(&self, guard: &mut G) -> Result<(), CondvarWaitError>
150 where
151 G: MutexGuard,
152 {
153 self.bind_to_mutex(guard);
154 unsafe { wait_on(self.as_raw_underlying(), guard) }
155 }
156
157 /// Like [`wait`](Self::wait), but gives up once `timeout` has elapsed on
158 /// [`self.clock()`](Self::clock). The mutex is held again on return either way.
159 ///
160 /// # Panics
161 /// Panics if a previous wait on this condvar used a guard belonging to a different mutex.
162 ///
163 /// # Errors
164 /// See [`CondvarWaitError`]
165 #[inline]
166 pub fn wait_for<G>(
167 &self,
168 guard: &mut G,
169 timeout: Duration,
170 ) -> Result<WaitOutcome, CondvarWaitError>
171 where
172 G: MutexGuard,
173 {
174 self.bind_to_mutex(guard);
175 unsafe { wait_on_for(self.as_raw_underlying(), self.clock, guard, timeout) }
176 }
177
178 /// Returns a pointer to the underlying `pthread_cond_t`.
179 ///
180 /// The pointer stays valid until this `OwnedCondvar` is dropped.
181 #[inline]
182 pub fn as_raw_condvar(&self) -> *mut pthread_cond_t {
183 self.as_raw_underlying()
184 }
185
186 /// Records which mutex this condvar is being waited on with, and rejects any later attempt to
187 /// use a different one.
188 fn bind_to_mutex<G: MutexGuard>(&self, guard: &G) {
189 let mutex = guard.as_raw_underlying() as usize;
190 match self
191 .mutex
192 .compare_exchange(0, mutex, Ordering::AcqRel, Ordering::Acquire)
193 {
194 Ok(_) => {}
195 Err(bound) if bound == mutex => {}
196 Err(_) => panic!("this condvar is already being waited on with a different mutex"),
197 }
198 }
199}
200
201/// A heap-allocated raw condvar allocation.
202struct HeapRawCondvar(*mut MaybeUninit<RawCondvarAlloc>);
203
204impl Sealed for HeapRawCondvar {}
205
206impl AsRawUnderlying for HeapRawCondvar {
207 type Underlying = pthread_cond_t;
208
209 fn as_raw_underlying(&self) -> *mut pthread_cond_t {
210 self.0 as *mut _
211 }
212}
213
214impl HeapRawCondvar {
215 fn new() -> Self {
216 let boxed_uninit = Box::new(MaybeUninit::uninit());
217 let ptr = Box::into_raw(boxed_uninit);
218 Self(ptr)
219 }
220}
221
222impl Drop for HeapRawCondvar {
223 fn drop(&mut self) {
224 // # Safety
225 // This is fine because the pointer came from `Box::into_raw`.
226 unsafe {
227 let _ = Box::from_raw(self.0);
228 }
229 }
230}