terminable_threads/
lib.rs

1use std::any::Any;
2use std::fmt::Debug;
3use std::marker::PhantomData;
4use std::sync::atomic::{self, AtomicBool};
5use std::sync::Arc;
6use std::thread::JoinHandle;
7
8/// A basic thread manager that can signal all threads to terminate / finish early
9///
10/// Note that threads will only terminate if the `Arc<AtomicBool>` flag is used
11#[derive(Debug)]
12pub struct TerminableThreads<T, const N: usize> {
13    pub(crate) _threads: [JoinHandle<T>; N],
14    pub(crate) _terminate_flag: Arc<AtomicBool>,
15}
16
17impl<T, const N: usize> TerminableThreads<T, N> {
18    pub fn build() -> (TerminableThreadsBuilder<T, N>, Arc<AtomicBool>) {
19        TerminableThreadsBuilder::new()
20    }
21
22    /// Signal all threads to terminate and cease operation
23    ///
24    /// ## Note
25    ///
26    /// This does not guarantee all threads will terminate, or can be terminated.
27    ///
28    /// Threads will only terminate if the underlying function checks the flag passed to it.s
29    pub fn terminate(&self) {
30        self._terminate_flag
31            .as_ref()
32            .store(true, atomic::Ordering::SeqCst);
33    }
34
35    /// Join all threads, optionally signalling termination
36    ///
37    /// Optional termination signalling is useful because no termination signal
38    /// will let allow a function to run until it has finished naturally, while
39    /// early termination could stop operations earlier than wanted
40    ///
41    /// # Returns
42    ///
43    /// `[Result<T, Error>; N]`
44    /// 
45    /// An array of length N containing the results of joining each thread
46    pub fn join(
47        self,
48        signal_terminate: bool,
49    ) -> [Result<T, Box<dyn Any + Send + 'static>>; N] {
50        if signal_terminate {
51            self.terminate();
52        }
53
54        self._threads.map(JoinHandle::join)
55    }
56}
57
58/// Basic builder for a terminable thread object
59///
60/// The builder is necessary to provide the termination flag (`Arc<AtomicBool>`)
61/// for threads, that are later provided to the builder, to use.
62#[derive(Debug)]
63pub struct TerminableThreadsBuilder<T, const N: usize> {
64    terminate_flag: Arc<AtomicBool>,
65    _marker: PhantomData<T>,
66}
67
68impl<T, const N: usize> TerminableThreadsBuilder<T, N> {
69    /// Create a new `TeminableThreadBuilder`
70    pub fn new() -> (Self, Arc<AtomicBool>) {
71        let flag = Arc::new(AtomicBool::new(false));
72
73        (
74            Self {
75                terminate_flag: Arc::clone(&flag),
76                _marker: PhantomData,
77            },
78            flag,
79        )
80    }
81
82    /// Transform the builder into a `TerminableThreads<T, N>` struct with the specified threads
83    pub fn build_with_threads(self, threads: [JoinHandle<T>; N]) -> TerminableThreads<T, N> {
84        TerminableThreads {
85            _terminate_flag: self.terminate_flag,
86            _threads: threads,
87        }
88    }
89}