1 use cfg_if::cfg_if;
2 use std::time::Instant;
3 
4 /// Trait for the platform thread parker implementation.
5 ///
6 /// All unsafe methods are unsafe because the Unix thread parker is based on
7 /// pthread mutexes and condvars. Those primitives must not be moved and used
8 /// from any other memory address than the one they were located at when they
9 /// were initialized. As such, it's UB to call any unsafe method on
10 /// `ThreadParkerT` if the implementing instance has moved since the last
11 /// call to any of the unsafe methods.
12 pub trait ThreadParkerT {
13     type UnparkHandle: UnparkHandleT;
14 
15     const IS_CHEAP_TO_CONSTRUCT: bool;
16 
new() -> Self17     fn new() -> Self;
18 
19     /// Prepares the parker. This should be called before adding it to the queue.
prepare_park(&self)20     unsafe fn prepare_park(&self);
21 
22     /// Checks if the park timed out. This should be called while holding the
23     /// queue lock after park_until has returned false.
timed_out(&self) -> bool24     unsafe fn timed_out(&self) -> bool;
25 
26     /// Parks the thread until it is unparked. This should be called after it has
27     /// been added to the queue, after unlocking the queue.
park(&self)28     unsafe fn park(&self);
29 
30     /// Parks the thread until it is unparked or the timeout is reached. This
31     /// should be called after it has been added to the queue, after unlocking
32     /// the queue. Returns true if we were unparked and false if we timed out.
park_until(&self, timeout: Instant) -> bool33     unsafe fn park_until(&self, timeout: Instant) -> bool;
34 
35     /// Locks the parker to prevent the target thread from exiting. This is
36     /// necessary to ensure that thread-local ThreadData objects remain valid.
37     /// This should be called while holding the queue lock.
unpark_lock(&self) -> Self::UnparkHandle38     unsafe fn unpark_lock(&self) -> Self::UnparkHandle;
39 }
40 
41 /// Handle for a thread that is about to be unparked. We need to mark the thread
42 /// as unparked while holding the queue lock, but we delay the actual unparking
43 /// until after the queue lock is released.
44 pub trait UnparkHandleT {
45     /// Wakes up the parked thread. This should be called after the queue lock is
46     /// released to avoid blocking the queue for too long.
47     ///
48     /// This method is unsafe for the same reason as the unsafe methods in
49     /// `ThreadParkerT`.
unpark(self)50     unsafe fn unpark(self);
51 }
52 
53 cfg_if! {
54     if #[cfg(any(target_os = "linux", target_os = "android"))] {
55         #[path = "linux.rs"]
56         mod imp;
57     } else if #[cfg(unix)] {
58         #[path = "unix.rs"]
59         mod imp;
60     } else if #[cfg(windows)] {
61         #[path = "windows/mod.rs"]
62         mod imp;
63     } else if #[cfg(target_os = "redox")] {
64         #[path = "redox.rs"]
65         mod imp;
66     } else if #[cfg(all(target_env = "sgx", target_vendor = "fortanix"))] {
67         #[path = "sgx.rs"]
68         mod imp;
69     } else if #[cfg(all(
70         feature = "nightly",
71         target_family = "wasm",
72         target_feature = "atomics"
73     ))] {
74         #[path = "wasm_atomic.rs"]
75         mod imp;
76     } else if #[cfg(target_family = "wasm")] {
77         #[path = "wasm.rs"]
78         mod imp;
79     } else {
80         #[path = "generic.rs"]
81         mod imp;
82     }
83 }
84 
85 pub use self::imp::{thread_yield, ThreadParker, UnparkHandle};
86