orx_parallel/pools/pool_impl/
basic.rs1use crate::NumThreads;
2use crate::pools::ThreadPool;
3use crate::pools::env::max_num_threads_by_env_and_resource;
4use crate::pools::scope::Scope;
5use core::num::NonZeroUsize;
6use std::any::Any;
7use std::boxed::Box;
8use std::collections::VecDeque;
9use std::marker::PhantomData;
10use std::panic::{AssertUnwindSafe, catch_unwind, resume_unwind};
11use std::sync::atomic::{AtomicUsize, Ordering};
12use std::sync::{Arc, Condvar, Mutex};
13use std::thread;
14use std::vec::Vec;
15
16struct Inner {
17 shared: Arc<WorkerShared>,
18 workers: Mutex<Vec<std::thread::JoinHandle<()>>>,
19}
20
21struct WorkerShared {
22 state: Mutex<WorkerState>,
23 cv: Condvar,
24}
25
26struct WorkerState {
27 shutdown: bool,
28 queue: VecDeque<Task>,
29}
30
31impl Drop for Inner {
32 fn drop(&mut self) {
33 {
34 let mut state = self.shared.state.lock().expect("poisoned pool lock");
35 state.shutdown = true;
36 while let Some(task) = state.queue.pop_front() {
37 unsafe { task.drop() };
38 }
39 }
40 self.shared.cv.notify_all();
41
42 let mut workers = self.workers.lock().expect("poisoned workers lock");
43 for worker in workers.drain(..) {
44 let _ = worker.join();
45 }
46 }
47}
48
49struct ScopeRuntime {
50 pending: AtomicUsize,
51 completion_lock: Mutex<()>,
52 completion_cv: Condvar,
53 panic: Mutex<Option<Box<dyn Any + Send>>>,
54}
55
56impl ScopeRuntime {
57 fn new() -> Self {
58 Self {
59 pending: AtomicUsize::new(0),
60 completion_lock: Mutex::new(()),
61 completion_cv: Condvar::new(),
62 panic: Mutex::new(None),
63 }
64 }
65
66 fn begin_task(&self) {
67 self.pending.fetch_add(1, Ordering::AcqRel);
68 }
69
70 fn complete_task(&self) {
71 let guard = self
75 .completion_lock
76 .lock()
77 .expect("poisoned scope completion lock");
78 let prev = self.pending.fetch_sub(1, Ordering::AcqRel);
79 if prev == 1 {
80 self.completion_cv.notify_all();
81 }
82 drop(guard);
83 }
84
85 fn wait_for_completion(&self) {
86 let mut guard = self
87 .completion_lock
88 .lock()
89 .expect("poisoned scope completion lock");
90 while self.pending.load(Ordering::Acquire) != 0 {
91 guard = self
92 .completion_cv
93 .wait(guard)
94 .expect("poisoned scope completion lock");
95 }
96 }
97
98 fn record_panic(&self, err: Box<dyn Any + Send>) {
99 let mut panic_slot = self.panic.lock().expect("poisoned scope panic lock");
100 if panic_slot.is_none() {
101 *panic_slot = Some(err);
102 }
103 }
104
105 fn take_panic(&self) -> Option<Box<dyn Any + Send>> {
106 self.panic.lock().expect("poisoned scope panic lock").take()
107 }
108}
109
110pub struct ScopeRef<'env> {
111 shared: *const WorkerShared,
112 runtime: Arc<ScopeRuntime>,
113 _marker: PhantomData<&'env ()>,
114}
115
116impl<'env> ScopeRef<'env> {
117 fn shared(&self) -> &WorkerShared {
118 unsafe { &*self.shared }
119 }
120
121 fn runtime(&self) -> &ScopeRuntime {
122 &self.runtime
123 }
124}
125
126struct Task {
127 data: *mut (),
128 run_fn: unsafe fn(*mut ()),
129 drop_fn: unsafe fn(*mut ()),
130 runtime: Arc<ScopeRuntime>,
131}
132
133unsafe impl Send for Task {}
134
135impl Task {
136 fn new<W>(work: W, runtime: Arc<ScopeRuntime>) -> Self
137 where
138 W: FnOnce() + Send,
139 {
140 unsafe fn run_impl<W>(data: *mut ())
141 where
142 W: FnOnce() + Send,
143 {
144 let work = unsafe { Box::from_raw(data as *mut W) };
145 (*work)();
146 }
147
148 unsafe fn drop_impl<W>(data: *mut ())
149 where
150 W: FnOnce() + Send,
151 {
152 drop(unsafe { Box::from_raw(data as *mut W) });
153 }
154
155 let boxed = Box::new(work);
156 Self {
157 data: Box::into_raw(boxed) as *mut (),
158 run_fn: run_impl::<W>,
159 drop_fn: drop_impl::<W>,
160 runtime,
161 }
162 }
163
164 unsafe fn run(self) {
165 unsafe { (self.run_fn)(self.data) };
166 }
167
168 unsafe fn drop(self) {
169 unsafe { (self.drop_fn)(self.data) };
170 }
171}
172
173fn worker_loop(shared: Arc<WorkerShared>) {
174 loop {
175 let task = {
176 let mut state = shared.state.lock().expect("poisoned pool lock");
177 loop {
178 if state.shutdown {
179 return;
180 }
181
182 if let Some(task) = state.queue.pop_front() {
183 break task;
184 }
185
186 state = shared.cv.wait(state).expect("poisoned pool lock");
187 }
188 };
189
190 let runtime = Arc::clone(&task.runtime);
191 let result = catch_unwind(AssertUnwindSafe(|| unsafe { task.run() }));
192 if let Err(err) = result {
193 runtime.record_panic(err);
194 }
195 runtime.complete_task();
196 }
197}
198
199#[derive(Clone)]
214pub struct BasicPool {
215 max_num_threads: NonZeroUsize,
216 inner: Arc<Inner>,
217}
218
219impl Default for BasicPool {
220 fn default() -> Self {
221 Self::new(NumThreads::Auto)
222 }
223}
224
225impl BasicPool {
226 pub fn new(num_threads: impl Into<NumThreads>) -> Self {
232 let num_threads = match num_threads.into() {
233 NumThreads::Auto => max_num_threads_by_env_and_resource(),
234 NumThreads::Max(n) => max_num_threads_by_env_and_resource().min(n),
235 };
236
237 let shared = Arc::new(WorkerShared {
238 state: Mutex::new(WorkerState {
239 shutdown: false,
240 queue: VecDeque::new(),
241 }),
242 cv: Condvar::new(),
243 });
244
245 let nt: usize = num_threads.into();
246 let mut workers = Vec::with_capacity(nt);
247 for _ in 0..nt {
248 let shared_cloned = Arc::clone(&shared);
249 workers.push(thread::spawn(move || worker_loop(shared_cloned)));
250 }
251
252 Self {
253 max_num_threads: num_threads,
254 inner: Arc::new(Inner {
255 shared,
256 workers: Mutex::new(workers),
257 }),
258 }
259 }
260
261 fn scope_impl<'env, 'scope, F>(&'env self, f: F)
262 where
263 'env: 'scope,
264 for<'s> F: FnOnce(&'s ScopeRef<'env>) + Send,
265 {
266 let runtime = Arc::new(ScopeRuntime::new());
267
268 let scope_ref = ScopeRef {
269 shared: Arc::as_ptr(&self.inner.shared),
270 runtime: Arc::clone(&runtime),
271 _marker: PhantomData,
272 };
273
274 let user_result = catch_unwind(AssertUnwindSafe(|| f(&scope_ref)));
275
276 runtime.wait_for_completion();
277
278 if let Err(err) = user_result {
279 resume_unwind(err);
280 }
281
282 if let Some(err) = runtime.take_panic() {
283 resume_unwind(err);
284 }
285 }
286}
287
288impl<'s, 'env, 'scope> Scope<'s, 'env, 'scope> for &'s ScopeRef<'env> {
289 fn run<W>(self, work: W)
290 where
291 'scope: 's,
292 'env: 'scope + 's,
293 W: FnOnce() + Send + 'scope + 'env,
294 {
295 self.runtime().begin_task();
296
297 let task = Task::new(work, Arc::clone(&self.runtime));
298 {
299 let mut state = self.shared().state.lock().expect("poisoned pool lock");
300 state.queue.push_back(task);
301 }
302 self.shared().cv.notify_one();
303 }
304}
305
306impl ThreadPool for BasicPool {
307 type ScopeRef<'s, 'env, 'scope>
308 = &'s ScopeRef<'env>
309 where
310 'scope: 's,
311 'env: 'scope + 's;
312
313 fn max_num_threads(&self) -> NonZeroUsize {
314 self.max_num_threads
315 }
316
317 fn scope<'env, 'scope, F>(&'env self, f: F)
318 where
319 'env: 'scope,
320 for<'s> F: FnOnce(&'s ScopeRef<'env>) + Send,
321 {
322 self.scope_impl(f)
323 }
324}
325
326impl ThreadPool for &BasicPool {
327 type ScopeRef<'s, 'env, 'scope>
328 = &'s ScopeRef<'env>
329 where
330 'scope: 's,
331 'env: 'scope + 's;
332
333 fn max_num_threads(&self) -> NonZeroUsize {
334 self.max_num_threads
335 }
336
337 fn scope<'env, 'scope, F>(&'env self, f: F)
338 where
339 'env: 'scope,
340 for<'s> F: FnOnce(&'s ScopeRef<'env>) + Send,
341 {
342 (*self).scope_impl(f)
343 }
344}
345
346impl ThreadPool for &mut BasicPool {
347 type ScopeRef<'s, 'env, 'scope>
348 = &'s ScopeRef<'env>
349 where
350 'scope: 's,
351 'env: 'scope + 's;
352
353 fn max_num_threads(&self) -> NonZeroUsize {
354 self.max_num_threads
355 }
356
357 fn scope<'env, 'scope, F>(&'env self, f: F)
358 where
359 'env: 'scope,
360 for<'s> F: FnOnce(&'s ScopeRef<'env>) + Send,
361 {
362 (*self).scope_impl(f)
363 }
364}