From 2cf0f64722a92dffa12d43a4c0383a9d76becbcc Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Sat, 19 Sep 2020 18:03:10 +0200 Subject: Move linux-specific futex code into `sys` module. --- library/std/src/sys/unix/futex.rs | 38 ++++++++++++ library/std/src/sys/unix/mod.rs | 1 + library/std/src/thread/parker/futex.rs | 73 +++++++++++++++++++++++ library/std/src/thread/parker/linux.rs | 106 --------------------------------- library/std/src/thread/parker/mod.rs | 4 +- 5 files changed, 114 insertions(+), 108 deletions(-) create mode 100644 library/std/src/sys/unix/futex.rs create mode 100644 library/std/src/thread/parker/futex.rs delete mode 100644 library/std/src/thread/parker/linux.rs (limited to 'library/std/src') diff --git a/library/std/src/sys/unix/futex.rs b/library/std/src/sys/unix/futex.rs new file mode 100644 index 00000000000..6af06aa5f7e --- /dev/null +++ b/library/std/src/sys/unix/futex.rs @@ -0,0 +1,38 @@ +#![cfg(any(target_os = "linux", target_os = "android"))] + +use crate::sync::atomic::AtomicI32; +use crate::time::Duration; + +pub fn futex_wait(futex: &AtomicI32, expected: i32, timeout: Option) { + let timespec; + let timespec_ptr = match timeout { + Some(timeout) => { + timespec = libc::timespec { + tv_sec: timeout.as_secs() as _, + tv_nsec: timeout.subsec_nanos() as _, + }; + ×pec as *const libc::timespec + } + None => crate::ptr::null(), + }; + unsafe { + libc::syscall( + libc::SYS_futex, + futex as *const AtomicI32, + libc::FUTEX_WAIT | libc::FUTEX_PRIVATE_FLAG, + expected, + timespec_ptr, + ); + } +} + +pub fn futex_wake(futex: &AtomicI32) { + unsafe { + libc::syscall( + libc::SYS_futex, + futex as *const AtomicI32, + libc::FUTEX_WAKE | libc::FUTEX_PRIVATE_FLAG, + 1, + ); + } +} diff --git a/library/std/src/sys/unix/mod.rs b/library/std/src/sys/unix/mod.rs index eddf00d3979..6c623e21099 100644 --- a/library/std/src/sys/unix/mod.rs +++ b/library/std/src/sys/unix/mod.rs @@ -49,6 +49,7 @@ pub mod env; pub mod ext; pub mod fd; pub mod fs; +pub mod futex; pub mod io; #[cfg(target_os = "l4re")] mod l4re; diff --git a/library/std/src/thread/parker/futex.rs b/library/std/src/thread/parker/futex.rs new file mode 100644 index 00000000000..2d7a7770508 --- /dev/null +++ b/library/std/src/thread/parker/futex.rs @@ -0,0 +1,73 @@ +use crate::sync::atomic::AtomicI32; +use crate::sync::atomic::Ordering::{Acquire, Release}; +use crate::sys::futex::{futex_wait, futex_wake}; +use crate::time::Duration; + +const PARKED: i32 = -1; +const EMPTY: i32 = 0; +const NOTIFIED: i32 = 1; + +pub struct Parker { + state: AtomicI32, +} + +impl Parker { + #[inline] + pub const fn new() -> Self { + Parker { state: AtomicI32::new(EMPTY) } + } + + // Assumes this is only called by the thread that owns the Parker, + // which means that `self.state != PARKED`. + pub unsafe fn park(&self) { + // Change NOTIFIED=>EMPTY or EMPTY=>PARKED, and directly return in the + // first case. + if self.state.fetch_sub(1, Acquire) == NOTIFIED { + return; + } + loop { + // Wait for something to happen, assuming it's still set to PARKED. + futex_wait(&self.state, PARKED, None); + // Change NOTIFIED=>EMPTY and return in that case. + if self.state.compare_and_swap(NOTIFIED, EMPTY, Acquire) == NOTIFIED { + return; + } else { + // Spurious wake up. We loop to try again. + } + } + } + + // Assumes this is only called by the thread that owns the Parker, + // which means that `self.state != PARKED`. + pub unsafe fn park_timeout(&self, timeout: Duration) { + // Change NOTIFIED=>EMPTY or EMPTY=>PARKED, and directly return in the + // first case. + if self.state.fetch_sub(1, Acquire) == NOTIFIED { + return; + } + // Wait for something to happen, assuming it's still set to PARKED. + futex_wait(&self.state, PARKED, Some(timeout)); + // This is not just a store, because we need to establish a + // release-acquire ordering with unpark(). + if self.state.swap(EMPTY, Acquire) == NOTIFIED { + // Woke up because of unpark(). + } else { + // Timeout or spurious wake up. + // We return either way, because we can't easily tell if it was the + // timeout or not. + } + } + + #[inline] + pub fn unpark(&self) { + // Change PARKED=>NOTIFIED, EMPTY=>NOTIFIED, or NOTIFIED=>NOTIFIED, and + // wake the thread in the first case. + // + // Note that even NOTIFIED=>NOTIFIED results in a write. This is on + // purpose, to make sure every unpark() has a release-acquire ordering + // with park(). + if self.state.swap(NOTIFIED, Release) == PARKED { + futex_wake(&self.state); + } + } +} diff --git a/library/std/src/thread/parker/linux.rs b/library/std/src/thread/parker/linux.rs deleted file mode 100644 index 090c83fbb40..00000000000 --- a/library/std/src/thread/parker/linux.rs +++ /dev/null @@ -1,106 +0,0 @@ -use crate::sync::atomic::AtomicI32; -use crate::sync::atomic::Ordering::{Acquire, Release}; -use crate::time::Duration; - -const PARKED: i32 = -1; -const EMPTY: i32 = 0; -const NOTIFIED: i32 = 1; - -pub struct Parker { - state: AtomicI32, -} - -impl Parker { - #[inline] - pub const fn new() -> Self { - Parker { state: AtomicI32::new(EMPTY) } - } - - // Assumes this is only called by the thread that owns the Parker, - // which means that `self.state != PARKED`. - pub unsafe fn park(&self) { - // Change NOTIFIED=>EMPTY or EMPTY=>PARKED, and directly return in the - // first case. - if self.state.fetch_sub(1, Acquire) == NOTIFIED { - return; - } - loop { - // Wait for something to happen, assuming it's still set to PARKED. - futex_wait(&self.state, PARKED, None); - // Change NOTIFIED=>EMPTY and return in that case. - if self.state.compare_and_swap(NOTIFIED, EMPTY, Acquire) == NOTIFIED { - return; - } else { - // Spurious wake up. We loop to try again. - } - } - } - - // Assumes this is only called by the thread that owns the Parker, - // which means that `self.state != PARKED`. - pub unsafe fn park_timeout(&self, timeout: Duration) { - // Change NOTIFIED=>EMPTY or EMPTY=>PARKED, and directly return in the - // first case. - if self.state.fetch_sub(1, Acquire) == NOTIFIED { - return; - } - // Wait for something to happen, assuming it's still set to PARKED. - futex_wait(&self.state, PARKED, Some(timeout)); - // This is not just a store, because we need to establish a - // release-acquire ordering with unpark(). - if self.state.swap(EMPTY, Acquire) == NOTIFIED { - // Woke up because of unpark(). - } else { - // Timeout or spurious wake up. - // We return either way, because we can't easily tell if it was the - // timeout or not. - } - } - - #[inline] - pub fn unpark(&self) { - // Change PARKED=>NOTIFIED, EMPTY=>NOTIFIED, or NOTIFIED=>NOTIFIED, and - // wake the thread in the first case. - // - // Note that even NOTIFIED=>NOTIFIED results in a write. This is on - // purpose, to make sure every unpark() has a release-acquire ordering - // with park(). - if self.state.swap(NOTIFIED, Release) == PARKED { - futex_wake(&self.state); - } - } -} - -fn futex_wait(futex: &AtomicI32, expected: i32, timeout: Option) { - let timespec; - let timespec_ptr = match timeout { - Some(timeout) => { - timespec = libc::timespec { - tv_sec: timeout.as_secs() as _, - tv_nsec: timeout.subsec_nanos() as _, - }; - ×pec as *const libc::timespec - } - None => crate::ptr::null(), - }; - unsafe { - libc::syscall( - libc::SYS_futex, - futex as *const AtomicI32, - libc::FUTEX_WAIT | libc::FUTEX_PRIVATE_FLAG, - expected, - timespec_ptr, - ); - } -} - -fn futex_wake(futex: &AtomicI32) { - unsafe { - libc::syscall( - libc::SYS_futex, - futex as *const AtomicI32, - libc::FUTEX_WAKE | libc::FUTEX_PRIVATE_FLAG, - 1, - ); - } -} diff --git a/library/std/src/thread/parker/mod.rs b/library/std/src/thread/parker/mod.rs index 4dc5e1aa271..23c17c8e2cf 100644 --- a/library/std/src/thread/parker/mod.rs +++ b/library/std/src/thread/parker/mod.rs @@ -1,7 +1,7 @@ cfg_if::cfg_if! { if #[cfg(any(target_os = "linux", target_os = "android"))] { - mod linux; - pub use linux::Parker; + mod futex; + pub use futex::Parker; } else { mod generic; pub use generic::Parker; -- cgit 1.4.1-3-g733a5