Skip to content

Commit da4ef04

Browse files
committed
Spin before blocking in Mutex::lock.
1 parent 10b6f33 commit da4ef04

File tree

1 file changed

+38
-4
lines changed

1 file changed

+38
-4
lines changed

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

+38-4
Original file line numberDiff line numberDiff line change
@@ -39,14 +39,48 @@ impl Mutex {
3939
}
4040

4141
fn lock_contended(&self) {
42+
// Spin first to speed things up if the lock is released quickly.
43+
let mut state = self.spin();
44+
45+
// If it's unlocked now, attempt to take the lock
46+
// without marking it as contended.
47+
if state == 0 {
48+
match self.futex.compare_exchange(0, 1, Acquire, Relaxed) {
49+
Ok(_) => return, // Locked!
50+
Err(s) => state = s,
51+
}
52+
}
53+
4254
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.
55+
// Put the lock in contended state.
56+
// We avoid an unnecessary write if it as already set to 2,
57+
// to be friendlier for the caches.
58+
if state != 2 && self.futex.swap(2, Acquire) == 0 {
59+
// We changed it from 0 to 2, so we just succesfully locked it.
4660
return;
4761
}
48-
// Wait for the futex to change state.
62+
63+
// Wait for the futex to change state, assuming it is still 2.
4964
futex_wait(&self.futex, 2, None);
65+
66+
// Spin again after waking up.
67+
state = self.spin();
68+
}
69+
}
70+
71+
fn spin(&self) -> i32 {
72+
let mut spin = 100;
73+
loop {
74+
// We only use `load` (and not `swap` or `compare_exchange`)
75+
// while spinning, to be easier on the caches.
76+
let state = self.futex.load(Relaxed);
77+
78+
if state == 0 || spin == 0 {
79+
return state;
80+
}
81+
82+
crate::hint::spin_loop();
83+
spin -= 1;
5084
}
5185
}
5286

0 commit comments

Comments
 (0)