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 | |
17 | fn new() -> Self; |
18 | |
19 | /// Prepares the parker. This should be called before adding it to the queue. |
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. |
24 | 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. |
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. |
33 | 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. |
38 | 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`. |
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 | |