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}