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