async_thread/
lib.rs

1//! This crate provides an API identical to `std::thread`. However, `JoinHandle::join` is an `async
2//! fn`.
3//! ```
4//! let handle = crate::spawn(|| 5usize);
5//! assert_eq!(handle.join().await.map_err(drop), Ok(5));
6//! ```
7
8use futures_channel::oneshot;
9use std::any::Any;
10use std::io;
11use std::panic::{catch_unwind, AssertUnwindSafe};
12use std::thread as sync;
13
14/// An owned permission to join on a thread (block on its termination).
15///
16/// A `JoinHandle` *detaches* the associated thread when it is dropped, which
17/// means that there is no longer any handle to thread and no way to `join`
18/// on it.
19///
20/// Due to platform restrictions, it is not possible to `Clone` this
21/// handle: the ability to join a thread is a uniquely-owned permission.
22///
23/// This `struct` is created by the `thread::spawn` function and the
24/// `thread::Builder::spawn` method.
25///
26/// # Examples
27///
28/// Creation from `thread::spawn`:
29///
30/// ```
31/// let join_handle: async_thread::JoinHandle<_> = async_thread::spawn(|| {
32///     // some work here
33/// });
34/// ```
35///
36/// Creation from `thread::Builder::spawn`:
37///
38/// ```
39/// let builder = async_thread::Builder::new();
40///
41/// let join_handle: async_thread::JoinHandle<_> = builder.spawn(|| {
42///     // some work here
43/// }).unwrap();
44/// ```
45///
46/// Child being detached and outliving its parent:
47///
48/// ```no_run
49/// use std::time::Duration;
50///
51/// let original_thread = async_thread::spawn(|| {
52///     let _detached_thread = async_thread::spawn(|| {
53///         // Here we sleep to make sure that the first thread returns before.
54///         thread::sleep(Duration::from_millis(10));
55///         // This will be called, even though the JoinHandle is dropped.
56///         println!("♫ Still alive ♫");
57///     });
58/// });
59///
60/// original_thread.join().await.expect("The thread being joined has panicked");
61/// println!("Original thread is joined.");
62///
63/// // We make sure that the new thread has time to run, before the main
64/// // thread returns.
65///
66/// thread::sleep(Duration::from_millis(1000));
67/// ```
68#[derive(Debug)]
69pub struct JoinHandle<T> {
70    imp: sync::JoinHandle<()>,
71    chan: oneshot::Receiver<sync::Result<T>>,
72}
73
74impl<T> JoinHandle<T> {
75    /// Waits for the associated thread to finish.
76    ///
77    /// In terms of atomic memory orderings,  the completion of the associated
78    /// thread synchronizes with this function returning. In other words, all
79    /// operations performed by that thread are ordered before all
80    /// operations that happen after `join` returns.
81    ///
82    /// If the child thread panics, `Err` is returned with the parameter given
83    /// to `panic`.
84    ///
85    /// # Panics
86    ///
87    /// This function may panic on some platforms if a thread attempts to join
88    /// itself or otherwise may create a deadlock with joining threads.
89    ///
90    /// # Examples
91    ///
92    /// ```
93    /// let builder = async_thread::Builder::new();
94    ///
95    /// let join_handle: async_thread::JoinHandle<_> = builder.spawn(|| {
96    ///     // some work here
97    /// }).unwrap();
98    /// join_handle.join().await.expect("Couldn't join on the associated thread");
99    /// ```
100    pub async fn join(self) -> sync::Result<T> {
101        let ret = self.chan
102            .await
103            .map_err(|x| -> Box<dyn Any + Send + 'static> { Box::new(x) })
104            .and_then(|x| x);
105        let _ = self.imp.join(); // synchronize threads
106        ret
107    }
108
109    /// Extracts a handle to the underlying thread.
110    ///
111    /// # Examples
112    ///
113    /// ```
114    /// let builder = async_thread::Builder::new();
115    ///
116    /// let join_handle: async_thread::JoinHandle<_> = builder.spawn(|| {
117    ///     // some work here
118    /// }).unwrap();
119    ///
120    /// let thread = join_handle.thread();
121    /// println!("thread id: {:?}", thread.id());
122    /// ```
123    pub fn thread(&self) -> &sync::Thread {
124        self.imp.thread()
125    }
126}
127
128/// Thread factory, which can be used in order to configure the properties of
129/// a new thread.
130///
131/// Methods can be chained on it in order to configure it.
132///
133/// The two configurations available are:
134///
135/// - `name`: specifies an associated name for the thread
136/// - `stack_size`: specifies the desired stack size for the thread
137///
138/// The `spawn` method will take ownership of the builder and create an
139/// `io::Result` to the thread handle with the given configuration.
140///
141/// The `thread::spawn` free function uses a `Builder` with default
142/// configuration and `unwrap`s its return value.
143///
144/// You may want to use `spawn` instead of `thread::spawn`, when you want
145/// to recover from a failure to launch a thread, indeed the free function will
146/// panic where the `Builder` method will return a `io::Result`.
147///
148/// # Examples
149///
150/// ```
151/// use std::thread;
152///
153/// let builder = thread::Builder::new();
154///
155/// let handler = builder.spawn(|| {
156///     // thread code
157/// }).unwrap();
158///
159/// handler.join().unwrap();
160/// ```
161#[derive(Debug)]
162pub struct Builder {
163    imp: sync::Builder,
164}
165
166impl Builder {
167    /// Generates the base configuration for spawning a thread, from which
168    /// configuration methods can be chained.
169    ///
170    /// # Examples
171    ///
172    /// ```
173    /// let builder = async_thread::Builder::new()
174    ///                               .name("foo".into())
175    ///                               .stack_size(32 * 1024);
176    ///
177    /// let handler = builder.spawn(|| {
178    ///     // thread code
179    /// }).unwrap();
180    ///
181    /// handler.join().await.unwrap();
182    /// ```
183    pub fn new() -> Self {
184        Self {
185            imp: sync::Builder::new(),
186        }
187    }
188
189    /// Names the thread-to-be. Currently the name is used for identification
190    /// only in panic messages.
191    ///
192    /// The name must not contain null bytes (`\0`).
193    ///
194    /// For more information about named threads, see
195    /// the std::thread documentation.
196    ///
197    /// # Examples
198    ///
199    /// ```
200    /// let builder = async_thread::Builder::new()
201    ///     .name("foo".into());
202    ///
203    /// let handler = builder.spawn(|| {
204    ///     assert_eq!(thread::current().name(), Some("foo"))
205    /// }).unwrap();
206    ///
207    /// handler.join().await.unwrap();
208    /// ```
209    pub fn name(self, name: String) -> Self {
210        Self {
211            imp: self.imp.name(name),
212        }
213    }
214
215    /// Sets the size of the stack (in bytes) for the new thread.
216    ///
217    /// The actual stack size may be greater than this value if
218    /// the platform specifies a minimal stack size.
219    ///
220    /// For more information about the stack size for threads, see
221    /// the std::thread documentation.
222    ///
223    /// # Examples
224    ///
225    /// ```
226    /// let builder = async_thread::Builder::new().stack_size(32 * 1024);
227    /// ```
228    pub fn stack_size(self, size: usize) -> Self {
229        Self {
230            imp: self.imp.stack_size(size),
231        }
232    }
233
234    /// Spawns a new thread by taking ownership of the `Builder`, and returns an
235    /// `io::Result` to its `JoinHandle`.
236    ///
237    /// The spawned thread may outlive the caller (unless the caller thread
238    /// is the main thread; the whole process is terminated when the main
239    /// thread finishes). The join handle can be used to block on
240    /// termination of the child thread, including recovering its panics.
241    ///
242    /// For a more complete documentation see `async_thread::spawn`.
243    ///
244    /// # Errors
245    ///
246    /// Unlike the `spawn` free function, this method yields an
247    /// `io::Result` to capture any failure to create the thread at
248    /// the OS level.
249    ///
250    /// # Panics
251    ///
252    /// Panics if a thread name was set and it contained null bytes.
253    ///
254    /// # Examples
255    ///
256    /// ```
257    /// let builder = async_thread::Builder::new();
258    ///
259    /// let handler = builder.spawn(|| {
260    ///     // thread code
261    /// }).unwrap();
262    ///
263    /// handler.join().await.unwrap();
264    /// ```
265    pub fn spawn<F, T>(self, f: F) -> io::Result<JoinHandle<T>>
266    where
267        F: FnOnce() -> T,
268        F: Send + 'static,
269        T: Send + 'static,
270    {
271        let (send, recv) = oneshot::channel();
272        let handle = self.imp.spawn(move || {
273            let _ = send.send(catch_unwind(AssertUnwindSafe(f)));
274        })?;
275
276        Ok(JoinHandle {
277            chan: recv,
278            imp: handle,
279        })
280    }
281}
282
283/// Spawns a new thread, returning a `JoinHandle` for it.
284///
285/// The join handle will implicitly *detach* the child thread upon being
286/// dropped. In this case, the child thread may outlive the parent (unless
287/// the parent thread is the main thread; the whole process is terminated when
288/// the main thread finishes). Additionally, the join handle provides a `join`
289/// method that can be used to join the child thread. If the child thread
290/// panics, `join` will return an `Err` containing the argument given to
291/// `panic`.
292///
293/// This will create a thread using default parameters of `Builder`, if you
294/// want to specify the stack size or the name of the thread, use this API
295/// instead.
296///
297/// As you can see in the signature of `spawn` there are two constraints on
298/// both the closure given to `spawn` and its return value, let's explain them:
299///
300/// - The `'static` constraint means that the closure and its return value
301///   must have a lifetime of the whole program execution. The reason for this
302///   is that threads can `detach` and outlive the lifetime they have been
303///   created in.
304///   Indeed if the thread, and by extension its return value, can outlive their
305///   caller, we need to make sure that they will be valid afterwards, and since
306///   we *can't* know when it will return we need to have them valid as long as
307///   possible, that is until the end of the program, hence the `'static`
308///   lifetime.
309/// - The `Send` constraint is because the closure will need to be passed
310///   *by value* from the thread where it is spawned to the new thread. Its
311///   return value will need to be passed from the new thread to the thread
312///   where it is `join`ed.
313///   As a reminder, the `Send` marker trait expresses that it is safe to be
314///   passed from thread to thread. `Sync` expresses that it is safe to have a
315///   reference be passed from thread to thread.
316///
317/// # Panics
318///
319/// Panics if the OS fails to create a thread; use `Builder::spawn`
320/// to recover from such errors.
321///
322/// # Examples
323///
324/// Creating a thread.
325///
326/// ```
327/// let handler = async_thread::spawn(|| {
328///     // thread code
329/// });
330///
331/// handler.join().await.unwrap();
332/// ```
333///
334/// As mentioned in the std::thread documentation, threads are usually made to
335/// communicate using `channels`, here is how it usually looks.
336///
337/// This example also shows how to use `move`, in order to give ownership
338/// of values to a thread.
339///
340/// ```
341/// use std::sync::mpsc::channel;
342///
343/// let (tx, rx) = channel();
344///
345/// let sender = async_thread::spawn(move || {
346///     tx.send("Hello, thread".to_owned())
347///         .expect("Unable to send on channel");
348/// });
349///
350/// let receiver = async_thread::spawn(move || {
351///     let value = rx.recv().expect("Unable to receive from channel");
352///     println!("{}", value);
353/// });
354///
355/// sender.join().await.expect("The sender thread has panicked");
356/// receiver.join().await.expect("The receiver thread has panicked");
357/// ```
358///
359/// A thread can also return a value through its `JoinHandle`, you can use
360/// this to make asynchronous computations.
361///
362/// ```
363/// let computation = async_thread::spawn(|| {
364///     // Some expensive computation.
365///     42
366/// });
367///
368/// let result = computation.join().await.unwrap();
369/// println!("{}", result);
370/// ```
371pub fn spawn<F, T>(f: F) -> JoinHandle<T>
372where
373    F: FnOnce() -> T,
374    F: Send + 'static,
375    T: Send + 'static,
376{
377    Builder::new().spawn(f).expect("failed to spawn thread")
378}
379
380#[cfg(test)]
381mod tests {
382    #[async_std::test]
383    async fn it_works() {
384        let handle = crate::spawn(|| 5usize);
385        assert_eq!(handle.join().await.map_err(drop), Ok(5));
386    }
387}