about summary refs log tree commit diff
path: root/library/std/src/sys/unix
diff options
context:
space:
mode:
authorMara Bos <m-ou.se@m-ou.se>2022-03-24 09:51:48 +0100
committerMara Bos <m-ou.se@m-ou.se>2022-03-24 09:51:48 +0100
commit87299298d925af0943c94d2cc5bb8a2711d9f6b4 (patch)
tree71dcc832b7243a62c4367410a0889119de7098bb /library/std/src/sys/unix
parentda4ef044c1d0e8e58f2ab18459208469110c04be (diff)
downloadrust-87299298d925af0943c94d2cc5bb8a2711d9f6b4.tar.gz
rust-87299298d925af0943c94d2cc5bb8a2711d9f6b4.zip
Use FUTEX_WAIT_BITSET rather than FUTEX_WAIT on Linux.
Diffstat (limited to 'library/std/src/sys/unix')
-rw-r--r--library/std/src/sys/unix/futex.rs56
-rw-r--r--library/std/src/sys/unix/time.rs4
2 files changed, 38 insertions, 22 deletions
diff --git a/library/std/src/sys/unix/futex.rs b/library/std/src/sys/unix/futex.rs
index cc6da97ec73..adb661d877b 100644
--- a/library/std/src/sys/unix/futex.rs
+++ b/library/std/src/sys/unix/futex.rs
@@ -4,33 +4,45 @@
     all(target_os = "emscripten", target_feature = "atomics")
 ))]
 
-#[cfg(any(target_os = "linux", target_os = "android"))]
-use crate::convert::TryInto;
-#[cfg(any(target_os = "linux", target_os = "android"))]
-use crate::ptr::null;
 use crate::sync::atomic::AtomicI32;
 use crate::time::Duration;
 
 #[cfg(any(target_os = "linux", target_os = "android"))]
 pub fn futex_wait(futex: &AtomicI32, expected: i32, timeout: Option<Duration>) -> bool {
-    let timespec = timeout.and_then(|d| {
-        Some(libc::timespec {
-            // Sleep forever if the timeout is longer than fits in a timespec.
-            tv_sec: d.as_secs().try_into().ok()?,
-            // This conversion never truncates, as subsec_nanos is always <1e9.
-            tv_nsec: d.subsec_nanos() as _,
-        })
-    });
-    let r = unsafe {
-        libc::syscall(
-            libc::SYS_futex,
-            futex as *const AtomicI32,
-            libc::FUTEX_WAIT | libc::FUTEX_PRIVATE_FLAG,
-            expected,
-            timespec.as_ref().map_or(null(), |d| d as *const libc::timespec),
-        )
-    };
-    !(r < 0 && super::os::errno() == libc::ETIMEDOUT)
+    use super::time::Instant;
+    use crate::ptr::null;
+    use crate::sync::atomic::Ordering::Relaxed;
+
+    // Calculate the timeout as an absolute timespec.
+    let timespec =
+        timeout.and_then(|d| Some(Instant::now().checked_add_duration(&d)?.as_timespec()));
+
+    loop {
+        // No need to wait if the value already changed.
+        if futex.load(Relaxed) != expected {
+            return true;
+        }
+
+        // Use FUTEX_WAIT_BITSET rather than FUTEX_WAIT to be able to give an
+        // absolute time rather than a relative time.
+        let r = unsafe {
+            libc::syscall(
+                libc::SYS_futex,
+                futex as *const AtomicI32,
+                libc::FUTEX_WAIT_BITSET | libc::FUTEX_PRIVATE_FLAG,
+                expected,
+                timespec.as_ref().map_or(null(), |d| d as *const libc::timespec),
+                null::<u32>(), // This argument is unused for FUTEX_WAIT_BITSET.
+                !0u32,         // A full bitmask, to make it behave like a regular FUTEX_WAIT.
+            )
+        };
+
+        match (r < 0).then(super::os::errno) {
+            Some(libc::ETIMEDOUT) => return false,
+            Some(libc::EINTR) => continue,
+            _ => return true,
+        }
+    }
 }
 
 #[cfg(target_os = "emscripten")]
diff --git a/library/std/src/sys/unix/time.rs b/library/std/src/sys/unix/time.rs
index 59ddd1aa92f..64c249f33eb 100644
--- a/library/std/src/sys/unix/time.rs
+++ b/library/std/src/sys/unix/time.rs
@@ -299,6 +299,10 @@ mod inner {
         pub fn checked_sub_duration(&self, other: &Duration) -> Option<Instant> {
             Some(Instant { t: self.t.checked_sub_duration(other)? })
         }
+
+        pub(in crate::sys::unix) fn as_timespec(&self) -> libc::timespec {
+            self.t.t
+        }
     }
 
     impl fmt::Debug for Instant {