From 69f0bcb26def1bccdf3774fc487201258b746fca Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Thu, 28 Apr 2022 11:28:40 +0200 Subject: [PATCH] Use futex-based locks and thread parker on DragonFlyBSD. --- library/std/src/sys/unix/futex.rs | 30 +++++++++++++++++++ .../std/src/sys/unix/locks/futex_rwlock.rs | 13 +++++++- library/std/src/sys/unix/locks/mod.rs | 1 + .../std/src/sys_common/thread_parker/mod.rs | 1 + 4 files changed, 44 insertions(+), 1 deletion(-) diff --git a/library/std/src/sys/unix/futex.rs b/library/std/src/sys/unix/futex.rs index 64ed146b036..cfb2d1f07de 100644 --- a/library/std/src/sys/unix/futex.rs +++ b/library/std/src/sys/unix/futex.rs @@ -4,6 +4,7 @@ all(target_os = "emscripten", target_feature = "atomics"), target_os = "openbsd", target_os = "netbsd", + target_os = "dragonfly", ))] use crate::sync::atomic::AtomicU32; @@ -158,6 +159,35 @@ pub fn futex_wake_all(futex: &AtomicU32) { } } +#[cfg(target_os = "dragonfly")] +pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option) -> bool { + use crate::convert::TryFrom; + let r = unsafe { + libc::umtx_sleep( + futex as *const AtomicU32 as *const i32, + expected as i32, + // A timeout of 0 means infinite, so we round smaller timeouts up to 1 millisecond. + // Timeouts larger than i32::MAX milliseconds saturate. + timeout.map_or(0, |d| { + i32::try_from(d.as_millis()).map_or(i32::MAX, |millis| millis.max(1)) + }), + ) + }; + + r == 0 || super::os::errno() != libc::ETIMEDOUT +} + +// DragonflyBSD doesn't tell us how many threads are woken up, so this doesn't return a bool. +#[cfg(target_os = "dragonfly")] +pub fn futex_wake(futex: &AtomicU32) { + unsafe { libc::umtx_wakeup(futex as *const AtomicU32 as *const i32, 1) }; +} + +#[cfg(target_os = "dragonfly")] +pub fn futex_wake_all(futex: &AtomicU32) { + unsafe { libc::umtx_wakeup(futex as *const AtomicU32 as *const i32, i32::MAX) }; +} + #[cfg(target_os = "emscripten")] extern "C" { fn emscripten_futex_wake(addr: *const AtomicU32, count: libc::c_int) -> libc::c_int; diff --git a/library/std/src/sys/unix/locks/futex_rwlock.rs b/library/std/src/sys/unix/locks/futex_rwlock.rs index e42edb25858..8829ed4db25 100644 --- a/library/std/src/sys/unix/locks/futex_rwlock.rs +++ b/library/std/src/sys/unix/locks/futex_rwlock.rs @@ -283,7 +283,18 @@ impl RwLock { /// writer that was about to go to sleep. fn wake_writer(&self) -> bool { self.writer_notify.fetch_add(1, Release); - futex_wake(&self.writer_notify) + cfg_if::cfg_if! { + if #[cfg(target_os = "dragonfly")] { + // DragonFlyBSD doesn't tell us whether it woke up any threads or not. + // So, we always return `false` here, as that still results in correct behaviour. + // The downside is an extra syscall in case both readers and writers were waiting, + // and unnecessarily waking up readers when a writer is about to attempt to lock the lock. + futex_wake(&self.writer_notify); + false + } else { + futex_wake(&self.writer_notify) + } + } } /// Spin for a while, but stop directly at the given condition. diff --git a/library/std/src/sys/unix/locks/mod.rs b/library/std/src/sys/unix/locks/mod.rs index 61fc0d5841d..d39da200dda 100644 --- a/library/std/src/sys/unix/locks/mod.rs +++ b/library/std/src/sys/unix/locks/mod.rs @@ -5,6 +5,7 @@ cfg_if::cfg_if! { all(target_os = "emscripten", target_feature = "atomics"), target_os = "openbsd", target_os = "netbsd", + target_os = "dragonfly", ))] { mod futex; mod futex_rwlock; diff --git a/library/std/src/sys_common/thread_parker/mod.rs b/library/std/src/sys_common/thread_parker/mod.rs index 4ff76aea23d..7fd4d3610ca 100644 --- a/library/std/src/sys_common/thread_parker/mod.rs +++ b/library/std/src/sys_common/thread_parker/mod.rs @@ -5,6 +5,7 @@ cfg_if::cfg_if! { all(target_arch = "wasm32", target_feature = "atomics"), target_os = "openbsd", target_os = "netbsd", + target_os = "dragonfly", ))] { mod futex; pub use futex::Parker;