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