1use crate::thread_parker::{ThreadParker, ThreadParkerT, UnparkHandleT};
2use crate::word_lock::WordLock;
3use core::{
4 cell::{Cell, UnsafeCell},
5mem,
6 ptr::{self, NonNull},
7 sync::atomic::{AtomicPtr, AtomicUsize, Ordering},
8};
9use smallvec::SmallVec;
10use std::time::Instant;
1112static NUM_THREADS: AtomicUsize = AtomicUsize::new(0);
1314/// Aborts the process if it is dropped while unwinding.
15///
16/// Parking-lot operations manipulate shared intrusive state without unwind
17/// guards. Once an operation has started, unwinding could leave bucket locks
18/// held, queue links inconsistent, or a transferred `ThreadData` inaccessible.
19struct AbortOnPanic;
2021impl AbortOnPanic {
22/// Prevents the guard from being dropped after a successful operation.
23#[inline]
24fn disarm(self) {
25 mem::forget(self);
26 }
27}
2829impl Dropfor AbortOnPanic {
30fn drop(&mut self) {
31{
::core::panicking::panic_fmt(format_args!("aborting due to panic in parking lot"));
};panic!("aborting due to panic in parking lot");
32 }
33}
3435/// Holds the pointer to the currently active `HashTable`.
36///
37/// # Safety
38///
39/// Except for the initial value of null, it must always point to a valid `HashTable` instance.
40/// Any `HashTable` this global static has ever pointed to must never be freed.
41static HASHTABLE: AtomicPtr<HashTable> = AtomicPtr::new(ptr::null_mut());
4243// Even with 3x more buckets than threads, the memory overhead per thread is
44// still only a few hundred bytes per thread.
45const LOAD_FACTOR: usize = 3;
4647struct HashTable {
48// Hash buckets for the table
49entries: Box<[Bucket]>,
5051// Number of bits used for the hash function
52hash_bits: u32,
5354// Previous table. This is only kept to keep leak detectors happy.
55_prev: Option<NonNull<HashTable>>,
56}
5758impl HashTable {
59#[inline]
60fn new(num_threads: usize, prev: Option<NonNull<HashTable>>) -> Box<HashTable> {
61let new_size = (num_threads * LOAD_FACTOR).next_power_of_two();
62let hash_bits = 0usize.leading_zeros() - new_size.leading_zeros() - 1;
6364let mut entries = Vec::with_capacity(new_size);
65for _ in 0..new_size {
66 entries.push(Bucket::new());
67 }
6869Box::new(HashTable {
70 entries: entries.into_boxed_slice(),
71hash_bits,
72 _prev: prev,
73 })
74 }
75}
7677#[repr(align(64))]
78struct Bucket {
79// Lock protecting the queue
80mutex: WordLock,
8182// Linked list of threads waiting on this bucket
83queue_head: Cell<Option<NonNull<ThreadData>>>,
84 queue_tail: Cell<Option<NonNull<ThreadData>>>,
85}
8687impl Bucket {
88#[inline]
89pub fn new() -> Self {
90Self {
91 mutex: WordLock::new(),
92 queue_head: Cell::new(None),
93 queue_tail: Cell::new(None),
94 }
95 }
96}
9798// A ThreadData has two stable ownership states:
99//
100// * While it is not linked into a bucket queue, it is owned by its thread and
101// its non-atomic fields are unshared.
102// * While it is linked into a bucket queue, it is owned by the parking lot. Its
103// queue fields and lifetime are protected by the lock of the bucket that
104// currently contains it.
105//
106// To transfer ownership to the parking lot, the local thread initializes the
107// queue fields and prepares the parker while holding the destination bucket
108// lock, then links the ThreadData into the queue. Once linked, the local thread
109// must not access fields owned by the parking lot.
110//
111// To transfer ownership back, an unparker removes the ThreadData from the queue,
112// writes the result fields, and calls `unpark_lock` while still holding the
113// bucket lock. It must not access the ThreadData after `unpark_lock`, since the
114// parked thread may immediately resume and destroy it. The returned
115// UnparkHandle remains usable without access to the ThreadData. A successful
116// return from `park` or `park_until` completes the transfer back to the local
117// thread and synchronizes with the unparker's preceding writes.
118//
119// A timeout does not by itself transfer ownership back. The local thread must
120// use the atomic key to find and lock the current bucket, resolve any concurrent
121// unpark or requeue, and remove itself from the queue before it again owns the
122// ThreadData.
123struct ThreadData {
124// Accessed concurrently through the ThreadParker protocol rather than the
125 // bucket lock.
126parker: ThreadParker,
127128// Written by the parking lot under the bucket lock. This is atomic because
129 // a timing-out local thread must read it before regaining ownership, and a
130 // concurrent requeue may change it.
131key: AtomicUsize,
132133// Fields transferred between local ownership and parking-lot ownership as
134 // described above.
135state: UnsafeCell<ThreadDataState>,
136}
137138struct ThreadDataState {
139// Linked list of parked threads. Accessed only while holding the lock of
140 // the bucket that contains this ThreadData.
141next_in_queue: Option<NonNull<ThreadData>>,
142143// Written by the parking lot before `unpark_lock` and read locally only
144 // after a successful return from the parker.
145unpark_token: UnparkToken,
146147// Initialized before linking and subsequently read only under the bucket
148 // lock.
149park_token: ParkToken,
150151// Deadlock detector fields governed by the same ownership rules.
152#[cfg(feature = "deadlock_detection")]
153deadlock_data: deadlock::DeadlockData,
154}
155156impl ThreadData {
157fn new() -> ThreadData {
158// Keep track of the total number of live ThreadData objects and resize
159 // the hash table accordingly.
160let num_threads = NUM_THREADS.fetch_add(1, Ordering::Relaxed) + 1;
161grow_hashtable(num_threads);
162163ThreadData {
164 parker: ThreadParker::new(),
165 key: AtomicUsize::new(0),
166 state: UnsafeCell::new(ThreadDataState {
167 next_in_queue: None,
168 unpark_token: DEFAULT_UNPARK_TOKEN,
169 park_token: DEFAULT_PARK_TOKEN,
170#[cfg(feature = "deadlock_detection")]
171deadlock_data: deadlock::DeadlockData::new(),
172 }),
173 }
174 }
175176/// Returns the state under the ownership model described above.
177 ///
178 /// # Safety
179 ///
180 /// The caller must have exclusive access to the state. Either the current
181 /// thread must own this `ThreadData`, or the parking lot must own it and the
182 /// caller must hold the lock that protects it.
183#[inline]
184 #[allow(clippy::mut_from_ref)]
185unsafe fn state(&self) -> &mut ThreadDataState {
186unsafe { self.state.get().as_mut_unchecked() }
187 }
188}
189190// Invokes the given closure with a reference to the current thread `ThreadData`.
191#[inline(always)]
192fn with_thread_data<T>(f: impl FnOnce(&ThreadData) -> T) -> T {
193// Unlike word_lock::ThreadData, parking_lot::ThreadData is always expensive
194 // to construct. Try to use a thread-local version if possible. Otherwise just
195 // create a ThreadData on the stack
196let mut thread_data_storage = None;
197const 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());
198let thread_data = THREAD_DATA199 .try_with(|thread_data| NonNull::from(thread_data))
200 .unwrap_or_else(|_| NonNull::from(thread_data_storage.get_or_insert_with(ThreadData::new)));
201202f(unsafe { thread_data.as_ref() })
203}
204205impl Dropfor ThreadData {
206fn drop(&mut self) {
207NUM_THREADS.fetch_sub(1, Ordering::Relaxed);
208 }
209}
210211/// Returns a reference to the latest hash table, creating one if it doesn't exist yet.
212/// The reference is valid forever. However, the `HashTable` it references might become stale
213/// at any point. Meaning it still exists, but it is not the instance in active use.
214#[inline]
215fn get_hashtable() -> &'static HashTable {
216let table = HASHTABLE.load(Ordering::Acquire);
217218// If there is no table, create one
219if table.is_null() {
220create_hashtable()
221 } else {
222// SAFETY: when not null, `HASHTABLE` always points to a `HashTable` that is never freed.
223unsafe { table.as_ref_unchecked() }
224 }
225}
226227/// Returns a reference to the latest hash table, creating one if it doesn't exist yet.
228/// The reference is valid forever. However, the `HashTable` it references might become stale
229/// at any point. Meaning it still exists, but it is not the instance in active use.
230#[cold]
231fn create_hashtable() -> &'static HashTable {
232let new_table = Box::into_raw(HashTable::new(LOAD_FACTOR, None));
233234// If this fails then it means some other thread created the hash table first.
235let table = match HASHTABLE.compare_exchange(
236 ptr::null_mut(),
237new_table,
238 Ordering::AcqRel,
239 Ordering::Acquire,
240 ) {
241Ok(_) => new_table,
242Err(old_table) => {
243// Free the table we created
244 // SAFETY: `new_table` is created from `Box::into_raw` above and only freed here.
245unsafe {
246let _ = Box::from_raw(new_table);
247 }
248old_table249 }
250 };
251// SAFETY: The `HashTable` behind `table` is never freed. It is either the table pointer we
252 // created here, or it is one loaded from `HASHTABLE`.
253unsafe { table.as_ref_unchecked() }
254}
255256// Grow the hash table so that it is big enough for the given number of threads.
257// This isn't performance-critical since it is only done when a ThreadData is
258// created, which only happens once per thread.
259fn grow_hashtable(num_threads: usize) {
260// Lock all buckets in the existing table and get a reference to it
261let old_table = loop {
262let table = get_hashtable();
263264// Check if we need to resize the existing table
265if table.entries.len() >= LOAD_FACTOR * num_threads {
266return;
267 }
268269// Lock all buckets in the old table
270for bucket in &table.entries[..] {
271 bucket.mutex.lock();
272 }
273274// Now check if our table is still the latest one. Another thread could
275 // have grown the hash table between us reading HASHTABLE and locking
276 // the buckets.
277if ptr::eq(HASHTABLE.load(Ordering::Relaxed), table) {
278break table;
279 }
280281// Unlock buckets and try again
282for bucket in &table.entries[..] {
283// SAFETY: We hold the lock here, as required
284unsafe { bucket.mutex.unlock() };
285 }
286 };
287288// Create the new table
289let mut new_table = HashTable::new(num_threads, Some(NonNull::from(old_table)));
290291// Move the entries from the old table to the new one
292for bucket in &old_table.entries[..] {
293// SAFETY: Parking-lot operations maintain correctly linked queues. A
294 // `ThreadData` remains valid for as long as it is linked in a queue.
295unsafe { rehash_bucket_into(bucket, &mut new_table) };
296 }
297298// Publish the new table. No races are possible at this point because
299 // any other thread trying to grow the hash table is blocked on the bucket
300 // locks in the old table.
301HASHTABLE.store(Box::into_raw(new_table), Ordering::Release);
302303// Unlock all buckets in the old table
304for bucket in &old_table.entries[..] {
305// SAFETY: We hold the lock here, as required
306unsafe { bucket.mutex.unlock() };
307 }
308}
309310/// Iterates through all `ThreadData` objects in the bucket and inserts them
311/// into the buckets corresponding to their keys in the given table.
312///
313/// # Safety
314///
315/// The given `bucket` must have a correctly constructed linked list under `queue_head`, containing
316/// `ThreadData` instances that must stay valid at least as long as the given `table` is in use.
317///
318/// The given `table` must only contain buckets with correctly constructed linked lists.
319unsafe fn rehash_bucket_into(bucket: &'static Bucket, table: &mut HashTable) {
320let mut current = bucket.queue_head.get();
321while let Some(current_ptr) = current {
322let current_ref = unsafe { current_ptr.as_ref() };
323let next = unsafe { current_ref.state() }.next_in_queue;
324let hash = hash(current_ref.key.load(Ordering::Relaxed), table.hash_bits);
325if let Some(tail) = table.entries[hash].queue_tail.get() {
326unsafe { tail.as_ref().state() }.next_in_queue = Some(current_ptr);
327 } else {
328 table.entries[hash].queue_head.set(Some(current_ptr));
329 }
330 table.entries[hash].queue_tail.set(Some(current_ptr));
331unsafe { current_ref.state() }.next_in_queue = None;
332 current = next;
333 }
334}
335336// Hash function for addresses
337#[cfg(target_pointer_width = "32")]
338#[inline]
339fn hash(key: usize, bits: u32) -> usize {
340 key.wrapping_mul(0x9E3779B9) >> (32 - bits)
341}
342#[cfg(target_pointer_width = "64")]
343#[inline]
344fn hash(key: usize, bits: u32) -> usize {
345key.wrapping_mul(0x9E3779B97F4A7C15) >> (64 - bits)
346}
347348/// Locks the bucket for the given key and returns a reference to it.
349/// The returned bucket must be unlocked again in order to not cause deadlocks.
350#[inline]
351fn lock_bucket(key: usize) -> &'static Bucket {
352loop {
353let hashtable = get_hashtable();
354355let hash = hash(key, hashtable.hash_bits);
356let bucket = &hashtable.entries[hash];
357358// Lock the bucket
359bucket.mutex.lock();
360361// If no other thread has rehashed the table before we grabbed the lock
362 // then we are good to go! The lock we grabbed prevents any rehashes.
363if ptr::eq(HASHTABLE.load(Ordering::Relaxed), hashtable) {
364return bucket;
365 }
366367// Unlock the bucket and try again
368 // SAFETY: We hold the lock here, as required
369unsafe { bucket.mutex.unlock() };
370 }
371}
372373/// Locks the bucket for the given key and returns a reference to it. But checks that the key
374/// hasn't been changed in the meantime due to a requeue.
375/// The returned bucket must be unlocked again in order to not cause deadlocks.
376#[inline]
377fn lock_bucket_checked(key: &AtomicUsize) -> (usize, &'static Bucket) {
378loop {
379let hashtable = get_hashtable();
380let current_key = key.load(Ordering::Relaxed);
381382let hash = hash(current_key, hashtable.hash_bits);
383let bucket = &hashtable.entries[hash];
384385// Lock the bucket
386bucket.mutex.lock();
387388// Check that both the hash table and key are correct while the bucket
389 // is locked. Note that the key can't change once we locked the proper
390 // bucket for it, so we just keep trying until we have the correct key.
391if ptr::eq(HASHTABLE.load(Ordering::Relaxed), hashtable)
392 && key.load(Ordering::Relaxed) == current_key393 {
394return (current_key, bucket);
395 }
396397// Unlock the bucket and try again
398 // SAFETY: We hold the lock here, as required
399unsafe { bucket.mutex.unlock() };
400 }
401}
402403/// Locks the two buckets for the given pair of keys and returns references to them.
404/// The returned buckets must be unlocked again in order to not cause deadlocks.
405///
406/// If both keys hash to the same value, both returned references will be to the same bucket. Be
407/// careful to only unlock it once in this case, always use `unlock_bucket_pair`.
408#[inline]
409fn lock_bucket_pair(key1: usize, key2: usize) -> (&'static Bucket, &'static Bucket) {
410loop {
411let hashtable = get_hashtable();
412413let hash1 = hash(key1, hashtable.hash_bits);
414let hash2 = hash(key2, hashtable.hash_bits);
415416// Get the bucket at the lowest hash/index first
417let bucket1 = if hash1 <= hash2 {
418&hashtable.entries[hash1]
419 } else {
420&hashtable.entries[hash2]
421 };
422423// Lock the first bucket
424bucket1.mutex.lock();
425426// If no other thread has rehashed the table before we grabbed the lock
427 // then we are good to go! The lock we grabbed prevents any rehashes.
428if ptr::eq(HASHTABLE.load(Ordering::Relaxed), hashtable) {
429// Now lock the second bucket and return the two buckets
430if hash1 == hash2 {
431return (bucket1, bucket1);
432 } else if hash1 < hash2 {
433let bucket2 = &hashtable.entries[hash2];
434bucket2.mutex.lock();
435return (bucket1, bucket2);
436 } else {
437let bucket2 = &hashtable.entries[hash1];
438bucket2.mutex.lock();
439return (bucket2, bucket1);
440 }
441 }
442443// Unlock the bucket and try again
444 // SAFETY: We hold the lock here, as required
445unsafe { bucket1.mutex.unlock() };
446 }
447}
448449/// Unlocks a pair of buckets.
450///
451/// # Safety
452///
453/// Both buckets must be locked.
454#[inline]
455unsafe fn unlock_bucket_pair(bucket1: &Bucket, bucket2: &Bucket) {
456unsafe { bucket1.mutex.unlock() };
457if !ptr::eq(bucket1, bucket2) {
458unsafe { bucket2.mutex.unlock() };
459 }
460}
461462/// Result of a park operation.
463#[derive(#[automatically_derived]impl ::core::marker::Copy for ParkResult { }Copy, #[automatically_derived]#[doc(hidden)]unsafe impl ::core::clone::TrivialClone
for ParkResult {
}
#[automatically_derived]impl ::core::clone::Clone for ParkResult {
#[inline]
fn clone(&self) -> Self {
let _: ::core::clone::AssertParamIsClone<UnparkToken>;
*self
}
}Clone, #[automatically_derived]impl ::core::cmp::Eq for ParkResult {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_fields_are_eq(&self) {
let _: ::core::cmp::AssertParamIsEq<UnparkToken>;
}
}Eq, #[automatically_derived]impl ::core::marker::StructuralPartialEq for
ParkResult {
}
#[automatically_derived]impl ::core::cmp::PartialEq for ParkResult {
#[inline]
fn eq(&self, other: &Self) -> bool {
::core::intrinsics::discriminant_value(self) ==
::core::intrinsics::discriminant_value(other) &&
match (self, other) {
(Self::Unparked(__self_0), Self::Unparked(__arg1_0)) =>
__self_0 == __arg1_0,
_ => true,
}
}
}PartialEq, #[automatically_derived]impl ::core::fmt::Debug for ParkResult {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
match self {
Self::Unparked(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f,
"Unparked", &__self_0),
Self::Invalid => ::core::fmt::Formatter::write_str(f, "Invalid"),
Self::TimedOut =>
::core::fmt::Formatter::write_str(f, "TimedOut"),
}
}
}Debug)]
464pub enum ParkResult {
465/// We were unparked by another thread with the given token.
466Unparked(UnparkToken),
467468/// The validation callback returned false.
469Invalid,
470471/// The timeout expired.
472TimedOut,
473}
474475impl ParkResult {
476/// Returns true if we were unparked by another thread.
477#[inline]
478pub const fn is_unparked(self) -> bool {
479#[allow(non_exhaustive_omitted_patterns)] match self {
ParkResult::Unparked(_) => true,
_ => false,
}matches!(self, ParkResult::Unparked(_))480 }
481}
482483/// Result of an unpark operation.
484#[derive(#[automatically_derived]impl ::core::marker::Copy for UnparkResult { }Copy, #[automatically_derived]#[doc(hidden)]unsafe impl ::core::clone::TrivialClone
for UnparkResult {
}
#[automatically_derived]impl ::core::clone::Clone for UnparkResult {
#[inline]
fn clone(&self) -> Self {
let _: ::core::clone::AssertParamIsClone<usize>;
let _: ::core::clone::AssertParamIsClone<bool>;
*self
}
}Clone, #[automatically_derived]impl ::core::default::Default for UnparkResult {
#[inline]
fn default() -> Self {
UnparkResult {
unparked_threads: ::core::default::Default::default(),
requeued_threads: ::core::default::Default::default(),
have_more_threads: ::core::default::Default::default(),
}
}
}Default, #[automatically_derived]impl ::core::cmp::Eq for UnparkResult {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_fields_are_eq(&self) {
let _: ::core::cmp::AssertParamIsEq<usize>;
let _: ::core::cmp::AssertParamIsEq<bool>;
}
}Eq, #[automatically_derived]impl ::core::marker::StructuralPartialEq for
UnparkResult {
}
#[automatically_derived]impl ::core::cmp::PartialEq for UnparkResult {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.have_more_threads == other.have_more_threads &&
self.unparked_threads == other.unparked_threads &&
self.requeued_threads == other.requeued_threads
}
}PartialEq, #[automatically_derived]impl ::core::fmt::Debug for UnparkResult {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_struct_field3_finish(f,
"UnparkResult unparked_threads requeued_threads have_more_threads",
&self.unparked_threads, &self.requeued_threads,
&&self.have_more_threads)
}
}Debug)]
485#[non_exhaustive]
486pub struct UnparkResult {
487/// The number of threads that were unparked.
488pub unparked_threads: usize,
489490/// The number of threads that were requeued.
491pub requeued_threads: usize,
492493/// Whether any threads remain parked with the original key after the
494 /// operation.
495 ///
496 /// This is `false` if the operation was aborted before the queue was
497 /// inspected.
498pub have_more_threads: bool,
499}
500501/// Operation that `unpark_requeue` should perform.
502#[derive(#[automatically_derived]impl ::core::marker::Copy for RequeueOp { }Copy, #[automatically_derived]#[doc(hidden)]unsafe impl ::core::clone::TrivialClone
for RequeueOp {
}
#[automatically_derived]impl ::core::clone::Clone for RequeueOp {
#[inline]
fn clone(&self) -> Self { *self }
}Clone, #[automatically_derived]impl ::core::cmp::Eq for RequeueOp { }Eq, #[automatically_derived]impl ::core::marker::StructuralPartialEq for RequeueOp
{
}
#[automatically_derived]impl ::core::cmp::PartialEq for RequeueOp {
#[inline]
fn eq(&self, other: &Self) -> bool {
::core::intrinsics::discriminant_value(self) ==
::core::intrinsics::discriminant_value(other)
}
}PartialEq, #[automatically_derived]impl ::core::fmt::Debug for RequeueOp {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_c_like_enum_write_str(f,
"Abort UnparkOneRequeueRest RequeueAll UnparkOne RequeueOne",
match self {
RequeueOp::Abort => 0usize,
RequeueOp::UnparkOneRequeueRest => 6usize,
RequeueOp::RequeueAll => 27usize,
RequeueOp::UnparkOne => 38usize,
RequeueOp::RequeueOne => 48usize,
})
}
}Debug)]
503pub enum RequeueOp {
504/// Abort the operation without doing anything.
505Abort,
506507/// Unpark one thread and requeue the rest onto the target queue.
508UnparkOneRequeueRest,
509510/// Requeue all threads onto the target queue.
511RequeueAll,
512513/// Unpark one thread and leave the rest parked. No requeuing is done.
514UnparkOne,
515516/// Requeue one thread and leave the rest parked on the original queue.
517RequeueOne,
518}
519520/// Operation that `unpark_filter` should perform for each thread.
521#[derive(#[automatically_derived]impl ::core::marker::Copy for FilterOp { }Copy, #[automatically_derived]#[doc(hidden)]unsafe impl ::core::clone::TrivialClone
for FilterOp {
}
#[automatically_derived]impl ::core::clone::Clone for FilterOp {
#[inline]
fn clone(&self) -> Self { *self }
}Clone, #[automatically_derived]impl ::core::cmp::Eq for FilterOp { }Eq, #[automatically_derived]impl ::core::marker::StructuralPartialEq for FilterOp
{
}
#[automatically_derived]impl ::core::cmp::PartialEq for FilterOp {
#[inline]
fn eq(&self, other: &Self) -> bool {
::core::intrinsics::discriminant_value(self) ==
::core::intrinsics::discriminant_value(other)
}
}PartialEq, #[automatically_derived]impl ::core::fmt::Debug for FilterOp {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::write_str(f,
match self {
FilterOp::Unpark => "Unpark",
FilterOp::Skip => "Skip",
FilterOp::Stop => "Stop",
})
}
}Debug)]
522pub enum FilterOp {
523/// Unpark the thread and continue scanning the list of parked threads.
524Unpark,
525526/// Don't unpark the thread and continue scanning the list of parked threads.
527Skip,
528529/// Don't unpark the thread and stop scanning the list of parked threads.
530Stop,
531}
532533/// A value which is passed from an unparker to a parked thread.
534#[derive(#[automatically_derived]impl ::core::marker::Copy for UnparkToken { }Copy, #[automatically_derived]#[doc(hidden)]unsafe impl ::core::clone::TrivialClone
for UnparkToken {
}
#[automatically_derived]impl ::core::clone::Clone for UnparkToken {
#[inline]
fn clone(&self) -> Self {
let _: ::core::clone::AssertParamIsClone<usize>;
*self
}
}Clone, #[automatically_derived]impl ::core::cmp::Eq for UnparkToken {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_fields_are_eq(&self) {
let _: ::core::cmp::AssertParamIsEq<usize>;
}
}Eq, #[automatically_derived]impl ::core::marker::StructuralPartialEq for
UnparkToken {
}
#[automatically_derived]impl ::core::cmp::PartialEq for UnparkToken {
#[inline]
fn eq(&self, other: &Self) -> bool { self.0 == other.0 }
}PartialEq, #[automatically_derived]impl ::core::fmt::Debug for UnparkToken {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_tuple_field1_finish(f, "UnparkToken",
&&self.0)
}
}Debug)]
535pub struct UnparkToken(pub usize);
536537/// A value associated with a parked thread which can be used by `unpark_filter`.
538#[derive(#[automatically_derived]impl ::core::marker::Copy for ParkToken { }Copy, #[automatically_derived]#[doc(hidden)]unsafe impl ::core::clone::TrivialClone
for ParkToken {
}
#[automatically_derived]impl ::core::clone::Clone for ParkToken {
#[inline]
fn clone(&self) -> Self {
let _: ::core::clone::AssertParamIsClone<usize>;
*self
}
}Clone, #[automatically_derived]impl ::core::cmp::Eq for ParkToken {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_fields_are_eq(&self) {
let _: ::core::cmp::AssertParamIsEq<usize>;
}
}Eq, #[automatically_derived]impl ::core::marker::StructuralPartialEq for ParkToken
{
}
#[automatically_derived]impl ::core::cmp::PartialEq for ParkToken {
#[inline]
fn eq(&self, other: &Self) -> bool { self.0 == other.0 }
}PartialEq, #[automatically_derived]impl ::core::fmt::Debug for ParkToken {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_tuple_field1_finish(f, "ParkToken",
&&self.0)
}
}Debug)]
539pub struct ParkToken(pub usize);
540541/// A default unpark token to use.
542pub const DEFAULT_UNPARK_TOKEN: UnparkToken = UnparkToken(0);
543544/// A default park token to use.
545pub const DEFAULT_PARK_TOKEN: ParkToken = ParkToken(0);
546547/// Parks the current thread in the queue associated with the given key.
548///
549/// The `validate` function is called while the queue is locked and can abort
550/// the operation by returning false. If `validate` returns true then the
551/// current thread is appended to the queue and the queue is unlocked.
552///
553/// The `before_sleep` function is called after the queue is unlocked but before
554/// the thread is put to sleep. The thread will then sleep until it is unparked
555/// or the given timeout is reached.
556///
557/// The `timed_out` function is also called while the queue is locked, but only
558/// if the timeout was reached. It is passed the key of the queue it was in when
559/// it timed out, which may be different from the original key if
560/// `unpark_requeue` was called. It is also passed a bool which indicates
561/// whether it was the last thread in the queue.
562///
563/// A timeout is the earliest point at which this function may return
564/// [`ParkResult::TimedOut`]. Scheduling and platform-specific behavior may
565/// delay the actual return.
566///
567/// # Safety
568///
569/// You should only call this function with an address that you control, since
570/// you could otherwise interfere with the operation of other synchronization
571/// primitives.
572///
573/// The `validate` and `timed_out` functions are called while the queue is
574/// locked and must not unwind or call into any function in `parking_lot`.
575///
576/// The `before_sleep` function is called outside the queue lock and is allowed
577/// to call `unpark_one`, `unpark_all`, `unpark_requeue` or `unpark_filter`, but
578/// it is not allowed to call `park` or unwind.
579///
580/// The parking-lot functions are not reentrant. Calling this function from an
581/// asynchronous signal handler may cause undefined behavior, including
582/// internal-state corruption or deadlock.
583#[inline]
584pub unsafe fn park(
585 key: usize,
586 validate: impl FnOnce() -> bool,
587 before_sleep: impl FnOnce(),
588 timed_out: impl FnOnce(usize, bool),
589 park_token: ParkToken,
590 timeout: Option<Instant>,
591) -> ParkResult {
592let abort_on_panic = AbortOnPanic;
593594// Grab our thread data, this also ensures that the hash table exists
595let result = with_thread_data(|thread_data| {
596// Lock the bucket for the given key
597let bucket = lock_bucket(key);
598599// If the validation function fails, just return
600if !validate() {
601// SAFETY: We hold the lock here, as required
602unsafe { bucket.mutex.unlock() };
603return ParkResult::Invalid;
604 }
605606// Append our thread data to the queue and unlock the bucket
607let thread_data_ptr = NonNull::from(thread_data);
608let state = unsafe { thread_data.state() };
609state.next_in_queue = None;
610state.park_token = park_token;
611#[cfg(feature = "deadlock_detection")]
612{
613 state.deadlock_data.parked_with_timeout = timeout.is_some();
614 }
615thread_data.key.store(key, Ordering::Relaxed);
616unsafe { thread_data.parker.prepare_park() };
617if let Some(tail) = bucket.queue_tail.get() {
618unsafe { tail.as_ref().state() }.next_in_queue = Some(thread_data_ptr);
619 } else {
620bucket.queue_head.set(Some(thread_data_ptr));
621 }
622bucket.queue_tail.set(Some(thread_data_ptr));
623// SAFETY: We hold the lock here, as required
624unsafe { bucket.mutex.unlock() };
625626// Invoke the pre-sleep callback
627before_sleep();
628629// Park our thread and determine whether we were woken up by an unpark
630 // or by our timeout. Note that this isn't precise: we can still be
631 // unparked since we are still in the queue.
632let unparked = match timeout {
633Some(timeout) => unsafe { thread_data.parker.park_until(timeout) },
634None => {
635unsafe { thread_data.parker.park() };
636// Handle a pending deadlock-detector backtrace request.
637unsafe { deadlock::on_unpark(thread_data) };
638true
639}
640 };
641642// If we were unparked, return now
643if unparked {
644return ParkResult::Unparked(unsafe { thread_data.state() }.unpark_token);
645 }
646647// Lock our bucket again. Note that the hashtable may have been rehashed in
648 // the meantime. Our key may also have changed if we were requeued.
649let (key, bucket) = lock_bucket_checked(&thread_data.key);
650651// Now we need to check again if we were unparked or timed out. Unlike the
652 // last check this is precise because we hold the bucket lock.
653if !unsafe { thread_data.parker.timed_out() } {
654// SAFETY: We hold the lock here, as required
655unsafe { bucket.mutex.unlock() };
656return ParkResult::Unparked(unsafe { thread_data.state() }.unpark_token);
657 }
658659// We timed out, so we now need to remove our thread from the queue
660let mut current = bucket.queue_head.get();
661let mut previous: Option<NonNull<ThreadData>> = None;
662let mut was_last_thread = true;
663while let Some(current_ptr) = current {
664let current_ref = unsafe { current_ptr.as_ref() };
665let next = unsafe { current_ref.state() }.next_in_queue;
666if current_ptr == thread_data_ptr {
667if let Some(previous) = previous {
668unsafe { previous.as_ref().state() }.next_in_queue = next;
669 } else {
670 bucket.queue_head.set(next);
671 }
672if bucket.queue_tail.get() == Some(current_ptr) {
673 bucket.queue_tail.set(previous);
674 } else {
675// Scan the rest of the queue to see if there are any other
676 // entries with the given key.
677let mut scan = next;
678while let Some(scan_ptr) = scan {
679let scan_ref = unsafe { scan_ptr.as_ref() };
680if scan_ref.key.load(Ordering::Relaxed) == key {
681 was_last_thread = false;
682break;
683 }
684 scan = unsafe { scan_ref.state() }.next_in_queue;
685 }
686 }
687688// Callback to indicate that we timed out, and whether we were the
689 // last thread on the queue.
690timed_out(key, was_last_thread);
691break;
692 } else {
693if current_ref.key.load(Ordering::Relaxed) == key {
694 was_last_thread = false;
695 }
696 previous = Some(current_ptr);
697 current = next;
698 }
699 }
700701// There should be no way for our thread to have been removed from the queue
702 // if we timed out.
703if true {
if !current.is_some() {
::core::panicking::panic("assertion failed: current.is_some()")
};
};debug_assert!(current.is_some());
704705// Unlock the bucket, we are done
706 // SAFETY: We hold the lock here, as required
707unsafe { bucket.mutex.unlock() };
708 ParkResult::TimedOut709 });
710711abort_on_panic.disarm();
712result713}
714715/// Unparks one thread from the queue associated with the given key.
716///
717/// The `callback` function is called while the queue is locked and before the
718/// target thread is woken up. The `UnparkResult` argument to the function
719/// indicates whether a thread was found in the queue and whether this was the
720/// last thread in the queue. This value is also returned by `unpark_one`.
721///
722/// The `callback` function should return an `UnparkToken` value which will be
723/// passed to the thread that is unparked. If no thread is unparked then the
724/// returned value is ignored.
725///
726/// # Safety
727///
728/// You should only call this function with an address that you control, since
729/// you could otherwise interfere with the operation of other synchronization
730/// primitives.
731///
732/// The `callback` function is called while the queue is locked and must not
733/// unwind or call into any function in `parking_lot`.
734///
735/// The parking-lot functions are not reentrant. Calling this function from an
736/// asynchronous signal handler may cause undefined behavior, including
737/// internal-state corruption or deadlock.
738#[inline]
739pub unsafe fn unpark_one(
740 key: usize,
741 callback: impl FnOnce(UnparkResult) -> UnparkToken,
742) -> UnparkResult {
743let abort_on_panic = AbortOnPanic;
744745// Lock the bucket for the given key
746let bucket = lock_bucket(key);
747748// Find a thread with a matching key and remove it from the queue
749let mut current = bucket.queue_head.get();
750let mut previous: Option<NonNull<ThreadData>> = None;
751let mut result = UnparkResult::default();
752while let Some(current_ptr) = current {
753let current_ref = unsafe { current_ptr.as_ref() };
754let next = unsafe { current_ref.state() }.next_in_queue;
755if current_ref.key.load(Ordering::Relaxed) == key {
756// Remove the thread from the queue
757if let Some(previous) = previous {
758unsafe { previous.as_ref().state() }.next_in_queue = next;
759 } else {
760 bucket.queue_head.set(next);
761 }
762if bucket.queue_tail.get() == Some(current_ptr) {
763 bucket.queue_tail.set(previous);
764 } else {
765// Scan the rest of the queue to see if there are any other
766 // entries with the given key.
767let mut scan = next;
768while let Some(scan_ptr) = scan {
769let scan_ref = unsafe { scan_ptr.as_ref() };
770if scan_ref.key.load(Ordering::Relaxed) == key {
771 result.have_more_threads = true;
772break;
773 }
774 scan = unsafe { scan_ref.state() }.next_in_queue;
775 }
776 }
777778// Invoke the callback before waking up the thread
779result.unparked_threads = 1;
780let token = callback(result);
781782// Set the token for the target thread
783unsafe { current_ref.state() }.unpark_token = token;
784785// Commit the unpark before unlocking the queue. The target may now
786 // destroy its ThreadData, but the handle remains valid for the
787 // deferred wake.
788let handle = unsafe { current_ref.parker.unpark_lock() };
789// SAFETY: We hold the lock here, as required
790unsafe { bucket.mutex.unlock() };
791unsafe { handle.unpark() };
792793 abort_on_panic.disarm();
794return result;
795 } else {
796 previous = Some(current_ptr);
797 current = next;
798 }
799 }
800801// No threads with a matching key were found in the bucket
802callback(result);
803// SAFETY: We hold the lock here, as required
804unsafe { bucket.mutex.unlock() };
805806abort_on_panic.disarm();
807result808}
809810/// Unparks all threads in the queue associated with the given key.
811///
812/// The given `UnparkToken` is passed to all unparked threads.
813///
814/// This function returns the number of threads that were unparked.
815///
816/// # Safety
817///
818/// You should only call this function with an address that you control, since
819/// you could otherwise interfere with the operation of other synchronization
820/// primitives.
821///
822/// The parking-lot functions are not reentrant. Calling this function from an
823/// asynchronous signal handler may cause undefined behavior, including
824/// internal-state corruption or deadlock.
825#[inline]
826pub unsafe fn unpark_all(key: usize, unpark_token: UnparkToken) -> usize {
827let abort_on_panic = AbortOnPanic;
828829// Lock the bucket for the given key
830let bucket = lock_bucket(key);
831832// Remove all threads with the given key in the bucket
833let mut current = bucket.queue_head.get();
834let mut previous: Option<NonNull<ThreadData>> = None;
835let mut threads = SmallVec::<[_; 8]>::new();
836while let Some(current_ptr) = current {
837let current_ref = unsafe { current_ptr.as_ref() };
838let next = unsafe { current_ref.state() }.next_in_queue;
839if current_ref.key.load(Ordering::Relaxed) == key {
840// Remove the thread from the queue
841if let Some(previous) = previous {
842unsafe { previous.as_ref().state() }.next_in_queue = next;
843 } else {
844 bucket.queue_head.set(next);
845 }
846if bucket.queue_tail.get() == Some(current_ptr) {
847 bucket.queue_tail.set(previous);
848 }
849850// Set the token for the target thread
851unsafe { current_ref.state() }.unpark_token = unpark_token;
852853// Commit the unpark now and defer the wake until the queue is unlocked.
854threads.push(unsafe { current_ref.parker.unpark_lock() });
855 current = next;
856 } else {
857 previous = Some(current_ptr);
858 current = next;
859 }
860 }
861862// Unlock the bucket
863 // SAFETY: We hold the lock here, as required
864unsafe { bucket.mutex.unlock() };
865866// Now that we are outside the lock, wake up all the threads that we removed
867 // from the queue.
868let num_threads = threads.len();
869for handle in threads.into_iter() {
870unsafe { handle.unpark() };
871 }
872873abort_on_panic.disarm();
874num_threads875}
876877/// Removes all threads from the queue associated with `key_from`, optionally
878/// unparks the first one and requeues the rest onto the queue associated with
879/// `key_to`.
880///
881/// The `validate` function is called while both queues are locked. Its return
882/// value will determine which operation is performed, or whether the operation
883/// should be aborted. See `RequeueOp` for details about the different possible
884/// return values.
885///
886/// If `validate` returns [`RequeueOp::Abort`], the queues are left unchanged,
887/// `callback` is not called, and a default [`UnparkResult`] is returned.
888/// Otherwise, `callback` is called while both queues are locked. It is passed
889/// the `RequeueOp` returned by `validate` and an `UnparkResult` indicating
890/// whether a thread was unparked and whether threads remain parked with
891/// `key_from`. Threads requeued to `key_to` are not included. This
892/// `UnparkResult` value is also returned by `unpark_requeue`.
893///
894/// The `callback` function should return an `UnparkToken` value which will be
895/// passed to the thread that is unparked. If no thread is unparked then the
896/// returned value is ignored.
897///
898/// # Safety
899///
900/// You should only call this function with an address that you control, since
901/// you could otherwise interfere with the operation of other synchronization
902/// primitives.
903///
904/// The `validate` and `callback` functions are called while both queues are locked
905/// and must not unwind or call into any function in `parking_lot`.
906///
907/// The parking-lot functions are not reentrant. Calling this function from an
908/// asynchronous signal handler may cause undefined behavior, including
909/// internal-state corruption or deadlock.
910#[inline]
911pub unsafe fn unpark_requeue(
912 key_from: usize,
913 key_to: usize,
914 validate: impl FnOnce() -> RequeueOp,
915 callback: impl FnOnce(RequeueOp, UnparkResult) -> UnparkToken,
916) -> UnparkResult {
917let abort_on_panic = AbortOnPanic;
918919// Lock the two buckets for the given key
920let (bucket_from, bucket_to) = lock_bucket_pair(key_from, key_to);
921922// If the validation function fails, just return
923let mut result = UnparkResult::default();
924let op = validate();
925if op == RequeueOp::Abort {
926// SAFETY: Both buckets are locked, as required.
927unsafe { unlock_bucket_pair(bucket_from, bucket_to) };
928abort_on_panic.disarm();
929return result;
930 }
931932// Remove all threads with the given key in the source bucket
933let mut current = bucket_from.queue_head.get();
934let mut previous: Option<NonNull<ThreadData>> = None;
935let mut requeue_threads: Option<NonNull<ThreadData>> = None;
936let mut requeue_threads_tail: Option<NonNull<ThreadData>> = None;
937let mut wakeup_thread = None;
938while let Some(current_ptr) = current {
939let current_ref = unsafe { current_ptr.as_ref() };
940let next = unsafe { current_ref.state() }.next_in_queue;
941if current_ref.key.load(Ordering::Relaxed) == key_from {
942// Remove the thread from the queue
943if let Some(previous) = previous {
944unsafe { previous.as_ref().state() }.next_in_queue = next;
945 } else {
946 bucket_from.queue_head.set(next);
947 }
948if bucket_from.queue_tail.get() == Some(current_ptr) {
949 bucket_from.queue_tail.set(previous);
950 }
951952// Prepare the first thread for wakeup and requeue the rest.
953if (op == RequeueOp::UnparkOneRequeueRest || op == RequeueOp::UnparkOne)
954 && wakeup_thread.is_none()
955 {
956 wakeup_thread = Some(current_ptr);
957 result.unparked_threads = 1;
958 } else {
959if let Some(tail) = requeue_threads_tail {
960unsafe { tail.as_ref().state() }.next_in_queue = Some(current_ptr);
961 } else {
962 requeue_threads = Some(current_ptr);
963 }
964 requeue_threads_tail = Some(current_ptr);
965 current_ref.key.store(key_to, Ordering::Relaxed);
966 result.requeued_threads += 1;
967 }
968if op == RequeueOp::UnparkOne || op == RequeueOp::RequeueOne {
969// Scan the rest of the queue to see if there are any other
970 // entries with the given key.
971let mut scan = next;
972while let Some(scan_ptr) = scan {
973let scan_ref = unsafe { scan_ptr.as_ref() };
974if scan_ref.key.load(Ordering::Relaxed) == key_from {
975 result.have_more_threads = true;
976break;
977 }
978 scan = unsafe { scan_ref.state() }.next_in_queue;
979 }
980break;
981 }
982 current = next;
983 } else {
984 previous = Some(current_ptr);
985 current = next;
986 }
987 }
988989// Add the requeued threads to the destination bucket
990if let Some(requeue_threads) = requeue_threads {
991let requeue_threads_tail = requeue_threads_tail.unwrap();
992unsafe { requeue_threads_tail.as_ref().state() }.next_in_queue = None;
993if let Some(tail) = bucket_to.queue_tail.get() {
994unsafe { tail.as_ref().state() }.next_in_queue = Some(requeue_threads);
995 } else {
996bucket_to.queue_head.set(Some(requeue_threads));
997 }
998bucket_to.queue_tail.set(Some(requeue_threads_tail));
999 }
10001001// Invoke the callback before waking up the thread
1002let token = callback(op, result);
10031004// Commit the unpark before unlocking the queues, then perform the wake.
1005if let Some(wakeup_thread) = wakeup_thread {
1006let wakeup_thread = unsafe { wakeup_thread.as_ref() };
1007unsafe { wakeup_thread.state() }.unpark_token = token;
1008let handle = unsafe { wakeup_thread.parker.unpark_lock() };
1009// SAFETY: Both buckets are locked, as required.
1010unsafe { unlock_bucket_pair(bucket_from, bucket_to) };
1011unsafe { handle.unpark() };
1012 } else {
1013// SAFETY: Both buckets are locked, as required.
1014unsafe { unlock_bucket_pair(bucket_from, bucket_to) };
1015 }
10161017abort_on_panic.disarm();
1018result1019}
10201021/// Unparks a number of threads from the front of the queue associated with
1022/// `key` depending on the results of a filter function which inspects the
1023/// `ParkToken` associated with each thread.
1024///
1025/// The `filter` function is called for each thread in the queue or until
1026/// `FilterOp::Stop` is returned. This function is passed the `ParkToken`
1027/// associated with a particular thread, which is unparked if `FilterOp::Unpark`
1028/// is returned.
1029///
1030/// The `callback` function is also called while the queue is locked. It is
1031/// passed an `UnparkResult` indicating the number of threads that were unparked
1032/// and whether there are still parked threads in the queue. This `UnparkResult`
1033/// value is also returned by `unpark_filter`.
1034///
1035/// The `callback` function should return an `UnparkToken` value which will be
1036/// passed to all threads that are unparked. If no thread is unparked then the
1037/// returned value is ignored.
1038///
1039/// # Safety
1040///
1041/// You should only call this function with an address that you control, since
1042/// you could otherwise interfere with the operation of other synchronization
1043/// primitives.
1044///
1045/// The `filter` and `callback` functions are called while the queue is locked
1046/// and must not unwind or call into any function in `parking_lot`.
1047///
1048/// The parking-lot functions are not reentrant. Calling this function from an
1049/// asynchronous signal handler may cause undefined behavior, including
1050/// internal-state corruption or deadlock.
1051#[inline]
1052pub unsafe fn unpark_filter(
1053 key: usize,
1054 filter: impl FnMut(ParkToken) -> FilterOp,
1055 callback: impl FnOnce(UnparkResult) -> UnparkToken,
1056) -> UnparkResult {
1057let abort_on_panic = AbortOnPanic;
1058let mut filter = filter;
10591060// Lock the bucket for the given key
1061let bucket = lock_bucket(key);
10621063// Go through the queue looking for threads with a matching key
1064let mut current = bucket.queue_head.get();
1065let mut previous: Option<NonNull<ThreadData>> = None;
1066let mut threads = SmallVec::<[_; 8]>::new();
1067let mut result = UnparkResult::default();
1068while let Some(current_ptr) = current {
1069let current_ref = unsafe { current_ptr.as_ref() };
1070let next = unsafe { current_ref.state() }.next_in_queue;
1071if current_ref.key.load(Ordering::Relaxed) == key {
1072// Call the filter function with the thread's ParkToken
1073match filter(unsafe { current_ref.state() }.park_token) {
1074 FilterOp::Unpark => {
1075// Remove the thread from the queue
1076if let Some(previous) = previous {
1077unsafe { previous.as_ref().state() }.next_in_queue = next;
1078 } else {
1079 bucket.queue_head.set(next);
1080 }
1081if bucket.queue_tail.get() == Some(current_ptr) {
1082 bucket.queue_tail.set(previous);
1083 }
10841085// Add the thread to our list of threads to unpark
1086threads.push((current_ptr, None));
10871088 current = next;
1089 }
1090 FilterOp::Skip => {
1091 result.have_more_threads = true;
1092 previous = Some(current_ptr);
1093 current = next;
1094 }
1095 FilterOp::Stop => {
1096 result.have_more_threads = true;
1097break;
1098 }
1099 }
1100 } else {
1101 previous = Some(current_ptr);
1102 current = next;
1103 }
1104 }
11051106// Invoke the callback before waking up the threads
1107result.unparked_threads = threads.len();
1108let token = callback(result);
11091110// Pass the token to all threads that are going to be unparked and prepare
1111 // them for unparking.
1112for t in threads.iter_mut() {
1113let thread = unsafe { t.0.as_ref() };
1114unsafe { thread.state() }.unpark_token = token;
1115 t.1 = Some(unsafe { thread.parker.unpark_lock() });
1116 }
11171118// SAFETY: We hold the lock here, as required
1119unsafe { bucket.mutex.unlock() };
11201121// Now that we are outside the lock, wake up all the threads that we removed
1122 // from the queue.
1123for (_, handle) in threads.into_iter() {
1124unsafe { handle.unwrap_unchecked().unpark() };
1125 }
11261127abort_on_panic.disarm();
1128result1129}
11301131/// \[Experimental\] Deadlock detection
1132///
1133/// Enabled via the `deadlock_detection` feature flag.
1134pub mod deadlock {
1135#[cfg(feature = "deadlock_detection")]
1136use super::deadlock_impl;
11371138#[cfg(feature = "deadlock_detection")]
1139pub(super) use super::deadlock_impl::DeadlockData;
11401141/// Records that the current thread owns the resource identified by `key`.
1142 ///
1143 /// This is a no-op if the `deadlock_detection` feature is not enabled.
1144 /// Each recorded acquisition should be paired with a call to
1145 /// [`release_resource`] using the same key. The same key may be recorded
1146 /// more than once if a resource is acquired repeatedly by the same thread.
1147 /// Call this after the resource has been acquired.
1148 ///
1149 /// # Safety
1150 ///
1151 /// This must not be called from a callback passed to a parking-lot
1152 /// function.
1153#[inline]
1154pub unsafe fn acquire_resource(_key: usize) {
1155let abort_on_panic = super::AbortOnPanic;
11561157#[cfg(feature = "deadlock_detection")]
1158unsafe {
1159 deadlock_impl::acquire_resource(_key);
1160 }
11611162abort_on_panic.disarm();
1163 }
11641165/// Stops recording one acquisition of the resource identified by `key` by
1166 /// the current thread.
1167 ///
1168 /// This is a no-op if the `deadlock_detection` feature is not enabled.
1169 /// The key should match an acquisition previously recorded by this thread.
1170 /// The bookkeeping may be removed before the underlying resource is
1171 /// physically released.
1172 ///
1173 /// # Safety
1174 ///
1175 /// This must not be called from a callback passed to a parking-lot
1176 /// function.
1177#[inline]
1178pub unsafe fn release_resource(_key: usize) {
1179let abort_on_panic = super::AbortOnPanic;
11801181#[cfg(feature = "deadlock_detection")]
1182unsafe {
1183 deadlock_impl::release_resource(_key);
1184 }
11851186abort_on_panic.disarm();
1187 }
11881189/// Detects and returns all currently deadlocked thread components.
1190 ///
1191 /// Each inner vector contains the threads in one mutually deadlocked
1192 /// component.
1193 ///
1194 /// Waits with a deadline are excluded because they can resolve by timing
1195 /// out.
1196 ///
1197 /// Reporting a component removes its threads from the parking queues. Each
1198 /// thread captures its backtrace and then remains blocked permanently.
1199#[cfg(feature = "deadlock_detection")]
1200 #[inline]
1201pub fn check_deadlock() -> Vec<Vec<deadlock_impl::DeadlockedThread>> {
1202let abort_on_panic = super::AbortOnPanic;
1203let result = deadlock_impl::check_deadlock();
1204 abort_on_panic.disarm();
1205 result
1206 }
12071208#[inline]
1209pub(super) unsafe fn on_unpark(_td: &super::ThreadData) {
1210let abort_on_panic = super::AbortOnPanic;
12111212#[cfg(feature = "deadlock_detection")]
1213unsafe {
1214 deadlock_impl::on_unpark(_td);
1215 }
12161217abort_on_panic.disarm();
1218 }
1219}
12201221#[cfg(feature = "deadlock_detection")]
1222mod deadlock_impl {
1223use super::{HASHTABLE, NUM_THREADS, ThreadData, get_hashtable, with_thread_data};
1224use crate::thread_parker::{ThreadParkerT, UnparkHandleT};
1225use crate::word_lock::WordLock;
1226use backtrace::Backtrace;
1227use petgraph::graphmap::DiGraphMap;
1228use std::collections::HashMap;
1229use std::ptr::NonNull;
1230use std::sync::atomic::Ordering;
1231use std::sync::mpsc;
1232use std::thread::ThreadId;
12331234/// Representation of a deadlocked thread.
1235pub struct DeadlockedThread {
1236 thread_id: ThreadId,
1237 backtrace: Backtrace,
1238 }
12391240impl DeadlockedThread {
1241/// The identifier of the deadlocked thread.
1242pub fn thread_id(&self) -> ThreadId {
1243self.thread_id
1244 }
12451246/// The backtrace captured on the deadlocked thread.
1247pub fn backtrace(&self) -> &Backtrace {
1248&self.backtrace
1249 }
1250 }
12511252pub struct DeadlockData {
1253// Resource keys currently recorded as owned by this thread.
1254resources: Vec<usize>,
12551256// Set before linking to indicate whether the current wait has a
1257 // deadline, then read by the detector under the bucket lock.
1258pub(super) parked_with_timeout: bool,
12591260// Installed by the detector while the ThreadData is owned by the
1261 // parking lot and taken locally only after the parker returns.
1262deadlock_backtrace: Option<mpsc::Sender<DeadlockedThread>>,
12631264// Immutable after construction.
1265thread_id: ThreadId,
1266 }
12671268impl DeadlockData {
1269pub fn new() -> Self {
1270 DeadlockData {
1271 resources: Vec::new(),
1272 parked_with_timeout: false,
1273 deadlock_backtrace: None,
1274 thread_id: std::thread::current().id(),
1275 }
1276 }
1277 }
12781279pub(super) unsafe fn on_unpark(td: &ThreadData) {
1280let state = unsafe { td.state() };
1281if let Some(sender) = state.deadlock_data.deadlock_backtrace.take() {
1282 sender
1283 .send(DeadlockedThread {
1284 thread_id: state.deadlock_data.thread_id,
1285 backtrace: Backtrace::new(),
1286 })
1287 .unwrap();
1288// Drop the sender before parking permanently so that the detector
1289 // observes the channel closing.
1290drop(sender);
12911292// This ThreadData has been removed from its queue and cannot be
1293 // reached by another parking-lot wake operation. Park permanently
1294 // after reporting the backtrace.
1295unsafe { td.parker.prepare_park() };
1296unsafe { td.parker.park() };
1297unreachable!("unparked deadlocked thread!");
1298 }
1299 }
13001301pub unsafe fn acquire_resource(key: usize) {
1302 with_thread_data(|thread_data| {
1303unsafe { thread_data.state() }
1304 .deadlock_data
1305 .resources
1306 .push(key);
1307 });
1308 }
13091310pub unsafe fn release_resource(key: usize) {
1311 with_thread_data(|thread_data| {
1312let resources = &mut unsafe { thread_data.state() }.deadlock_data.resources;
13131314// During TLS destruction the original ThreadData may no longer be
1315 // accessible, in which case with_thread_data supplies a temporary
1316 // empty ThreadData. There is no bookkeeping left to update.
1317if let Some(p) = resources.iter().rposition(|x| *x == key) {
1318 resources.swap_remove(p);
1319 }
1320 });
1321 }
13221323pub fn check_deadlock() -> Vec<Vec<DeadlockedThread>> {
1324// First use an approximate snapshot to avoid the precise scan in the
1325 // common case where no cycle is observed.
1326if unsafe { check_wait_graph_fast() } {
1327// Verify the result using a consistent snapshot.
1328unsafe { check_wait_graph_slow() }
1329 } else {
1330 Vec::new()
1331 }
1332 }
13331334// Build an approximate wait graph while locking one bucket at a time. The
1335 // resulting snapshot may combine states from different points in time, so
1336 // a cycle only triggers the precise slow check.
1337unsafe fn check_wait_graph_fast() -> bool {
1338'retry: loop {
1339let table = get_hashtable();
1340let thread_count = NUM_THREADS.load(Ordering::Relaxed);
1341let mut graph =
1342 DiGraphMap::<usize, ()>::with_capacity(thread_count * 2, thread_count * 2);
13431344for b in &table.entries[..] {
1345 b.mutex.lock();
13461347// Hash-table growth may have moved this bucket's queue while
1348 // we were waiting for its lock. Restart with the current table
1349 // rather than traversing queue links no longer protected by
1350 // this bucket.
1351if !core::ptr::eq(HASHTABLE.load(Ordering::Relaxed), table) {
1352unsafe { b.mutex.unlock() };
1353continue 'retry;
1354 }
13551356let mut current = b.queue_head.get();
1357while let Some(current_ptr) = current {
1358let thread_data = unsafe { current_ptr.as_ref() };
1359let state = unsafe { thread_data.state() };
1360// Exclude timed waits because they can eventually resolve
1361 // without being unparked by another thread.
1362if !state.deadlock_data.parked_with_timeout {
1363let thread = current_ptr.as_ptr().addr();
1364// Each owned resource points to its owning thread.
1365for &resource in &state.deadlock_data.resources {
1366 graph.add_edge(resource, thread, ());
1367 }
1368// The thread points to the queue key on which it is parked.
1369graph.add_edge(thread, thread_data.key.load(Ordering::Relaxed), ());
1370 }
1371 current = state.next_in_queue;
1372 }
1373// SAFETY: We hold the lock here, as required
1374unsafe { b.mutex.unlock() };
1375 }
13761377return petgraph::algo::is_cyclic_directed(&graph);
1378 }
1379 }
13801381#[derive(Hash, PartialEq, Eq, PartialOrd, Ord, Copy, Clone)]
1382enum WaitGraphNode {
1383 Thread(NonNull<ThreadData>),
1384 Resource(usize),
1385 }
13861387use self::WaitGraphNode::*;
13881389// Unlike the fast check, this locks every bucket while constructing the
1390 // wait graph and selecting deadlocked components. Reported threads are
1391 // removed from their queues before the buckets are unlocked.
1392unsafe fn check_wait_graph_slow() -> Vec<Vec<DeadlockedThread>> {
1393// Reporting mutates the global queues, so only one slow check may run
1394 // at a time.
1395static DEADLOCK_DETECTION_LOCK: WordLock = WordLock::new();
1396 DEADLOCK_DETECTION_LOCK.lock();
13971398let mut table = get_hashtable();
1399loop {
1400// Lock all buckets in the candidate table.
1401for b in &table.entries[..] {
1402 b.mutex.lock();
1403 }
14041405// Another thread may have replaced the hash table while its
1406 // buckets were being locked. In that case, retry with the current
1407 // table.
1408let new_table = get_hashtable();
1409if core::ptr::eq(new_table, table) {
1410break;
1411 }
14121413// Unlock the candidate table's buckets and try again.
1414for b in &table.entries[..] {
1415// SAFETY: We hold the lock here, as required
1416unsafe { b.mutex.unlock() };
1417 }
14181419 table = new_table;
1420 }
14211422let thread_count = NUM_THREADS.load(Ordering::Relaxed);
1423let mut graph =
1424 DiGraphMap::<WaitGraphNode, ()>::with_capacity(thread_count * 2, thread_count * 2);
14251426for b in &table.entries[..] {
1427let mut current = b.queue_head.get();
1428while let Some(current_ptr) = current {
1429let thread_data = unsafe { current_ptr.as_ref() };
1430let state = unsafe { thread_data.state() };
1431// Exclude timed waits because they can eventually resolve
1432 // without being unparked by another thread.
1433if !state.deadlock_data.parked_with_timeout {
1434// Each owned resource points to its owning thread.
1435for &resource in &state.deadlock_data.resources {
1436 graph.add_edge(Resource(resource), Thread(current_ptr), ());
1437 }
1438// The thread points to the queue key on which it is parked.
1439graph.add_edge(
1440 Thread(current_ptr),
1441 Resource(thread_data.key.load(Ordering::Relaxed)),
1442 (),
1443 );
1444 }
1445 current = state.next_in_queue;
1446 }
1447 }
14481449let components = graph_components(&graph);
1450let deadlocked_thread_count = components.iter().map(Vec::len).sum();
1451let mut senders = HashMap::with_capacity(deadlocked_thread_count);
1452let mut receivers = Vec::with_capacity(components.len());
1453// Use one channel per component, with one sender stored in each thread
1454 // that must report a backtrace.
1455for component in components {
1456let (sender, receiver) = mpsc::channel();
1457for thread in component {
1458let previous = senders.insert(thread, sender.clone());
1459debug_assert!(previous.is_none());
1460 }
1461// Drop the detector's sender so only the per-thread copies keep
1462 // the channel open.
1463drop(sender);
1464 receivers.push(receiver);
1465 }
14661467// The components contain raw ThreadData pointers. Keeping every bucket
1468 // locked preserves each queued ThreadData until it has been unlinked
1469 // and passed to unpark_lock.
1470let mut handles = Vec::with_capacity(deadlocked_thread_count);
1471for bucket in &table.entries[..] {
1472let mut current = bucket.queue_head.get();
1473let mut previous: Option<NonNull<ThreadData>> = None;
1474while let Some(current_ptr) = current {
1475let thread_data = unsafe { current_ptr.as_ref() };
1476let state = unsafe { thread_data.state() };
1477let next = state.next_in_queue;
1478if let Some(sender) = senders.remove(¤t_ptr) {
1479if let Some(previous) = previous {
1480unsafe { previous.as_ref().state() }.next_in_queue = next;
1481 } else {
1482 bucket.queue_head.set(next);
1483 }
1484if bucket.queue_tail.get() == Some(current_ptr) {
1485 bucket.queue_tail.set(previous);
1486 }
14871488// The reporting thread parks permanently after capturing
1489 // its backtrace, so it must no longer be visible to normal
1490 // queue operations.
1491state.next_in_queue = None;
1492 state.deadlock_data.deadlock_backtrace = Some(sender);
1493 handles.push(unsafe { thread_data.parker.unpark_lock() });
1494 } else {
1495 previous = Some(current_ptr);
1496 }
1497 current = next;
1498 }
1499 }
1500debug_assert!(senders.is_empty());
15011502for b in &table.entries[..] {
1503// SAFETY: We hold the lock here, as required
1504unsafe { b.mutex.unlock() };
1505 }
15061507// Platform wake operations may block, so perform them only after all
1508 // bucket locks have been released.
1509for handle in handles {
1510unsafe { handle.unpark() };
1511 }
15121513// Collect until every per-thread sender for the component is dropped.
1514let results = receivers
1515 .into_iter()
1516 .map(|receiver| receiver.iter().collect())
1517 .collect();
15181519unsafe { DEADLOCK_DETECTION_LOCK.unlock() };
15201521 results
1522 }
15231524// Returns the thread nodes in each nontrivial strongly connected component.
1525fn graph_components(g: &DiGraphMap<WaitGraphNode, ()>) -> Vec<Vec<NonNull<ThreadData>>> {
1526 petgraph::algo::kosaraju_scc(g)
1527 .into_iter()
1528 .filter(|component| component.len() > 1)
1529 .filter_map(|component| {
1530let threads = component
1531 .into_iter()
1532 .filter_map(|node| match node {
1533 Thread(thread) => Some(thread),
1534 Resource(_) => None,
1535 })
1536 .collect::<Vec<_>>();
1537 (!threads.is_empty()).then_some(threads)
1538 })
1539 .collect()
1540 }
15411542#[cfg(test)]
1543mod tests {
1544use super::*;
15451546#[test]
1547fn overlapping_cycles_form_one_component() {
1548let storage = [0u8; 3];
1549let thread1 = NonNull::from(&storage[0]).cast::<ThreadData>();
1550let thread2 = NonNull::from(&storage[1]).cast::<ThreadData>();
1551let thread3 = NonNull::from(&storage[2]).cast::<ThreadData>();
1552let mut graph = DiGraphMap::new();
15531554 graph.add_edge(Thread(thread1), Resource(1), ());
1555 graph.add_edge(Resource(1), Thread(thread2), ());
1556 graph.add_edge(Thread(thread2), Resource(2), ());
1557 graph.add_edge(Resource(2), Thread(thread1), ());
15581559 graph.add_edge(Thread(thread2), Resource(3), ());
1560 graph.add_edge(Resource(3), Thread(thread3), ());
1561 graph.add_edge(Thread(thread3), Resource(4), ());
1562 graph.add_edge(Resource(4), Thread(thread2), ());
15631564let mut components = graph_components(&graph);
1565assert_eq!(components.len(), 1);
1566 components[0].sort_unstable();
15671568let mut expected = vec![thread1, thread2, thread3];
1569 expected.sort_unstable();
1570assert_eq!(components[0], expected);
1571 }
1572 }
1573}
15741575#[cfg(test)]
1576mod tests {
1577use super::{DEFAULT_PARK_TOKEN, DEFAULT_UNPARK_TOKEN, ThreadData};
1578use std::{
1579 ptr,
1580 sync::{
1581 Arc,
1582 atomic::{AtomicIsize, AtomicPtr, AtomicUsize, Ordering},
1583 },
1584 thread,
1585 time::Duration,
1586 };
15871588/// Calls a closure for every `ThreadData` currently parked on a given key
1589fn for_each(key: usize, mut f: impl FnMut(&ThreadData)) {
1590let bucket = super::lock_bucket(key);
15911592let mut current = bucket.queue_head.get();
1593while let Some(current_ptr) = current {
1594let current_ref = unsafe { current_ptr.as_ref() };
1595if current_ref.key.load(Ordering::Relaxed) == key {
1596 f(current_ref);
1597 }
1598 current = unsafe { current_ref.state() }.next_in_queue;
1599 }
16001601// SAFETY: We hold the lock here, as required
1602unsafe { bucket.mutex.unlock() };
1603 }
16041605macro_rules! test {
1606 ( $( $name:ident(
1607 repeats: $repeats:expr,
1608 latches: $latches:expr,
1609 delay: $delay:expr,
1610 threads: $threads:expr,
1611 single_unparks: $single_unparks:expr);
1612 )* ) => {
1613 $(#[test]
1614fn $name() {
1615let delay = Duration::from_micros($delay);
1616for _ in 0..$repeats {
1617 run_parking_test($latches, delay, $threads, $single_unparks);
1618 }
1619 })*
1620 };
1621 }
16221623test! {
1624 unpark_all_one_fast(
1625 repeats: 1000, latches: 1, delay: 0, threads: 1, single_unparks: 0
1626);
1627 unpark_all_hundred_fast(
1628 repeats: 100, latches: 1, delay: 0, threads: 100, single_unparks: 0
1629);
1630 unpark_one_one_fast(
1631 repeats: 1000, latches: 1, delay: 0, threads: 1, single_unparks: 1
1632);
1633 unpark_one_hundred_fast(
1634 repeats: 20, latches: 1, delay: 0, threads: 100, single_unparks: 100
1635);
1636 unpark_one_fifty_then_fifty_all_fast(
1637 repeats: 50, latches: 1, delay: 0, threads: 100, single_unparks: 50
1638);
1639 unpark_all_one(
1640 repeats: 100, latches: 1, delay: 10000, threads: 1, single_unparks: 0
1641);
1642 unpark_all_hundred(
1643 repeats: 100, latches: 1, delay: 10000, threads: 100, single_unparks: 0
1644);
1645 unpark_one_one(
1646 repeats: 10, latches: 1, delay: 10000, threads: 1, single_unparks: 1
1647);
1648 unpark_one_fifty(
1649 repeats: 1, latches: 1, delay: 10000, threads: 50, single_unparks: 50
1650);
1651 unpark_one_fifty_then_fifty_all(
1652 repeats: 2, latches: 1, delay: 10000, threads: 100, single_unparks: 50
1653);
1654 hundred_unpark_all_one_fast(
1655 repeats: 100, latches: 100, delay: 0, threads: 1, single_unparks: 0
1656);
1657 hundred_unpark_all_one(
1658 repeats: 1, latches: 100, delay: 10000, threads: 1, single_unparks: 0
1659);
1660 }
16611662fn run_parking_test(
1663 num_latches: usize,
1664 delay: Duration,
1665 num_threads: usize,
1666 num_single_unparks: usize,
1667 ) {
1668let mut tests = Vec::with_capacity(num_latches);
16691670for _ in 0..num_latches {
1671let test = Arc::new(SingleLatchTest::new(num_threads));
1672let mut threads = Vec::with_capacity(num_threads);
1673for _ in 0..num_threads {
1674let test = test.clone();
1675 threads.push(thread::spawn(move || test.run()));
1676 }
1677 tests.push((test, threads));
1678 }
16791680for unpark_index in 0..num_single_unparks {
1681 thread::sleep(delay);
1682for (test, _) in &tests {
1683 test.unpark_one(unpark_index);
1684 }
1685 }
16861687for (test, threads) in tests {
1688 test.finish(num_single_unparks);
1689for thread in threads {
1690 thread.join().expect("Test thread panic");
1691 }
1692 }
1693 }
16941695struct SingleLatchTest {
1696 semaphore: AtomicIsize,
1697 num_awake: AtomicUsize,
1698/// Holds the pointer to the last *unprocessed* woken up thread.
1699last_awoken: AtomicPtr<ThreadData>,
1700/// Total number of threads participating in this test.
1701num_threads: usize,
1702 }
17031704impl SingleLatchTest {
1705pub fn new(num_threads: usize) -> Self {
1706Self {
1707// This implements a fair (FIFO) semaphore, and it starts out unavailable.
1708semaphore: AtomicIsize::new(0),
1709 num_awake: AtomicUsize::new(0),
1710 last_awoken: AtomicPtr::new(ptr::null_mut()),
1711 num_threads,
1712 }
1713 }
17141715pub fn run(&self) {
1716// Get one slot from the semaphore
1717self.down();
17181719// Report back to the test verification code that this thread woke up
1720let this_thread_ptr = super::with_thread_data(|t| t as *const _ as *mut _);
1721self.last_awoken.store(this_thread_ptr, Ordering::SeqCst);
1722self.num_awake.fetch_add(1, Ordering::SeqCst);
1723 }
17241725pub fn unpark_one(&self, single_unpark_index: usize) {
1726// last_awoken should be null at all times except between self.up() and at the bottom
1727 // of this method where it's reset to null again
1728assert!(self.last_awoken.load(Ordering::SeqCst).is_null());
17291730let mut queue: Vec<*mut ThreadData> = Vec::with_capacity(self.num_threads);
1731 for_each(self.semaphore_addr(), |thread_data| {
1732 queue.push(thread_data as *const _ as *mut _);
1733 });
1734assert!(queue.len() <= self.num_threads - single_unpark_index);
17351736let num_awake_before_up = self.num_awake.load(Ordering::SeqCst);
17371738self.up();
17391740// Wait for a parked thread to wake up and update num_awake + last_awoken.
1741while self.num_awake.load(Ordering::SeqCst) != num_awake_before_up + 1 {
1742 thread::yield_now();
1743 }
17441745// At this point the other thread should have set last_awoken inside the run() method
1746let last_awoken = self.last_awoken.load(Ordering::SeqCst);
1747assert!(!last_awoken.is_null());
1748if !queue.is_empty() && queue[0] != last_awoken {
1749panic!(
1750"Woke up wrong thread:\n\tqueue: {:?}\n\tlast awoken: {:?}",
1751 queue, last_awoken
1752 );
1753 }
1754self.last_awoken.store(ptr::null_mut(), Ordering::SeqCst);
1755 }
17561757pub fn finish(&self, num_single_unparks: usize) {
1758// The amount of threads not unparked via unpark_one
1759let mut num_threads_left = self.num_threads.checked_sub(num_single_unparks).unwrap();
17601761// Wake remaining threads up with unpark_all. Has to be in a loop, because there might
1762 // still be threads that has not yet parked.
1763while num_threads_left > 0 {
1764let mut num_waiting_on_address = 0;
1765 for_each(self.semaphore_addr(), |_thread_data| {
1766 num_waiting_on_address += 1;
1767 });
1768assert!(num_waiting_on_address <= num_threads_left);
17691770let num_awake_before_unpark = self.num_awake.load(Ordering::SeqCst);
17711772let num_unparked =
1773unsafe { super::unpark_all(self.semaphore_addr(), DEFAULT_UNPARK_TOKEN) };
1774assert!(num_unparked >= num_waiting_on_address);
1775assert!(num_unparked <= num_threads_left);
17761777// Wait for all unparked threads to wake up and update num_awake + last_awoken.
1778while self.num_awake.load(Ordering::SeqCst)
1779 != num_awake_before_unpark + num_unparked
1780 {
1781 thread::yield_now()
1782 }
17831784 num_threads_left = num_threads_left.checked_sub(num_unparked).unwrap();
1785 }
1786// By now, all threads should have been woken up
1787assert_eq!(self.num_awake.load(Ordering::SeqCst), self.num_threads);
17881789// Make sure no thread is parked on our semaphore address
1790let mut num_waiting_on_address = 0;
1791 for_each(self.semaphore_addr(), |_thread_data| {
1792 num_waiting_on_address += 1;
1793 });
1794assert_eq!(num_waiting_on_address, 0);
1795 }
17961797pub fn down(&self) {
1798let old_semaphore_value = self.semaphore.fetch_sub(1, Ordering::SeqCst);
17991800if old_semaphore_value > 0 {
1801// We acquired the semaphore. Done.
1802return;
1803 }
18041805// We need to wait.
1806let validate = || true;
1807let before_sleep = || {};
1808let timed_out = |_, _| {};
1809unsafe {
1810super::park(
1811self.semaphore_addr(),
1812 validate,
1813 before_sleep,
1814 timed_out,
1815 DEFAULT_PARK_TOKEN,
1816None,
1817 );
1818 }
1819 }
18201821pub fn up(&self) {
1822let old_semaphore_value = self.semaphore.fetch_add(1, Ordering::SeqCst);
18231824// Check if anyone was waiting on the semaphore. If they were, then pass ownership to them.
1825if old_semaphore_value < 0 {
1826// We need to continue until we have actually unparked someone. It might be that
1827 // the thread we want to pass ownership to has decremented the semaphore counter,
1828 // but not yet parked.
1829loop {
1830match unsafe {
1831super::unpark_one(self.semaphore_addr(), |_| DEFAULT_UNPARK_TOKEN)
1832 .unparked_threads
1833 } {
18341 => break,
18350 => (),
1836 i => panic!("Should not wake up {} threads", i),
1837 }
1838 }
1839 }
1840 }
18411842fn semaphore_addr(&self) -> usize {
1843 core::ptr::from_ref(&self.semaphore).addr()
1844 }
1845 }
1846}