Skip to content

Commit 8729929

Browse files
committed
Use FUTEX_WAIT_BITSET rather than FUTEX_WAIT on Linux.
1 parent da4ef04 commit 8729929

File tree

2 files changed

+38
-22
lines changed

2 files changed

+38
-22
lines changed

library/std/src/sys/unix/futex.rs

+34-22
Original file line numberDiff line numberDiff line change
@@ -4,33 +4,45 @@
44
all(target_os = "emscripten", target_feature = "atomics")
55
))]
66

7-
#[cfg(any(target_os = "linux", target_os = "android"))]
8-
use crate::convert::TryInto;
9-
#[cfg(any(target_os = "linux", target_os = "android"))]
10-
use crate::ptr::null;
117
use crate::sync::atomic::AtomicI32;
128
use crate::time::Duration;
139

1410
#[cfg(any(target_os = "linux", target_os = "android"))]
1511
pub fn futex_wait(futex: &AtomicI32, expected: i32, timeout: Option<Duration>) -> bool {
16-
let timespec = timeout.and_then(|d| {
17-
Some(libc::timespec {
18-
// Sleep forever if the timeout is longer than fits in a timespec.
19-
tv_sec: d.as_secs().try_into().ok()?,
20-
// This conversion never truncates, as subsec_nanos is always <1e9.
21-
tv_nsec: d.subsec_nanos() as _,
22-
})
23-
});
24-
let r = unsafe {
25-
libc::syscall(
26-
libc::SYS_futex,
27-
futex as *const AtomicI32,
28-
libc::FUTEX_WAIT | libc::FUTEX_PRIVATE_FLAG,
29-
expected,
30-
timespec.as_ref().map_or(null(), |d| d as *const libc::timespec),
31-
)
32-
};
33-
!(r < 0 && super::os::errno() == libc::ETIMEDOUT)
12+
use super::time::Instant;
13+
use crate::ptr::null;
14+
use crate::sync::atomic::Ordering::Relaxed;
15+
16+
// Calculate the timeout as an absolute timespec.
17+
let timespec =
18+
timeout.and_then(|d| Some(Instant::now().checked_add_duration(&d)?.as_timespec()));
19+
20+
loop {
21+
// No need to wait if the value already changed.
22+
if futex.load(Relaxed) != expected {
23+
return true;
24+
}
25+
26+
// Use FUTEX_WAIT_BITSET rather than FUTEX_WAIT to be able to give an
27+
// absolute time rather than a relative time.
28+
let r = unsafe {
29+
libc::syscall(
30+
libc::SYS_futex,
31+
futex as *const AtomicI32,
32+
libc::FUTEX_WAIT_BITSET | libc::FUTEX_PRIVATE_FLAG,
33+
expected,
34+
timespec.as_ref().map_or(null(), |d| d as *const libc::timespec),
35+
null::<u32>(), // This argument is unused for FUTEX_WAIT_BITSET.
36+
!0u32, // A full bitmask, to make it behave like a regular FUTEX_WAIT.
37+
)
38+
};
39+
40+
match (r < 0).then(super::os::errno) {
41+
Some(libc::ETIMEDOUT) => return false,
42+
Some(libc::EINTR) => continue,
43+
_ => return true,
44+
}
45+
}
3446
}
3547

3648
#[cfg(target_os = "emscripten")]

library/std/src/sys/unix/time.rs

+4
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,10 @@ mod inner {
299299
pub fn checked_sub_duration(&self, other: &Duration) -> Option<Instant> {
300300
Some(Instant { t: self.t.checked_sub_duration(other)? })
301301
}
302+
303+
pub(in crate::sys::unix) fn as_timespec(&self) -> libc::timespec {
304+
self.t.t
305+
}
302306
}
303307

304308
impl fmt::Debug for Instant {

0 commit comments

Comments
 (0)