]> git.lizzy.rs Git - rust.git/commitdiff
Use FUTEX_WAIT_BITSET rather than FUTEX_WAIT on Linux.
authorMara Bos <m-ou.se@m-ou.se>
Thu, 24 Mar 2022 08:51:48 +0000 (09:51 +0100)
committerMara Bos <m-ou.se@m-ou.se>
Thu, 24 Mar 2022 08:51:48 +0000 (09:51 +0100)
library/std/src/sys/unix/futex.rs
library/std/src/sys/unix/time.rs

index cc6da97ec730670d3c7db74be1bec07d3bd42817..adb661d877bb33e1e2f8131e503f4de384c21755 100644 (file)
@@ -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")]
index 59ddd1aa92f813d91151254fba2d5734ec887b45..64c249f33ebc58e1f5ed08c4ad5574d228d6d358 100644 (file)
@@ -299,6 +299,10 @@ pub fn checked_add_duration(&self, other: &Duration) -> Option<Instant> {
         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 {