]>
Commit | Line | Data |
---|---|---|
6a06907d XL |
1 | use crate::job::*; |
2 | use crate::registry::Registry; | |
3 | use crate::unwind; | |
2c00a5a8 XL |
4 | use std::mem; |
5 | use std::sync::Arc; | |
2c00a5a8 XL |
6 | |
7 | /// Fires off a task into the Rayon threadpool in the "static" or | |
8 | /// "global" scope. Just like a standard thread, this task is not | |
9 | /// tied to the current stack frame, and hence it cannot hold any | |
10 | /// references other than those with `'static` lifetime. If you want | |
11 | /// to spawn a task that references stack data, use [the `scope()` | |
12 | /// function][scope] to create a scope. | |
13 | /// | |
14 | /// [scope]: fn.scope.html | |
15 | /// | |
16 | /// Since tasks spawned with this function cannot hold references into | |
17 | /// the enclosing stack frame, you almost certainly want to use a | |
18 | /// `move` closure as their argument (otherwise, the closure will | |
19 | /// typically hold references to any variables from the enclosing | |
20 | /// function that you happen to use). | |
21 | /// | |
22 | /// This API assumes that the closure is executed purely for its | |
23 | /// side-effects (i.e., it might send messages, modify data protected | |
923072b8 | 24 | /// by a mutex, or some such thing). |
2c00a5a8 | 25 | /// |
e74abb32 XL |
26 | /// There is no guaranteed order of execution for spawns, given that |
27 | /// other threads may steal tasks at any time. However, they are | |
28 | /// generally prioritized in a LIFO order on the thread from which | |
29 | /// they were spawned. Other threads always steal from the other end of | |
30 | /// the deque, like FIFO order. The idea is that "recent" tasks are | |
31 | /// most likely to be fresh in the local CPU's cache, while other | |
32 | /// threads can steal older "stale" tasks. For an alternate approach, | |
33 | /// consider [`spawn_fifo()`] instead. | |
34 | /// | |
35 | /// [`spawn_fifo()`]: fn.spawn_fifo.html | |
36 | /// | |
2c00a5a8 XL |
37 | /// # Panic handling |
38 | /// | |
39 | /// If this closure should panic, the resulting panic will be | |
40 | /// propagated to the panic handler registered in the `ThreadPoolBuilder`, | |
41 | /// if any. See [`ThreadPoolBuilder::panic_handler()`][ph] for more | |
42 | /// details. | |
43 | /// | |
44 | /// [ph]: struct.ThreadPoolBuilder.html#method.panic_handler | |
45 | /// | |
46 | /// # Examples | |
47 | /// | |
48 | /// This code creates a Rayon task that increments a global counter. | |
49 | /// | |
50 | /// ```rust | |
51 | /// # use rayon_core as rayon; | |
52 | /// use std::sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT}; | |
53 | /// | |
54 | /// static GLOBAL_COUNTER: AtomicUsize = ATOMIC_USIZE_INIT; | |
55 | /// | |
56 | /// rayon::spawn(move || { | |
57 | /// GLOBAL_COUNTER.fetch_add(1, Ordering::SeqCst); | |
58 | /// }); | |
59 | /// ``` | |
60 | pub fn spawn<F>(func: F) | |
532ac7d7 XL |
61 | where |
62 | F: FnOnce() + Send + 'static, | |
2c00a5a8 XL |
63 | { |
64 | // We assert that current registry has not terminated. | |
65 | unsafe { spawn_in(func, &Registry::current()) } | |
66 | } | |
67 | ||
923072b8 | 68 | /// Spawns an asynchronous job in `registry.` |
2c00a5a8 XL |
69 | /// |
70 | /// Unsafe because `registry` must not yet have terminated. | |
e74abb32 XL |
71 | pub(super) unsafe fn spawn_in<F>(func: F, registry: &Arc<Registry>) |
72 | where | |
73 | F: FnOnce() + Send + 'static, | |
74 | { | |
75 | // We assert that this does not hold any references (we know | |
76 | // this because of the `'static` bound in the inferface); | |
77 | // moreover, we assert that the code below is not supposed to | |
78 | // be able to panic, and hence the data won't leak but will be | |
79 | // enqueued into some deque for later execution. | |
80 | let abort_guard = unwind::AbortIfPanic; // just in case we are wrong, and code CAN panic | |
81 | let job_ref = spawn_job(func, registry); | |
82 | registry.inject_or_push(job_ref); | |
83 | mem::forget(abort_guard); | |
84 | } | |
85 | ||
86 | unsafe fn spawn_job<F>(func: F, registry: &Arc<Registry>) -> JobRef | |
532ac7d7 XL |
87 | where |
88 | F: FnOnce() + Send + 'static, | |
2c00a5a8 XL |
89 | { |
90 | // Ensure that registry cannot terminate until this job has | |
91 | // executed. This ref is decremented at the (*) below. | |
92 | registry.increment_terminate_count(); | |
93 | ||
e74abb32 | 94 | Box::new(HeapJob::new(0, { |
923072b8 | 95 | let registry = Arc::clone(registry); |
2c00a5a8 XL |
96 | move || { |
97 | match unwind::halt_unwinding(func) { | |
532ac7d7 | 98 | Ok(()) => {} |
2c00a5a8 XL |
99 | Err(err) => { |
100 | registry.handle_panic(err); | |
101 | } | |
102 | } | |
103 | registry.terminate(); // (*) permit registry to terminate now | |
104 | } | |
e74abb32 XL |
105 | })) |
106 | .as_job_ref() | |
107 | } | |
108 | ||
109 | /// Fires off a task into the Rayon threadpool in the "static" or | |
110 | /// "global" scope. Just like a standard thread, this task is not | |
111 | /// tied to the current stack frame, and hence it cannot hold any | |
112 | /// references other than those with `'static` lifetime. If you want | |
113 | /// to spawn a task that references stack data, use [the `scope_fifo()` | |
114 | /// function](fn.scope_fifo.html) to create a scope. | |
115 | /// | |
116 | /// The behavior is essentially the same as [the `spawn` | |
117 | /// function](fn.spawn.html), except that calls from the same thread | |
118 | /// will be prioritized in FIFO order. This is similar to the now- | |
119 | /// deprecated [`breadth_first`] option, except the effect is isolated | |
120 | /// to relative `spawn_fifo` calls, not all threadpool tasks. | |
121 | /// | |
122 | /// For more details on this design, see Rayon [RFC #1]. | |
123 | /// | |
124 | /// [`breadth_first`]: struct.ThreadPoolBuilder.html#method.breadth_first | |
125 | /// [RFC #1]: https://github.com/rayon-rs/rfcs/blob/master/accepted/rfc0001-scope-scheduling.md | |
126 | /// | |
127 | /// # Panic handling | |
128 | /// | |
129 | /// If this closure should panic, the resulting panic will be | |
130 | /// propagated to the panic handler registered in the `ThreadPoolBuilder`, | |
131 | /// if any. See [`ThreadPoolBuilder::panic_handler()`][ph] for more | |
132 | /// details. | |
133 | /// | |
134 | /// [ph]: struct.ThreadPoolBuilder.html#method.panic_handler | |
135 | pub fn spawn_fifo<F>(func: F) | |
136 | where | |
137 | F: FnOnce() + Send + 'static, | |
138 | { | |
139 | // We assert that current registry has not terminated. | |
140 | unsafe { spawn_fifo_in(func, &Registry::current()) } | |
141 | } | |
2c00a5a8 | 142 | |
923072b8 | 143 | /// Spawns an asynchronous FIFO job in `registry.` |
e74abb32 XL |
144 | /// |
145 | /// Unsafe because `registry` must not yet have terminated. | |
146 | pub(super) unsafe fn spawn_fifo_in<F>(func: F, registry: &Arc<Registry>) | |
147 | where | |
148 | F: FnOnce() + Send + 'static, | |
149 | { | |
2c00a5a8 XL |
150 | // We assert that this does not hold any references (we know |
151 | // this because of the `'static` bound in the inferface); | |
152 | // moreover, we assert that the code below is not supposed to | |
153 | // be able to panic, and hence the data won't leak but will be | |
154 | // enqueued into some deque for later execution. | |
155 | let abort_guard = unwind::AbortIfPanic; // just in case we are wrong, and code CAN panic | |
e74abb32 XL |
156 | let job_ref = spawn_job(func, registry); |
157 | ||
158 | // If we're in the pool, use our thread's private fifo for this thread to execute | |
159 | // in a locally-FIFO order. Otherwise, just use the pool's global injector. | |
160 | match registry.current_thread() { | |
161 | Some(worker) => worker.push_fifo(job_ref), | |
162 | None => registry.inject(&[job_ref]), | |
163 | } | |
2c00a5a8 XL |
164 | mem::forget(abort_guard); |
165 | } | |
166 | ||
167 | #[cfg(test)] | |
168 | mod test; |