std/sys/pal/unix/sync/
condvar.rs1use super::Mutex;
2use crate::pin::Pin;
3use crate::sys::helpers::COpaque;
4#[cfg(not(any(target_os = "nto", target_os = "qnx")))]
5use crate::sys::pal::time::TIMESPEC_MAX;
6#[cfg(any(target_os = "nto", target_os = "qnx"))]
7use crate::sys::pal::time::TIMESPEC_MAX_CAPPED;
8use crate::time::Duration;
9
10pub struct Condvar {
11 inner: COpaque<libc::pthread_cond_t>,
12}
13
14impl Condvar {
15 pub fn new() -> Condvar {
16 Condvar { inner: COpaque::new(libc::PTHREAD_COND_INITIALIZER) }
17 }
18
19 #[inline]
20 fn raw(self: Pin<&Self>) -> *mut libc::pthread_cond_t {
21 let inner = unsafe { self.map_unchecked(|c| &c.inner) };
22 inner.get()
23 }
24
25 #[inline]
28 pub unsafe fn notify_one(self: Pin<&Self>) {
29 let r = unsafe { libc::pthread_cond_signal(self.raw()) };
30 debug_assert_eq!(r, 0);
31 }
32
33 #[inline]
36 pub unsafe fn notify_all(self: Pin<&Self>) {
37 let r = unsafe { libc::pthread_cond_broadcast(self.raw()) };
38 debug_assert_eq!(r, 0);
39 }
40
41 #[inline]
46 pub unsafe fn wait(self: Pin<&Self>, mutex: Pin<&Mutex>) {
47 let r = unsafe { libc::pthread_cond_wait(self.raw(), mutex.raw()) };
48 debug_assert_eq!(r, 0);
49 }
50}
51
52#[cfg(not(target_vendor = "apple"))]
53impl Condvar {
54 pub unsafe fn wait_timeout(self: Pin<&Self>, mutex: Pin<&Mutex>, dur: Duration) -> bool {
59 use crate::sys::pal::time::Timespec;
60
61 let mutex = mutex.raw();
62
63 #[cfg(target_os = "cygwin")]
69 let dur = Duration::min(dur, Duration::from_secs(1000 * 365 * 86400));
70
71 let timeout = Timespec::now(Self::CLOCK).checked_add_duration(&dur);
72
73 #[cfg(not(any(target_os = "nto", target_os = "qnx")))]
74 let timeout = timeout.and_then(|t| t.to_timespec()).unwrap_or(TIMESPEC_MAX);
75
76 #[cfg(any(target_os = "nto", target_os = "qnx"))]
77 let timeout = timeout.and_then(|t| t.to_timespec_capped()).unwrap_or(TIMESPEC_MAX_CAPPED);
78
79 let r = unsafe { libc::pthread_cond_timedwait(self.raw(), mutex, &timeout) };
80 assert!(r == libc::ETIMEDOUT || r == 0);
81 r == 0
82 }
83}
84
85#[cfg(target_vendor = "apple")]
90impl Condvar {
91 pub unsafe fn wait_timeout(self: Pin<&Self>, mutex: Pin<&Mutex>, dur: Duration) -> bool {
96 let mutex = mutex.raw();
97
98 const MAX_DURATION: Duration = Duration::from_nanos(u64::MAX);
119
120 let (dur, clamped) = if dur <= MAX_DURATION { (dur, false) } else { (MAX_DURATION, true) };
121
122 let timeout = if let Ok(tv_sec) = dur.as_secs().try_into() {
124 libc::timespec { tv_sec, tv_nsec: dur.subsec_nanos() as _ }
125 } else {
126 TIMESPEC_MAX
128 };
129
130 let r = unsafe { libc::pthread_cond_timedwait_relative_np(self.raw(), mutex, &timeout) };
131 assert!(r == libc::ETIMEDOUT || r == 0);
132 r == 0 || clamped
135 }
136}
137
138#[cfg(not(any(
139 target_os = "android",
140 target_vendor = "apple",
141 target_os = "espidf",
142 target_os = "horizon",
143 target_os = "l4re",
144 target_os = "redox",
145 target_os = "teeos",
146)))]
147impl Condvar {
148 pub const PRECISE_TIMEOUT: bool = true;
149 const CLOCK: libc::clockid_t = libc::CLOCK_MONOTONIC;
150
151 pub unsafe fn init(self: Pin<&mut Self>) {
154 use crate::pin::pin;
155
156 struct AttrGuard<'a>(Pin<&'a COpaque<libc::pthread_condattr_t>>);
157 impl Drop for AttrGuard<'_> {
158 fn drop(&mut self) {
159 unsafe {
160 let result = libc::pthread_condattr_destroy(self.0.get());
161 assert_eq!(result, 0);
162 }
163 }
164 }
165
166 unsafe {
167 let attr = pin!(COpaque::<libc::pthread_condattr_t>::uninit());
168 let attr = attr.into_ref();
170 let r = libc::pthread_condattr_init(attr.get());
171 assert_eq!(r, 0);
172 let attr = AttrGuard(attr);
173 let r = libc::pthread_condattr_setclock(attr.0.get(), Self::CLOCK);
174 assert_eq!(r, 0);
175 let r = libc::pthread_cond_init(self.as_ref().raw(), attr.0.get());
176 assert_eq!(r, 0);
177 }
178 }
179}
180
181#[cfg(target_vendor = "apple")]
182impl Condvar {
183 pub const PRECISE_TIMEOUT: bool = true;
186
187 pub unsafe fn init(self: Pin<&mut Self>) {
190 }
193}
194
195#[cfg(any(
197 target_os = "android",
198 target_os = "espidf",
199 target_os = "horizon",
200 target_os = "l4re",
201 target_os = "redox",
202 target_os = "teeos",
203))]
204impl Condvar {
205 pub const PRECISE_TIMEOUT: bool = false;
206 const CLOCK: libc::clockid_t = libc::CLOCK_REALTIME;
207
208 pub unsafe fn init(self: Pin<&mut Self>) {
211 if cfg!(any(target_os = "espidf", target_os = "horizon", target_os = "teeos")) {
212 let r = unsafe { libc::pthread_cond_init(self.as_ref().raw(), crate::ptr::null()) };
217 assert_eq!(r, 0);
218 }
219 }
220}
221
222impl !Unpin for Condvar {}
223
224unsafe impl Sync for Condvar {}
225unsafe impl Send for Condvar {}
226
227impl Drop for Condvar {
228 #[inline]
229 fn drop(&mut self) {
230 let inner = unsafe { Pin::new_unchecked(&self.inner) };
231 let r = unsafe { libc::pthread_cond_destroy(inner.get()) };
232 if cfg!(target_os = "dragonfly") {
233 debug_assert!(r == 0 || r == libc::EINVAL);
238 } else {
239 debug_assert_eq!(r, 0);
240 }
241 }
242}