@@ -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