Skip to main content

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}