Skip to main content

parking_lot_core/thread_parker/
mod.rs

1use std::time::Instant;
2
3/// Trait for the platform thread parker implementation.
4///
5/// The unsafe methods form a protocol and implementations may rely on the
6/// parker remaining at a stable address once it has been used. In particular,
7/// the Unix implementation contains initialized pthread mutexes and condition
8/// variables which must not be moved. Calling an unsafe method after moving the
9/// parker from the address at which a previous unsafe method was called is
10/// undefined behavior.
11///
12/// The protocol also transfers ownership of the surrounding `ThreadData`
13/// between the parked thread and the parking lot. Writes performed by an
14/// unparker before `unpark_lock` must be visible after `park` returns or after
15/// `park_until` returns true.
16pub trait ThreadParkerT {
17    type UnparkHandle: UnparkHandleT;
18
19    const IS_CHEAP_TO_CONSTRUCT: bool;
20
21    fn new() -> Self;
22
23    /// Prepares the parker. This must be called while the local thread still
24    /// owns the `ThreadData`, before adding it to the queue.
25    unsafe fn prepare_park(&self);
26
27    /// Checks if the park timed out. This should be called while holding the
28    /// queue lock after `park_until` has returned false.
29    unsafe fn timed_out(&self) -> bool;
30
31    /// Parks the thread until it is unparked. This must be called after the
32    /// `ThreadData` has been added to the queue and the queue has been unlocked.
33    /// Returning transfers ownership of the `ThreadData` back to the local
34    /// thread and acquires the writes that preceded `unpark_lock`.
35    unsafe fn park(&self);
36
37    /// Parks the thread until it is unparked or the timeout is reached. This
38    /// must be called after the `ThreadData` has been added to the queue and the
39    /// queue has been unlocked. Returning true transfers ownership of the
40    /// `ThreadData` back to the local thread and acquires the writes that
41    /// preceded `unpark_lock`. Returning false does not transfer ownership: the
42    /// caller must resolve the timeout while holding the queue lock.
43    ///
44    /// The timeout is the earliest point at which this method may return false,
45    /// but scheduling and platform-specific behavior may delay the return.
46    unsafe fn park_until(&self, timeout: Instant) -> bool;
47
48    /// Prepares to unpark a thread after removing its `ThreadData` from its
49    /// queue and writing its result fields.
50    ///
51    /// The caller must exclusively own the target `ThreadData`, which must
52    /// remain valid and parked until this method is called.
53    ///
54    /// The returned handle must be consumed by a call to
55    /// [`UnparkHandleT::unpark`].
56    ///
57    /// After this returns, the caller must not access the target `ThreadData`:
58    /// the target may immediately observe the unpark and destroy it, even while
59    /// the queue remains locked. The returned handle must nevertheless remain
60    /// safe to pass to `unpark`. Implementations may either keep the parker
61    /// alive or make such a late `unpark` harmless.
62    unsafe fn unpark_lock(&self) -> Self::UnparkHandle;
63}
64
65/// Handle for a thread whose unpark has been committed. Every handle returned
66/// by [`ThreadParkerT::unpark_lock`] must be consumed by a call to
67/// [`UnparkHandleT::unpark`]. Any potentially expensive wake operation is
68/// delayed until then.
69pub trait UnparkHandleT {
70    /// Wakes up the parked thread. This should be called after the queue lock is
71    /// released to avoid blocking the queue for too long.
72    ///
73    /// This method is unsafe for the same reason as the unsafe methods in
74    /// `ThreadParkerT`.
75    unsafe fn unpark(self);
76}
77
78cfg_select! {
79    any(target_os = "linux", target_os = "android") => {
80        #[path = "linux.rs"]
81        mod imp;
82    }
83    unix => {
84        #[path = "unix.rs"]
85        mod imp;
86    }
87    windows => {
88        #[path = "windows/mod.rs"]
89        mod imp;
90    }
91    target_os = "redox" => {
92        #[path = "redox.rs"]
93        mod imp;
94    }
95    all(target_env = "sgx", target_vendor = "fortanix") => {
96        #[path = "sgx.rs"]
97        mod imp;
98    }
99    all(
100        feature = "nightly",
101        target_family = "wasm",
102        target_feature = "atomics"
103    ) => {
104        #[path = "wasm_atomic.rs"]
105        mod imp;
106    }
107    target_os = "wasi" => {
108        // WASI provides pthreads with or without the atomics extension. This
109        // also works on stable, where target_feature = "atomics" is not set.
110        // Keep the intrinsic backend above preferred when it is available.
111        #[path = "unix.rs"]
112        mod imp;
113    }
114    target_family = "wasm" => {
115        #[path = "wasm.rs"]
116        mod imp;
117    }
118    _ => {
119        #[path = "generic.rs"]
120        mod imp;
121    }
122}
123
124pub use self::imp::{ThreadParker, thread_yield};