Skip to content

Commit 04e8c16

Browse files
committed
Implement WakerListLock
WakerListLock is an optimised version of Spinlock<WakerList> which is more efficient in performance and space.
1 parent e132fc8 commit 04e8c16

File tree

2 files changed

+74
-6
lines changed

2 files changed

+74
-6
lines changed

src/sync/mutex.rs

+6-6
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use std::sync::atomic::{AtomicUsize, Ordering};
77
use crate::future::Future;
88
use crate::task::{Context, Poll};
99

10-
use super::waker_list::WakerList;
10+
use super::waker_list::{WakerList, WakerListLock};
1111
use std::num::NonZeroUsize;
1212

1313
/// Set if the mutex is locked.
@@ -18,7 +18,7 @@ const BLOCKED: usize = 1 << 1;
1818

1919
struct RawMutex {
2020
state: AtomicUsize,
21-
blocked: std::sync::Mutex<WakerList>,
21+
blocked: WakerListLock,
2222
}
2323

2424
unsafe impl Send for RawMutex {}
@@ -30,7 +30,7 @@ impl RawMutex {
3030
pub fn new() -> RawMutex {
3131
RawMutex {
3232
state: AtomicUsize::new(0),
33-
blocked: std::sync::Mutex::new(WakerList::new()),
33+
blocked: WakerListLock::new(WakerList::new()),
3434
}
3535
}
3636

@@ -54,7 +54,7 @@ impl RawMutex {
5454

5555
#[cold]
5656
fn unlock_slow(&self) {
57-
let mut blocked = self.blocked.lock().unwrap();
57+
let mut blocked = self.blocked.lock();
5858
blocked.wake_one_weak();
5959
}
6060

@@ -84,7 +84,7 @@ impl<'a> Future for RawLockFuture<'a> {
8484
self.acquired = true;
8585
Poll::Ready(())
8686
} else {
87-
let mut blocked = self.mutex.blocked.lock().unwrap();
87+
let mut blocked = self.mutex.blocked.lock();
8888

8989
// Register the current task.
9090
match self.opt_key {
@@ -124,7 +124,7 @@ impl<'a> Future for RawLockFuture<'a> {
124124
impl Drop for RawLockFuture<'_> {
125125
fn drop(&mut self) {
126126
if let Some(key) = self.opt_key {
127-
let mut blocked = self.mutex.blocked.lock().unwrap();
127+
let mut blocked = self.mutex.blocked.lock();
128128
let opt_waker = unsafe { blocked.remove(key) };
129129

130130
if opt_waker.is_none() && !self.acquired {

src/sync/waker_list.rs

+68
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
use crate::task::Waker;
22

3+
use crossbeam_utils::Backoff;
34
use std::num::NonZeroUsize;
5+
use std::ops::{Deref, DerefMut};
6+
use std::sync::atomic::{AtomicUsize, Ordering};
47

58
struct WakerNode {
69
/// Previous `WakerNode` in the queue. If this node is the first node, it shall point to the last node.
@@ -19,6 +22,7 @@ unsafe impl Sync for WakerList {}
1922

2023
impl WakerList {
2124
/// Create a new empty `WakerList`
25+
#[inline]
2226
pub fn new() -> Self {
2327
Self {
2428
head: std::ptr::null_mut(),
@@ -128,3 +132,67 @@ impl<'a> Iterator for Iter<'a> {
128132
Some(unsafe { &mut (*ptr).waker })
129133
}
130134
}
135+
136+
/// This is identical to a Spinlock<WakerList>, but is efficient in space and occupies the same
137+
/// amount of memory as a pointer. Performance-wise it can be better than a spinlock because once
138+
/// the lock is acquired, modification to the WakerList is on the stack, and thus is not on the
139+
/// same cache line as the atomic variable itself, relieving some contention.
140+
pub struct WakerListLock {
141+
/// If this value is 1, it represents that it is locked.
142+
/// All other values represent a valid `*mut WakerNode`.
143+
value: AtomicUsize,
144+
}
145+
146+
impl WakerListLock {
147+
/// Returns a new pointer lock initialized with `value`.
148+
#[inline]
149+
pub fn new(value: WakerList) -> Self {
150+
Self {
151+
value: AtomicUsize::new(value.head as usize),
152+
}
153+
}
154+
155+
/// Locks the `WakerListLock`.
156+
pub fn lock(&self) -> WakerListLockGuard<'_> {
157+
let backoff = Backoff::new();
158+
loop {
159+
let value = self.value.swap(1, Ordering::Acquire);
160+
if value != 1 {
161+
return WakerListLockGuard {
162+
parent: self,
163+
list: WakerList {
164+
head: value as *mut WakerNode,
165+
},
166+
};
167+
}
168+
backoff.snooze();
169+
}
170+
}
171+
}
172+
173+
pub struct WakerListLockGuard<'a> {
174+
parent: &'a WakerListLock,
175+
list: WakerList,
176+
}
177+
178+
impl<'a> Drop for WakerListLockGuard<'a> {
179+
fn drop(&mut self) {
180+
self.parent
181+
.value
182+
.store(self.list.head as usize, Ordering::Release);
183+
}
184+
}
185+
186+
impl<'a> Deref for WakerListLockGuard<'a> {
187+
type Target = WakerList;
188+
189+
fn deref(&self) -> &WakerList {
190+
&self.list
191+
}
192+
}
193+
194+
impl<'a> DerefMut for WakerListLockGuard<'a> {
195+
fn deref_mut(&mut self) -> &mut WakerList {
196+
&mut self.list
197+
}
198+
}

0 commit comments

Comments
 (0)