| 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 | |