perseus_cli/
thread.rs

1use std::thread::{self, JoinHandle};
2
3/// Spawns a new thread with the given code, or executes it directly if the
4/// environment variable `PERSEUS_CLI_SEQUENTIAL` is set to any valid (Unicode)
5/// value. Multithreading is the default.
6pub fn spawn_thread<F, T>(f: F, sequential: bool) -> ThreadHandle<F, T>
7where
8    F: FnOnce() -> T,
9    F: Send + 'static,
10    T: Send + 'static,
11{
12    if sequential {
13        ThreadHandle {
14            join_handle: None,
15            f: Some(f),
16        }
17    } else {
18        let join_handle = thread::spawn(f);
19        ThreadHandle {
20            join_handle: Some(join_handle),
21            f: None,
22        }
23    }
24}
25
26/// An abstraction over a `JoinHandle` in a multithreaded case, or just a
27/// similar interface that will immediately return if otherwise. This allows the
28/// interfaces for multithreading and single-threading to be basically
29/// identical.
30pub struct ThreadHandle<F, T>
31where
32    F: FnOnce() -> T,
33    F: Send + 'static,
34    T: Send + 'static,
35{
36    /// If multithreaded, this is the join handle.
37    join_handle: Option<JoinHandle<T>>,
38    // If single-threaded, this is the output (it's already been executed).
39    f: Option<F>,
40}
41impl<F, T> ThreadHandle<F, T>
42where
43    F: FnOnce() -> T,
44    F: Send + 'static,
45    T: Send + 'static,
46{
47    /// Waits for the 'thread' to complete, properly if it's multithreaded, or
48    /// by direct execution if it's single-threaded.
49    pub fn join(
50        self,
51    ) -> Result<T, std::boxed::Box<(dyn std::any::Any + std::marker::Send + 'static)>> {
52        if let Some(join_handle) = self.join_handle {
53            join_handle.join()
54        } else if let Some(f) = self.f {
55            let output = f();
56            Ok(output)
57        } else {
58            unreachable!();
59        }
60    }
61}