From da4ef044c1d0e8e58f2ab18459208469110c04be Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Mon, 21 Mar 2022 14:53:00 +0100 Subject: [PATCH] Spin before blocking in Mutex::lock. --- library/std/src/sys/unix/locks/futex.rs | 42 ++++++++++++++++++++++--- 1 file changed, 38 insertions(+), 4 deletions(-) diff --git a/library/std/src/sys/unix/locks/futex.rs b/library/std/src/sys/unix/locks/futex.rs index 48d800341a378..ba37d58cc31b7 100644 --- a/library/std/src/sys/unix/locks/futex.rs +++ b/library/std/src/sys/unix/locks/futex.rs @@ -39,14 +39,48 @@ impl Mutex { } fn lock_contended(&self) { + // 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, if it wasn't already. - if self.futex.swap(2, Acquire) == 0 { - // It was unlocked, so we just locked it. + // 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. + + // Wait for the futex to change state, assuming it is still 2. futex_wait(&self.futex, 2, None); + + // Spin again after waking up. + state = self.spin(); + } + } + + fn spin(&self) -> i32 { + 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; } }