parking_lot_core/
word_lock.rs1use crate::spinwait::SpinWait;
2use crate::thread_parker::{ThreadParker, ThreadParkerT, UnparkHandleT};
3use core::{
4 cell::Cell,
5 mem, ptr,
6 sync::atomic::{AtomicPtr, Ordering},
7};
8
9struct ThreadData {
10 parker: ThreadParker,
11
12 queue_tail: Cell<*const ThreadData>,
25 prev: Cell<*const ThreadData>,
26 next: Cell<*const ThreadData>,
27}
28
29impl ThreadData {
30 #[inline]
31 fn new() -> ThreadData {
32 if !(mem::align_of::<ThreadData>() > !QUEUE_MASK) {
::core::panicking::panic("assertion failed: mem::align_of::<ThreadData>() > !QUEUE_MASK")
};assert!(mem::align_of::<ThreadData>() > !QUEUE_MASK);
33 ThreadData {
34 parker: ThreadParker::new(),
35 queue_tail: Cell::new(ptr::null()),
36 prev: Cell::new(ptr::null()),
37 next: Cell::new(ptr::null()),
38 }
39 }
40}
41
42#[inline]
44fn with_thread_data<T>(f: impl FnOnce(&ThreadData) -> T) -> T {
45 let mut thread_data_ptr = ptr::null();
46 if !ThreadParker::IS_CHEAP_TO_CONSTRUCT {
49 const THREAD_DATA: ::std::thread::LocalKey<ThreadData> =
{
#[allow(mismatched_lifetime_syntaxes)]
#[inline]
fn __rust_std_internal_init_fn(_lifetime_elision:
::std::marker::PhantomData<&'static ()>) -> ThreadData {
ThreadData::new()
}
unsafe {
::std::thread::LocalKey::new(const {
if ::std::mem::needs_drop::<ThreadData>() {
|__rust_std_internal_init|
{
#[thread_local]
static __RUST_STD_INTERNAL_VAL:
::std::thread::local_impl::LazyStorage<ThreadData, ()> =
::std::thread::local_impl::LazyStorage::new();
__RUST_STD_INTERNAL_VAL.get_or_init(__rust_std_internal_init,
|| __rust_std_internal_init_fn(::std::marker::PhantomData))
}
} else {
|__rust_std_internal_init|
{
#[thread_local]
static __RUST_STD_INTERNAL_VAL:
::std::thread::local_impl::LazyStorage<ThreadData, !> =
::std::thread::local_impl::LazyStorage::new();
__RUST_STD_INTERNAL_VAL.get_or_init(__rust_std_internal_init,
|| __rust_std_internal_init_fn(::std::marker::PhantomData))
}
}
})
}
};thread_local!(static THREAD_DATA: ThreadData = ThreadData::new());
50 if let Ok(tls_thread_data) = THREAD_DATA.try_with(|x| x as *const ThreadData) {
51 thread_data_ptr = tls_thread_data;
52 }
53 }
54 let mut thread_data_storage = None;
56 if thread_data_ptr.is_null() {
57 thread_data_ptr = thread_data_storage.get_or_insert_with(ThreadData::new);
58 }
59
60 f(unsafe { thread_data_ptr.as_ref_unchecked() })
61}
62
63const LOCKED_BIT: usize = 1;
64const QUEUE_LOCKED_BIT: usize = 2;
65const QUEUE_MASK: usize = !3;
66
67pub struct WordLock {
70 state: AtomicPtr<ThreadData>,
71}
72
73impl WordLock {
74 pub const fn new() -> Self {
76 WordLock {
77 state: AtomicPtr::new(ptr::null_mut()),
78 }
79 }
80
81 #[inline]
82 pub fn lock(&self) {
83 if self
84 .state
85 .compare_exchange_weak(
86 ptr::null_mut(),
87 ptr::without_provenance_mut(LOCKED_BIT),
88 Ordering::Acquire,
89 Ordering::Relaxed,
90 )
91 .is_ok()
92 {
93 return;
94 }
95 self.lock_slow();
96 }
97
98 #[inline]
100 pub unsafe fn unlock(&self) {
101 let state = self.state.fetch_byte_sub(LOCKED_BIT, Ordering::Release);
102 if state.is_queue_locked() || state.queue_head().is_null() {
103 return;
104 }
105 self.unlock_slow();
106 }
107
108 #[cold]
109 fn lock_slow(&self) {
110 let mut spinwait = SpinWait::new();
111 let mut state = self.state.load(Ordering::Relaxed);
112 loop {
113 if !state.is_locked() {
115 match self.state.compare_exchange_weak(
116 state,
117 state.map_addr(|a| a | LOCKED_BIT),
118 Ordering::Acquire,
119 Ordering::Relaxed,
120 ) {
121 Ok(_) => return,
122 Err(x) => state = x,
123 }
124 continue;
125 }
126
127 if state.queue_head().is_null() && spinwait.spin() {
129 state = self.state.load(Ordering::Relaxed);
130 continue;
131 }
132
133 state = with_thread_data(|thread_data| {
135 #[allow(unused_unsafe)]
138 unsafe {
139 thread_data.parker.prepare_park();
140 }
141
142 let queue_head = state.queue_head();
144 if queue_head.is_null() {
145 thread_data.queue_tail.set(thread_data);
146 thread_data.prev.set(ptr::null());
147 } else {
148 thread_data.queue_tail.set(ptr::null());
149 thread_data.prev.set(ptr::null());
150 thread_data.next.set(queue_head);
151 }
152 if let Err(x) = self.state.compare_exchange_weak(
153 state,
154 state.with_queue_head(thread_data).cast_mut(),
155 Ordering::AcqRel,
156 Ordering::Relaxed,
157 ) {
158 return x;
159 }
160
161 #[allow(unused_unsafe)]
164 unsafe {
165 thread_data.parker.park();
166 }
167
168 spinwait.reset();
170 self.state.load(Ordering::Relaxed)
171 });
172 }
173 }
174
175 #[cold]
176 fn unlock_slow(&self) {
177 let mut state = self.state.load(Ordering::Relaxed);
178 loop {
179 if state.is_queue_locked() || state.queue_head().is_null() {
183 return;
184 }
185
186 match self.state.compare_exchange_weak(
188 state,
189 state.map_addr(|a| a | QUEUE_LOCKED_BIT),
190 Ordering::Acquire,
191 Ordering::Relaxed,
192 ) {
193 Ok(previous) => {
194 state = previous.map_addr(|a| a | QUEUE_LOCKED_BIT);
195 break;
196 }
197 Err(x) => state = x,
198 }
199 }
200
201 'outer: loop {
203 let queue_head = state.queue_head();
207 let mut queue_tail;
208 let mut current = queue_head;
209 loop {
210 queue_tail = unsafe { (*current).queue_tail.get() };
211 if !queue_tail.is_null() {
212 break;
213 }
214 unsafe {
215 let next = (*current).next.get();
216 (*next).prev.set(current);
217 current = next;
218 }
219 }
220
221 unsafe {
224 (*queue_head).queue_tail.set(queue_tail);
225 }
226
227 let new_tail = unsafe { (*queue_tail).prev.get() };
229 loop {
230 if state.is_locked() {
234 match self.state.compare_exchange_weak(
235 state,
236 state.map_addr(|a| a & !QUEUE_LOCKED_BIT),
237 Ordering::Release,
238 Ordering::Acquire,
239 ) {
240 Ok(_) => return,
241 Err(x) => state = x,
242 }
243 continue;
244 }
245
246 if state.queue_head() != queue_head {
249 continue 'outer;
250 }
251
252 if new_tail.is_null() {
257 match self.state.compare_exchange_weak(
258 state,
259 state.map_addr(|a| a & LOCKED_BIT),
260 Ordering::Release,
261 Ordering::Acquire,
262 ) {
263 Ok(_) => break,
264 Err(x) => state = x,
265 }
266 } else {
267 unsafe {
268 (*queue_head).queue_tail.set(new_tail);
269 }
270 self.state.fetch_and(!QUEUE_LOCKED_BIT, Ordering::Release);
271 break;
272 }
273 }
274
275 unsafe {
279 (*queue_tail).parker.unpark_lock().unpark();
280 }
281 break;
282 }
283 }
284}
285
286#[allow(clippy::wrong_self_convention)]
287trait LockState {
288 fn is_locked(self) -> bool;
289 fn is_queue_locked(self) -> bool;
290 fn queue_head(self) -> *const ThreadData;
291 fn with_queue_head(self, thread_data: *const ThreadData) -> Self;
292}
293
294impl LockState for *const ThreadData {
295 #[inline]
296 fn is_locked(self) -> bool {
297 self.addr() & LOCKED_BIT != 0
298 }
299
300 #[inline]
301 fn is_queue_locked(self) -> bool {
302 self.addr() & QUEUE_LOCKED_BIT != 0
303 }
304
305 #[inline]
306 fn queue_head(self) -> *const ThreadData {
307 self.map_addr(|a| a & QUEUE_MASK)
308 }
309
310 #[inline]
311 fn with_queue_head(self, thread_data: *const ThreadData) -> Self {
312 thread_data.with_addr((self.addr() & !QUEUE_MASK) | thread_data.addr())
314 }
315}