-
Notifications
You must be signed in to change notification settings - Fork 13.4k
Replace Linux Mutex and Condvar with futex based ones. #95035
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 6 commits
Commits
Show all changes
14 commits
Select commit
Hold shift + click to select a range
4fbd71c
Return timeout status in futex_wait.
m-ou-se 73d6348
Add futex_wake_all.
m-ou-se 7f26ade
Replace Linux Mutex and Condvar with futex based ones.
m-ou-se 10b6f33
Update tests.
m-ou-se da4ef04
Spin before blocking in Mutex::lock.
m-ou-se 8729929
Use FUTEX_WAIT_BITSET rather than FUTEX_WAIT on Linux.
m-ou-se 23badeb
Make Timespec available in sys::unix.
m-ou-se c9ae3fe
Explicitly use CLOCK_MONOTONIC in futex_wait.
m-ou-se c49887d
Add comment about futex_wait timeout.
m-ou-se 6392f15
Shuffle around #[inline] and #[cold] in mutex impl.
m-ou-se 321690c
Don't spin on contended mutexes.
m-ou-se 4b1b305
Use MaybeUninit for clock_gettime's timespec.
m-ou-se 104e95f
Mark unix::locks::futex::Mutex::new as #[inline].
m-ou-se 650315e
Reword comment in futex condvar implementation.
m-ou-se File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,159 @@ | ||
use crate::sync::atomic::{ | ||
AtomicI32, | ||
Ordering::{Acquire, Relaxed, Release}, | ||
}; | ||
use crate::sys::futex::{futex_wait, futex_wake, futex_wake_all}; | ||
use crate::time::Duration; | ||
|
||
pub type MovableMutex = Mutex; | ||
pub type MovableCondvar = Condvar; | ||
|
||
pub struct Mutex { | ||
/// 0: unlocked | ||
/// 1: locked, no other threads waiting | ||
/// 2: locked, and other threads waiting (contended) | ||
futex: AtomicI32, | ||
m-ou-se marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
impl Mutex { | ||
pub const fn new() -> Self { | ||
Self { futex: AtomicI32::new(0) } | ||
} | ||
|
||
#[inline] | ||
pub unsafe fn init(&mut self) {} | ||
|
||
#[inline] | ||
pub unsafe fn destroy(&self) {} | ||
|
||
#[inline] | ||
pub unsafe fn try_lock(&self) -> bool { | ||
self.futex.compare_exchange(0, 1, Acquire, Relaxed).is_ok() | ||
} | ||
|
||
#[inline] | ||
pub unsafe fn lock(&self) { | ||
if self.futex.compare_exchange(0, 1, Acquire, Relaxed).is_err() { | ||
self.lock_contended(); | ||
} | ||
} | ||
|
||
fn lock_contended(&self) { | ||
m-ou-se marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// Spin first to speed things up if the lock is released quickly. | ||
let mut state = self.spin(); | ||
|
||
// If it's unlocked now, attempt to take the lock | ||
// without marking it as contended. | ||
if state == 0 { | ||
match self.futex.compare_exchange(0, 1, Acquire, Relaxed) { | ||
Ok(_) => return, // Locked! | ||
Err(s) => state = s, | ||
} | ||
} | ||
|
||
loop { | ||
// Put the lock in contended state. | ||
// We avoid an unnecessary write if it as already set to 2, | ||
// to be friendlier for the caches. | ||
if state != 2 && self.futex.swap(2, Acquire) == 0 { | ||
// We changed it from 0 to 2, so we just succesfully locked it. | ||
return; | ||
} | ||
|
||
// Wait for the futex to change state, assuming it is still 2. | ||
futex_wait(&self.futex, 2, None); | ||
|
||
// Spin again after waking up. | ||
m-ou-se marked this conversation as resolved.
Show resolved
Hide resolved
|
||
state = self.spin(); | ||
} | ||
} | ||
|
||
fn spin(&self) -> i32 { | ||
m-ou-se marked this conversation as resolved.
Show resolved
Hide resolved
|
||
let mut spin = 100; | ||
loop { | ||
// We only use `load` (and not `swap` or `compare_exchange`) | ||
// while spinning, to be easier on the caches. | ||
let state = self.futex.load(Relaxed); | ||
|
||
if state == 0 || spin == 0 { | ||
return state; | ||
} | ||
|
||
crate::hint::spin_loop(); | ||
spin -= 1; | ||
} | ||
} | ||
|
||
#[inline] | ||
pub unsafe fn unlock(&self) { | ||
if self.futex.swap(0, Release) == 2 { | ||
// We only wake up one thread. When that thread locks the mutex, it | ||
// will mark the mutex as contended (2) (see lock_contended above), | ||
// which makes sure that any other waiting threads will also be | ||
// woken up eventually. | ||
futex_wake(&self.futex); | ||
m-ou-se marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
} | ||
} | ||
|
||
pub struct Condvar { | ||
// The value of this atomic is simply incremented on every notification. | ||
// This is used by `.wait()` to not miss any notifications after | ||
// unlocking the mutex and before waiting for notifications. | ||
futex: AtomicI32, | ||
} | ||
|
||
impl Condvar { | ||
#[inline] | ||
pub const fn new() -> Self { | ||
Self { futex: AtomicI32::new(0) } | ||
} | ||
|
||
#[inline] | ||
pub unsafe fn init(&mut self) {} | ||
|
||
#[inline] | ||
pub unsafe fn destroy(&self) {} | ||
|
||
// All the memory orderings here are `Relaxed`, | ||
// because synchronization is done by unlocking and locking the mutex. | ||
|
||
#[inline] | ||
m-ou-se marked this conversation as resolved.
Show resolved
Hide resolved
|
||
pub unsafe fn notify_one(&self) { | ||
self.futex.fetch_add(1, Relaxed); | ||
m-ou-se marked this conversation as resolved.
Show resolved
Hide resolved
|
||
futex_wake(&self.futex); | ||
} | ||
|
||
#[inline] | ||
m-ou-se marked this conversation as resolved.
Show resolved
Hide resolved
|
||
pub unsafe fn notify_all(&self) { | ||
self.futex.fetch_add(1, Relaxed); | ||
futex_wake_all(&self.futex); | ||
} | ||
|
||
#[inline] | ||
m-ou-se marked this conversation as resolved.
Show resolved
Hide resolved
|
||
pub unsafe fn wait(&self, mutex: &Mutex) { | ||
self.wait_optional_timeout(mutex, None); | ||
} | ||
|
||
#[inline] | ||
m-ou-se marked this conversation as resolved.
Show resolved
Hide resolved
|
||
pub unsafe fn wait_timeout(&self, mutex: &Mutex, timeout: Duration) -> bool { | ||
self.wait_optional_timeout(mutex, Some(timeout)) | ||
} | ||
|
||
unsafe fn wait_optional_timeout(&self, mutex: &Mutex, timeout: Option<Duration>) -> bool { | ||
// Check the notification counter before we unlock the mutex. | ||
m-ou-se marked this conversation as resolved.
Show resolved
Hide resolved
|
||
let futex_value = self.futex.load(Relaxed); | ||
|
||
// Unlock the mutex before going to sleep. | ||
mutex.unlock(); | ||
|
||
// Wait, but only if there hasn't been any | ||
// notification since we unlocked the mutex. | ||
let r = futex_wait(&self.futex, futex_value, timeout); | ||
|
||
// Lock the mutex again. | ||
mutex.lock(); | ||
|
||
r | ||
} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,24 @@ | ||
mod pthread_condvar; | ||
mod pthread_mutex; | ||
mod pthread_remutex; | ||
mod pthread_rwlock; | ||
pub use pthread_condvar::{Condvar, MovableCondvar}; | ||
pub use pthread_mutex::{MovableMutex, Mutex}; | ||
pub use pthread_remutex::ReentrantMutex; | ||
pub use pthread_rwlock::{MovableRWLock, RWLock}; | ||
cfg_if::cfg_if! { | ||
if #[cfg(any( | ||
target_os = "linux", | ||
target_os = "android", | ||
))] { | ||
mod futex; | ||
#[allow(dead_code)] | ||
mod pthread_mutex; // Only used for PthreadMutexAttr, needed by pthread_remutex. | ||
mod pthread_remutex; // FIXME: Implement this using a futex | ||
mod pthread_rwlock; // FIXME: Implement this using a futex | ||
pub use futex::{Mutex, MovableMutex, Condvar, MovableCondvar}; | ||
pub use pthread_remutex::ReentrantMutex; | ||
pub use pthread_rwlock::{RWLock, MovableRWLock}; | ||
} else { | ||
mod pthread_mutex; | ||
mod pthread_remutex; | ||
mod pthread_rwlock; | ||
mod pthread_condvar; | ||
pub use pthread_mutex::{Mutex, MovableMutex}; | ||
pub use pthread_remutex::ReentrantMutex; | ||
pub use pthread_rwlock::{RWLock, MovableRWLock}; | ||
pub use pthread_condvar::{Condvar, MovableCondvar}; | ||
} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.