Skip to main content

orx_parallel/pools/
tasks.rs

1use crate::Scope;
2use orx_meta::queue;
3
4/// Entry point for building a statically typed [`TaskQueue`] to run in parallel
5/// via [`ThreadPool::run_all`].
6///
7/// Since the queue is typed rather than relying on dynamic dispatch, pushed tasks
8/// are stored inline: no object safety, boxing or heap allocation is required.
9///
10/// [`ThreadPool::run_all`]: crate::ThreadPool::run_all
11///
12/// # Example
13///
14/// ```rust
15/// use orx_parallel::*;
16///
17/// let work_for = |n| std::thread::sleep(std::time::Duration::from_millis(n));
18///
19/// let tasks = Tasks::new()
20///     .push(|| {
21///         work_for(90);
22///         println!("t1 completes 4th");
23///     })
24///     .push(|| println!("t2 completes 1st"))
25///     .push(|| {
26///         work_for(10);
27///         println!("t3 completes 2nd");
28///     })
29///     .push(|| {
30///         work_for(50);
31///         println!("t4 completes 3rd");
32///     });
33///
34/// Pool::global().run_all(tasks);
35///
36/// // prints:
37/// // t2 completes 1st
38/// // t3 completes 2nd
39/// // t4 completes 3rd
40/// // t1 completes 4th
41/// ```
42///
43/// Below is a more practical example: computing independent statistics over the same
44/// input concurrently and collecting the results:
45///
46/// ```rust
47/// use orx_parallel::*;
48/// use std::sync::Mutex;
49///
50/// let numbers = [4, 8, 15, 16, 23, 42];
51///
52/// let sum = Mutex::new(0);
53/// let max = Mutex::new(i32::MIN);
54/// let all_positive = Mutex::new(false);
55///
56/// let tasks = tasks![
57///     || *sum.lock().unwrap() = numbers.iter().sum(),
58///     || *max.lock().unwrap() = numbers.iter().copied().max().unwrap(),
59///     || *all_positive.lock().unwrap() = numbers.iter().all(|&x| x > 0),
60/// ];
61///
62/// Pool::global().run_all(tasks);
63///
64/// println!(
65///     "sum={}, max={}, all_positive={}",
66///     sum.into_inner().unwrap(),
67///     max.into_inner().unwrap(),
68///     all_positive.into_inner().unwrap(),
69/// );
70/// ```
71///
72/// Tasks can also be built fluently via [`Tasks::new`] and [`TaskQueue::push`].
73pub struct Tasks;
74
75impl Tasks {
76    /// Creates a new, empty task queue to [`push`] tasks onto.
77    ///
78    /// [`push`]: TaskQueue::push
79    #[allow(clippy::new_ret_no_self)]
80    pub fn new() -> TasksEmpty {
81        TasksEmpty::new()
82    }
83}
84
85/// Macro helper to build a statically typed [`TaskQueue`] with the given tasks.
86///
87/// Returns a task queue (equivalent to chaining [`Tasks::new().push(...)`](Tasks::new)).
88///
89/// # Example
90///
91/// ```rust
92/// use orx_parallel::*;
93/// use std::sync::Mutex;
94///
95/// let numbers = [4, 8, 15, 16, 23, 42];
96///
97/// let sum = Mutex::new(0);
98/// let max = Mutex::new(i32::MIN);
99/// let all_positive = Mutex::new(false);
100///
101/// let tasks = tasks![
102///     || *sum.lock().unwrap() = numbers.iter().sum(),
103///     || *max.lock().unwrap() = numbers.iter().copied().max().unwrap(),
104///     || *all_positive.lock().unwrap() = numbers.iter().all(|&x| x > 0),
105/// ];
106///
107/// Pool::global().run_all(tasks);
108///
109/// assert_eq!(*sum.lock().unwrap(), 108);
110/// assert_eq!(*max.lock().unwrap(), 42);
111/// assert!(*all_positive.lock().unwrap());
112/// ```
113///
114/// See [`adhoc_tasks.rs`](https://github.com/orxfun/orx-parallel/blob/main/examples/adhoc_tasks.rs)
115/// for a complete example of running independent ad-hoc tasks concurrently.
116#[macro_export]
117macro_rules! tasks {
118    () => {
119        $crate::Tasks::new()
120    };
121    ( $( $task:expr ),* $(,)? ) => {
122        $crate::Tasks::new()
123            $( .push($task) )*
124    };
125}
126
127#[queue(TaskQueue; TasksEmpty, TasksSingle, TasksMulti)]
128pub trait ParFun {
129    fn run<'s, 'env, 'scope>(self, scope: impl Scope<'s, 'env, 'scope>)
130    where
131        'scope: 's,
132        'env: 'scope + 's,
133        Self: 'scope + 'env;
134}
135
136impl<F: FnOnce() + Send> ParFun for F {
137    #[inline]
138    fn run<'s, 'env, 'scope>(self, scope: impl Scope<'s, 'env, 'scope>)
139    where
140        'scope: 's,
141        'env: 'scope + 's,
142        Self: 'scope + 'env,
143    {
144        scope.run(self);
145    }
146}