@@ -39,14 +39,48 @@ impl Mutex {
39
39
}
40
40
41
41
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
+
42
54
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.
46
60
return ;
47
61
}
48
- // Wait for the futex to change state.
62
+
63
+ // Wait for the futex to change state, assuming it is still 2.
49
64
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 ;
50
84
}
51
85
}
52
86
0 commit comments