4 all(target_os = "emscripten", target_feature = "atomics"),
7 target_os = "dragonfly",
10 use crate::sync::atomic::AtomicU32;
11 use crate::time::Duration;
13 /// Wait for a futex_wake operation to wake us.
15 /// Returns directly if the futex doesn't hold the expected value.
17 /// Returns false on timeout, and true in all other cases.
18 #[cfg(any(target_os = "linux", target_os = "android", target_os = "freebsd"))]
19 pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option<Duration>) -> bool {
20 use super::time::Timespec;
22 use crate::sync::atomic::Ordering::Relaxed;
24 // Calculate the timeout as an absolute timespec.
26 // Overflows are rounded up to an infinite timeout (None).
27 let timespec = timeout
28 .and_then(|d| Timespec::now(libc::CLOCK_MONOTONIC).checked_add_duration(&d))
29 .and_then(|t| t.to_timespec());
32 // No need to wait if the value already changed.
33 if futex.load(Relaxed) != expected {
39 if #[cfg(target_os = "freebsd")] {
40 // FreeBSD doesn't have futex(), but it has
41 // _umtx_op(UMTX_OP_WAIT_UINT_PRIVATE), which is nearly
42 // identical. It supports absolute timeouts through a flag
43 // in the _umtx_time struct.
44 let umtx_timeout = timespec.map(|t| libc::_umtx_time {
46 _flags: libc::UMTX_ABSTIME,
47 _clockid: libc::CLOCK_MONOTONIC as u32,
49 let umtx_timeout_ptr = umtx_timeout.as_ref().map_or(null(), |t| t as *const _);
50 let umtx_timeout_size = umtx_timeout.as_ref().map_or(0, |t| crate::mem::size_of_val(t));
52 futex as *const AtomicU32 as *mut _,
53 libc::UMTX_OP_WAIT_UINT_PRIVATE,
54 expected as libc::c_ulong,
55 crate::ptr::invalid_mut(umtx_timeout_size),
56 umtx_timeout_ptr as *mut _,
58 } else if #[cfg(any(target_os = "linux", target_os = "android"))] {
59 // Use FUTEX_WAIT_BITSET rather than FUTEX_WAIT to be able to give an
60 // absolute time rather than a relative time.
63 futex as *const AtomicU32,
64 libc::FUTEX_WAIT_BITSET | libc::FUTEX_PRIVATE_FLAG,
66 timespec.as_ref().map_or(null(), |t| t as *const libc::timespec),
67 null::<u32>(), // This argument is unused for FUTEX_WAIT_BITSET.
68 !0u32, // A full bitmask, to make it behave like a regular FUTEX_WAIT.
71 compile_error!("unknown target_os");
76 match (r < 0).then(super::os::errno) {
77 Some(libc::ETIMEDOUT) => return false,
78 Some(libc::EINTR) => continue,
84 /// Wake up one thread that's blocked on futex_wait on this futex.
86 /// Returns true if this actually woke up such a thread,
87 /// or false if no thread was waiting on this futex.
89 /// On some platforms, this always returns false.
90 #[cfg(any(target_os = "linux", target_os = "android"))]
91 pub fn futex_wake(futex: &AtomicU32) -> bool {
92 let ptr = futex as *const AtomicU32;
93 let op = libc::FUTEX_WAKE | libc::FUTEX_PRIVATE_FLAG;
94 unsafe { libc::syscall(libc::SYS_futex, ptr, op, 1) > 0 }
97 /// Wake up all threads that are waiting on futex_wait on this futex.
98 #[cfg(any(target_os = "linux", target_os = "android"))]
99 pub fn futex_wake_all(futex: &AtomicU32) {
100 let ptr = futex as *const AtomicU32;
101 let op = libc::FUTEX_WAKE | libc::FUTEX_PRIVATE_FLAG;
103 libc::syscall(libc::SYS_futex, ptr, op, i32::MAX);
107 // FreeBSD doesn't tell us how many threads are woken up, so this always returns false.
108 #[cfg(target_os = "freebsd")]
109 pub fn futex_wake(futex: &AtomicU32) -> bool {
110 use crate::ptr::null_mut;
113 futex as *const AtomicU32 as *mut _,
114 libc::UMTX_OP_WAKE_PRIVATE,
123 #[cfg(target_os = "freebsd")]
124 pub fn futex_wake_all(futex: &AtomicU32) {
125 use crate::ptr::null_mut;
128 futex as *const AtomicU32 as *mut _,
129 libc::UMTX_OP_WAKE_PRIVATE,
130 i32::MAX as libc::c_ulong,
137 #[cfg(target_os = "openbsd")]
138 pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option<Duration>) -> bool {
139 use super::time::Timespec;
140 use crate::ptr::{null, null_mut};
142 // Overflows are rounded up to an infinite timeout (None).
143 let timespec = timeout
144 .and_then(|d| Timespec::zero().checked_add_duration(&d))
145 .and_then(|t| t.to_timespec());
149 futex as *const AtomicU32 as *mut u32,
152 timespec.as_ref().map_or(null(), |t| t as *const libc::timespec),
157 r == 0 || super::os::errno() != libc::ETIMEDOUT
160 #[cfg(target_os = "openbsd")]
161 pub fn futex_wake(futex: &AtomicU32) -> bool {
162 use crate::ptr::{null, null_mut};
164 libc::futex(futex as *const AtomicU32 as *mut u32, libc::FUTEX_WAKE, 1, null(), null_mut())
169 #[cfg(target_os = "openbsd")]
170 pub fn futex_wake_all(futex: &AtomicU32) {
171 use crate::ptr::{null, null_mut};
174 futex as *const AtomicU32 as *mut u32,
183 #[cfg(target_os = "dragonfly")]
184 pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option<Duration>) -> bool {
185 // A timeout of 0 means infinite.
186 // We round smaller timeouts up to 1 millisecond.
187 // Overflows are rounded up to an infinite timeout.
189 timeout.and_then(|d| Some(i32::try_from(d.as_millis()).ok()?.max(1))).unwrap_or(0);
192 libc::umtx_sleep(futex as *const AtomicU32 as *const i32, expected as i32, timeout_ms)
195 r == 0 || super::os::errno() != libc::ETIMEDOUT
198 // DragonflyBSD doesn't tell us how many threads are woken up, so this always returns false.
199 #[cfg(target_os = "dragonfly")]
200 pub fn futex_wake(futex: &AtomicU32) -> bool {
201 unsafe { libc::umtx_wakeup(futex as *const AtomicU32 as *const i32, 1) };
205 #[cfg(target_os = "dragonfly")]
206 pub fn futex_wake_all(futex: &AtomicU32) {
207 unsafe { libc::umtx_wakeup(futex as *const AtomicU32 as *const i32, i32::MAX) };
210 #[cfg(target_os = "emscripten")]
212 fn emscripten_futex_wake(addr: *const AtomicU32, count: libc::c_int) -> libc::c_int;
213 fn emscripten_futex_wait(
214 addr: *const AtomicU32,
216 max_wait_ms: libc::c_double,
220 #[cfg(target_os = "emscripten")]
221 pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option<Duration>) -> bool {
223 emscripten_futex_wait(
226 timeout.map_or(f64::INFINITY, |d| d.as_secs_f64() * 1000.0),
227 ) != -libc::ETIMEDOUT
231 #[cfg(target_os = "emscripten")]
232 pub fn futex_wake(futex: &AtomicU32) -> bool {
233 unsafe { emscripten_futex_wake(futex, 1) > 0 }
236 #[cfg(target_os = "emscripten")]
237 pub fn futex_wake_all(futex: &AtomicU32) {
238 unsafe { emscripten_futex_wake(futex, i32::MAX) };