tokio_executor/
park.rs

1//! Abstraction over blocking and unblocking the current thread.
2//!
3//! Provides an abstraction over blocking the current thread. This is similar to
4//! the park / unpark constructs provided by [`std`] but made generic. This
5//! allows embedding custom functionality to perform when the thread is blocked.
6//!
7//! A blocked [`Park`][p] instance is unblocked by calling [`unpark`] on its
8//! [`Unpark`][up] handle.
9//!
10//! The [`ParkThread`] struct implements [`Park`][p] using
11//! [`thread::park`][`std`] to put the thread to sleep. The Tokio reactor also
12//! implements park, but uses [`mio::Poll`][mio] to block the thread instead.
13//!
14//! The [`Park`][p] trait is composable. A timer implementation might decorate a
15//! [`Park`][p] implementation by checking if any timeouts have elapsed after
16//! the inner [`Park`][p] implementation unblocks.
17//!
18//! # Model
19//!
20//! Conceptually, each [`Park`][p] instance has an associated token, which is
21//! initially not present:
22//!
23//! * The [`park`] method blocks the current thread unless or until the token
24//!   is available, at which point it atomically consumes the token.
25//! * The [`unpark`] method atomically makes the token available if it wasn't
26//!   already.
27//!
28//! Some things to note:
29//!
30//! * If [`unpark`] is called before [`park`], the next call to [`park`] will
31//! **not** block the thread.
32//! * **Spurious** wakeups are permitted, i.e., the [`park`] method may unblock
33//!   even if [`unpark`] was not called.
34//! * [`park_timeout`] does the same as [`park`] but allows specifying a maximum
35//!   time to block the thread for.
36//!
37//! [`std`]: https://doc.rust-lang.org/std/thread/fn.park.html
38//! [`thread::park`]: https://doc.rust-lang.org/std/thread/fn.park.html
39//! [`ParkThread`]: struct.ParkThread.html
40//! [p]: trait.Park.html
41//! [`park`]: trait.Park.html#tymethod.park
42//! [`park_timeout`]: trait.Park.html#tymethod.park_timeout
43//! [`unpark`]: trait.Unpark.html#tymethod.unpark
44//! [up]: trait.Unpark.html
45//! [mio]: https://docs.rs/mio/0.6/mio/struct.Poll.html
46
47use std::marker::PhantomData;
48use std::rc::Rc;
49use std::sync::Arc;
50use std::time::Duration;
51
52use crossbeam_utils::sync::{Parker, Unparker};
53
54/// Block the current thread.
55///
56/// See [module documentation][mod] for more details.
57///
58/// [mod]: ../index.html
59pub trait Park {
60    /// Unpark handle type for the `Park` implementation.
61    type Unpark: Unpark;
62
63    /// Error returned by `park`
64    type Error;
65
66    /// Get a new `Unpark` handle associated with this `Park` instance.
67    fn unpark(&self) -> Self::Unpark;
68
69    /// Block the current thread unless or until the token is available.
70    ///
71    /// A call to `park` does not guarantee that the thread will remain blocked
72    /// forever, and callers should be prepared for this possibility. This
73    /// function may wakeup spuriously for any reason.
74    ///
75    /// See [module documentation][mod] for more details.
76    ///
77    /// # Panics
78    ///
79    /// This function **should** not panic, but ultimately, panics are left as
80    /// an implementation detail. Refer to the documentation for the specific
81    /// `Park` implementation
82    ///
83    /// [mod]: ../index.html
84    fn park(&mut self) -> Result<(), Self::Error>;
85
86    /// Park the current thread for at most `duration`.
87    ///
88    /// This function is the same as `park` but allows specifying a maximum time
89    /// to block the thread for.
90    ///
91    /// Same as `park`, there is no guarantee that the thread will remain
92    /// blocked for any amount of time. Spurious wakeups are permitted for any
93    /// reason.
94    ///
95    /// See [module documentation][mod] for more details.
96    ///
97    /// # Panics
98    ///
99    /// This function **should** not panic, but ultimately, panics are left as
100    /// an implementation detail. Refer to the documentation for the specific
101    /// `Park` implementation
102    ///
103    /// [mod]: ../index.html
104    fn park_timeout(&mut self, duration: Duration) -> Result<(), Self::Error>;
105}
106
107/// Unblock a thread blocked by the associated [`Park`] instance.
108///
109/// See [module documentation][mod] for more details.
110///
111/// [mod]: ../index.html
112/// [`Park`]: trait.Park.html
113pub trait Unpark: Sync + Send + 'static {
114    /// Unblock a thread that is blocked by the associated `Park` handle.
115    ///
116    /// Calling `unpark` atomically makes available the unpark token, if it is
117    /// not already available.
118    ///
119    /// See [module documentation][mod] for more details.
120    ///
121    /// # Panics
122    ///
123    /// This function **should** not panic, but ultimately, panics are left as
124    /// an implementation detail. Refer to the documentation for the specific
125    /// `Unpark` implementation
126    ///
127    /// [mod]: ../index.html
128    fn unpark(&self);
129}
130
131impl Unpark for Box<dyn Unpark> {
132    fn unpark(&self) {
133        (**self).unpark()
134    }
135}
136
137impl Unpark for Arc<dyn Unpark> {
138    fn unpark(&self) {
139        (**self).unpark()
140    }
141}
142
143/// Blocks the current thread using a condition variable.
144///
145/// Implements the [`Park`] functionality by using a condition variable. An
146/// atomic variable is also used to avoid using the condition variable if
147/// possible.
148///
149/// The condition variable is cached in a thread-local variable and is shared
150/// across all `ParkThread` instances created on the same thread. This also
151/// means that an instance of `ParkThread` might be unblocked by a handle
152/// associated with a different `ParkThread` instance.
153#[derive(Debug)]
154pub struct ParkThread {
155    _anchor: PhantomData<Rc<()>>,
156}
157
158/// Error returned by [`ParkThread`]
159///
160/// This currently is never returned, but might at some point in the future.
161///
162/// [`ParkThread`]: struct.ParkThread.html
163#[derive(Debug)]
164pub struct ParkError {
165    _p: (),
166}
167
168/// Unblocks a thread that was blocked by `ParkThread`.
169#[derive(Clone, Debug)]
170pub struct UnparkThread {
171    inner: Unparker,
172}
173
174thread_local! {
175    static CURRENT_PARKER: Parker = Parker::new();
176}
177
178// ===== impl ParkThread =====
179
180impl ParkThread {
181    /// Create a new `ParkThread` handle for the current thread.
182    ///
183    /// This type cannot be moved to other threads, so it should be created on
184    /// the thread that the caller intends to park.
185    pub fn new() -> ParkThread {
186        ParkThread {
187            _anchor: PhantomData,
188        }
189    }
190
191    /// Get a reference to the `ParkThread` handle for this thread.
192    fn with_current<F, R>(&self, f: F) -> R
193    where
194        F: FnOnce(&Parker) -> R,
195    {
196        CURRENT_PARKER.with(|inner| f(inner))
197    }
198}
199
200impl Park for ParkThread {
201    type Unpark = UnparkThread;
202    type Error = ParkError;
203
204    fn unpark(&self) -> Self::Unpark {
205        let inner = self.with_current(|inner| inner.unparker().clone());
206        UnparkThread { inner }
207    }
208
209    fn park(&mut self) -> Result<(), Self::Error> {
210        self.with_current(|inner| inner.park());
211        Ok(())
212    }
213
214    fn park_timeout(&mut self, duration: Duration) -> Result<(), Self::Error> {
215        self.with_current(|inner| inner.park_timeout(duration));
216        Ok(())
217    }
218}
219
220// ===== impl UnparkThread =====
221
222impl Unpark for UnparkThread {
223    fn unpark(&self) {
224        self.inner.unpark();
225    }
226}