1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
use crate::sync::atomic::{
AtomicU32,
Ordering::{Acquire, Relaxed, Release},
};
use crate::sys::futex::{futex_wait, futex_wake};
pub struct Mutex {
/// 0: unlocked
/// 1: 锁定,没有其他线程在等待
/// 2: 锁定,其他线程等待 (contended)
futex: AtomicU32,
}
impl Mutex {
#[inline]
pub const fn new() -> Self {
Self { futex: AtomicU32::new(0) }
}
#[inline]
pub fn try_lock(&self) -> bool {
self.futex.compare_exchange(0, 1, Acquire, Relaxed).is_ok()
}
#[inline]
pub fn lock(&self) {
if self.futex.compare_exchange(0, 1, Acquire, Relaxed).is_err() {
self.lock_contended();
}
}
#[cold]
fn lock_contended(&self) {
// 如果锁被快速释放,请先自旋以加快速度。
let mut state = self.spin();
// 如果它现在已解锁,请尝试获取锁而不将其标记为已竞争。
//
if state == 0 {
match self.futex.compare_exchange(0, 1, Acquire, Relaxed) {
Ok(_) => return, // Locked!
Err(s) => state = s,
}
}
loop {
// 将锁置于竞争状态。
// 如果它已经设置为 2,我们会避免不必要的写入,以便对缓存更友好。
//
if state != 2 && self.futex.swap(2, Acquire) == 0 {
// 我们将它从 0 更改为 2,所以我们刚刚成功锁定它。
return;
}
// 等待 futex 改变状态,假设它仍然是 2.
futex_wait(&self.futex, 2, None);
// 醒来后再次自旋。
state = self.spin();
}
}
fn spin(&self) -> u32 {
let mut spin = 100;
loop {
// 我们只在自旋时使用 `load` (而不是 `swap` 或 `compare_exchange`),以便更轻松地处理缓存。
//
let state = self.futex.load(Relaxed);
// 当互斥锁解锁 (0) 时,我们停止自旋,当它与 (2) 竞争时也是如此。
//
if state != 1 || spin == 0 {
return state;
}
crate::hint::spin_loop();
spin -= 1;
}
}
#[inline]
pub unsafe fn unlock(&self) {
if self.futex.swap(0, Release) == 2 {
// 我们只唤醒一个线程。
// 当该线程锁定上面的互斥锁时,它会将互斥锁标记为竞争 (2) (参见 lock_contended),这确保任何其他等待的线程最终也会被唤醒。
//
//
self.wake();
}
}
#[cold]
fn wake(&self) {
futex_wake(&self.futex);
}
}