Skip to content

Commit 7f26ade

Browse files
committed
Replace Linux Mutex and Condvar with futex based ones.
1 parent 73d6348 commit 7f26ade

File tree

2 files changed

+149
-8
lines changed

2 files changed

+149
-8
lines changed
+125
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
use crate::sync::atomic::{
2+
AtomicI32,
3+
Ordering::{Acquire, Relaxed, Release},
4+
};
5+
use crate::sys::futex::{futex_wait, futex_wake, futex_wake_all};
6+
use crate::time::Duration;
7+
8+
pub type MovableMutex = Mutex;
9+
pub type MovableCondvar = Condvar;
10+
11+
pub struct Mutex {
12+
/// 0: unlocked
13+
/// 1: locked, no other threads waiting
14+
/// 2: locked, and other threads waiting (contended)
15+
futex: AtomicI32,
16+
}
17+
18+
impl Mutex {
19+
pub const fn new() -> Self {
20+
Self { futex: AtomicI32::new(0) }
21+
}
22+
23+
#[inline]
24+
pub unsafe fn init(&mut self) {}
25+
26+
#[inline]
27+
pub unsafe fn destroy(&self) {}
28+
29+
#[inline]
30+
pub unsafe fn try_lock(&self) -> bool {
31+
self.futex.compare_exchange(0, 1, Acquire, Relaxed).is_ok()
32+
}
33+
34+
#[inline]
35+
pub unsafe fn lock(&self) {
36+
if self.futex.compare_exchange(0, 1, Acquire, Relaxed).is_err() {
37+
self.lock_contended();
38+
}
39+
}
40+
41+
fn lock_contended(&self) {
42+
loop {
43+
// Put the lock in contended state, if it wasn't already.
44+
if self.futex.swap(2, Acquire) == 0 {
45+
// It was unlocked, so we just locked it.
46+
return;
47+
}
48+
// Wait for the futex to change state.
49+
futex_wait(&self.futex, 2, None);
50+
}
51+
}
52+
53+
#[inline]
54+
pub unsafe fn unlock(&self) {
55+
if self.futex.swap(0, Release) == 2 {
56+
// We only wake up one thread. When that thread locks the mutex, it
57+
// will mark the mutex as contended (2) (see lock_contended above),
58+
// which makes sure that any other waiting threads will also be
59+
// woken up eventually.
60+
futex_wake(&self.futex);
61+
}
62+
}
63+
}
64+
65+
pub struct Condvar {
66+
// The value of this atomic is simply incremented on every notification.
67+
// This is used by `.wait()` to not miss any notifications after
68+
// unlocking the mutex and before waiting for notifications.
69+
futex: AtomicI32,
70+
}
71+
72+
impl Condvar {
73+
#[inline]
74+
pub const fn new() -> Self {
75+
Self { futex: AtomicI32::new(0) }
76+
}
77+
78+
#[inline]
79+
pub unsafe fn init(&mut self) {}
80+
81+
#[inline]
82+
pub unsafe fn destroy(&self) {}
83+
84+
// All the memory orderings here are `Relaxed`,
85+
// because synchronization is done by unlocking and locking the mutex.
86+
87+
#[inline]
88+
pub unsafe fn notify_one(&self) {
89+
self.futex.fetch_add(1, Relaxed);
90+
futex_wake(&self.futex);
91+
}
92+
93+
#[inline]
94+
pub unsafe fn notify_all(&self) {
95+
self.futex.fetch_add(1, Relaxed);
96+
futex_wake_all(&self.futex);
97+
}
98+
99+
#[inline]
100+
pub unsafe fn wait(&self, mutex: &Mutex) {
101+
self.wait_optional_timeout(mutex, None);
102+
}
103+
104+
#[inline]
105+
pub unsafe fn wait_timeout(&self, mutex: &Mutex, timeout: Duration) -> bool {
106+
self.wait_optional_timeout(mutex, Some(timeout))
107+
}
108+
109+
unsafe fn wait_optional_timeout(&self, mutex: &Mutex, timeout: Option<Duration>) -> bool {
110+
// Check the notification counter before we unlock the mutex.
111+
let futex_value = self.futex.load(Relaxed);
112+
113+
// Unlock the mutex before going to sleep.
114+
mutex.unlock();
115+
116+
// Wait, but only if there hasn't been any
117+
// notification since we unlocked the mutex.
118+
let r = futex_wait(&self.futex, futex_value, timeout);
119+
120+
// Lock the mutex again.
121+
mutex.lock();
122+
123+
r
124+
}
125+
}

library/std/src/sys/unix/locks/mod.rs

+24-8
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,24 @@
1-
mod pthread_condvar;
2-
mod pthread_mutex;
3-
mod pthread_remutex;
4-
mod pthread_rwlock;
5-
pub use pthread_condvar::{Condvar, MovableCondvar};
6-
pub use pthread_mutex::{MovableMutex, Mutex};
7-
pub use pthread_remutex::ReentrantMutex;
8-
pub use pthread_rwlock::{MovableRWLock, RWLock};
1+
cfg_if::cfg_if! {
2+
if #[cfg(any(
3+
target_os = "linux",
4+
target_os = "android",
5+
))] {
6+
mod futex;
7+
#[allow(dead_code)]
8+
mod pthread_mutex; // Only used for PthreadMutexAttr, needed by pthread_remutex.
9+
mod pthread_remutex; // FIXME: Implement this using a futex
10+
mod pthread_rwlock; // FIXME: Implement this using a futex
11+
pub use futex::{Mutex, MovableMutex, Condvar, MovableCondvar};
12+
pub use pthread_remutex::ReentrantMutex;
13+
pub use pthread_rwlock::{RWLock, MovableRWLock};
14+
} else {
15+
mod pthread_mutex;
16+
mod pthread_remutex;
17+
mod pthread_rwlock;
18+
mod pthread_condvar;
19+
pub use pthread_mutex::{Mutex, MovableMutex};
20+
pub use pthread_remutex::ReentrantMutex;
21+
pub use pthread_rwlock::{RWLock, MovableRWLock};
22+
pub use pthread_condvar::{Condvar, MovableCondvar};
23+
}
24+
}

0 commit comments

Comments
 (0)