Skip to content

Commit 2f5310b

Browse files
committed
Document the global scope_fifo
1 parent 35c032c commit 2f5310b

File tree

2 files changed

+109
-14
lines changed

2 files changed

+109
-14
lines changed

rayon-core/src/lib.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -339,9 +339,11 @@ impl ThreadPoolBuilder {
339339
/// guaranteed.
340340
///
341341
/// This `breadth_first()` method is now deprecated per [RFC #1],
342-
/// and in the future its effect may be removed.
342+
/// and in the future its effect may be removed. Consider using
343+
/// [`scope_fifo()`] for a similar effect.
343344
///
344-
/// [RFC #1]: https://github.com/rayon-rs/rfcs/pull/1
345+
/// [RFC #1]: https://github.com/rayon-rs/rfcs/blob/master/accepted/rfc0001-scope-scheduling.md
346+
/// [`scope_fifo()`]: fn.scope_fifo.html
345347
#[deprecated(note = "use `scope_fifo` and `spawn_fifo` for similar effect")]
346348
pub fn breadth_first(mut self) -> Self {
347349
self.breadth_first = true;

rayon-core/src/scope/mod.rs

Lines changed: 105 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -148,17 +148,18 @@ struct ScopeBase<'scope> {
148148
/// | (start)
149149
/// |
150150
/// | (scope `s` created)
151-
/// +--------------------+ (task s.1)
152-
/// +-------+ (task s.2) |
153-
/// | | +---+ (task s.1.1)
154-
/// | | | |
155-
/// | | | | (scope `t` created)
156-
/// | | | +----------------+ (task t.1)
157-
/// | | | +---+ (task t.2) |
158-
/// | (mid) | | | | |
159-
/// : | | + <-+------------+ (scope `t` ends)
160-
/// : | | |
161-
/// |<------+------------+---+ (scope `s` ends)
151+
/// +-----------------------------------------------+ (task s.2)
152+
/// +-------+ (task s.1) |
153+
/// | | |
154+
/// | +---+ (task s.1.1) |
155+
/// | | | |
156+
/// | | | (scope `t` created) |
157+
/// | | +----------------+ (task t.2) |
158+
/// | | +---+ (task t.1) | |
159+
/// | (mid) | | | | |
160+
/// : | + <-+------------+ (scope `t` ends) |
161+
/// : | | |
162+
/// |<------+---+-----------------------------------+ (scope `s` ends)
162163
/// |
163164
/// | (end)
164165
/// ```
@@ -171,6 +172,19 @@ struct ScopeBase<'scope> {
171172
/// will be joined before that scope returns, which in turn occurs
172173
/// before the creating task (task `s.1.1` in this case) finishes.
173174
///
175+
/// There is no guaranteed order of execution for spawns in a scope,
176+
/// given that other threads may steal tasks at any time. However, they
177+
/// are generally prioritized in a LIFO order on the thread from which
178+
/// they were spawned. So in this example, absent any stealing, we can
179+
/// expect `s.2` to execute before `s.1`, and `t.2` before `t.1`. Other
180+
/// threads always steal from the other end of the deque, like FIFO
181+
/// order. The idea is that "recent" tasks are most likely to be fresh
182+
/// in the local CPU's cache, while other threads can steal older
183+
/// "stale" tasks. For an alternate approach, consider
184+
/// [`scope_fifo()`] instead.
185+
///
186+
/// [`scope_fifo()`]: fn.scope_fifo.html
187+
///
174188
/// # Accessing stack data
175189
///
176190
/// In general, spawned tasks may access stack data in place that
@@ -282,7 +296,86 @@ where
282296
})
283297
}
284298

285-
/// TODO: like `scope()` but FIFO
299+
/// Create a "fork-join" scope `s` with FIFO order, and invokes the
300+
/// closure with a reference to `s`. This closure can then spawn
301+
/// asynchronous tasks into `s`. Those tasks may run asynchronously with
302+
/// respect to the closure; they may themselves spawn additional tasks
303+
/// into `s`. When the closure returns, it will block until all tasks
304+
/// that have been spawned into `s` complete.
305+
///
306+
/// # Task execution
307+
///
308+
/// Tasks in a `scope_fifo()` run similarly to [`scope()`], but there's a
309+
/// difference in the order of execution. Consider a similar example:
310+
///
311+
/// [`scope()`]: fn.scope.html
312+
///
313+
/// ```rust
314+
/// # use rayon_core as rayon;
315+
/// // point start
316+
/// rayon::scope_fifo(|s| {
317+
/// s.spawn_fifo(|s| { // task s.1
318+
/// s.spawn_fifo(|s| { // task s.1.1
319+
/// rayon::scope_fifo(|t| {
320+
/// t.spawn_fifo(|_| ()); // task t.1
321+
/// t.spawn_fifo(|_| ()); // task t.2
322+
/// });
323+
/// });
324+
/// });
325+
/// s.spawn_fifo(|s| { // task 2
326+
/// });
327+
/// // point mid
328+
/// });
329+
/// // point end
330+
/// ```
331+
///
332+
/// The various tasks that are run will execute roughly like so:
333+
///
334+
/// ```notrust
335+
/// | (start)
336+
/// |
337+
/// | (FIFO scope `s` created)
338+
/// +--------------------+ (task s.1)
339+
/// +-------+ (task s.2) |
340+
/// | | +---+ (task s.1.1)
341+
/// | | | |
342+
/// | | | | (FIFO scope `t` created)
343+
/// | | | +----------------+ (task t.1)
344+
/// | | | +---+ (task t.2) |
345+
/// | (mid) | | | | |
346+
/// : | | + <-+------------+ (scope `t` ends)
347+
/// : | | |
348+
/// |<------+------------+---+ (scope `s` ends)
349+
/// |
350+
/// | (end)
351+
/// ```
352+
///
353+
/// Under `scope_fifo()`, the spawns are prioritized in a FIFO order on
354+
/// the thread from which they were spawned, as opposed to `scope()`'s
355+
/// LIFO. So in this example, we can expect `s.1` to execute before
356+
/// `s.2`, and `t.1` before `t.2`. Other threads also steal tasks in
357+
/// FIFO order, as usual. Overall, this has roughly the same order as
358+
/// the now-deprecated [`breadth_first`] option, except the effect is
359+
/// isolated to a particular scope. If spawns are intermingled from any
360+
/// combination of `scope()` and `scope_fifo()`, or from different
361+
/// threads, their order is only specified with respect to spawns in the
362+
/// same scope and thread.
363+
///
364+
/// For more details on this design, see Rayon [RFC #1].
365+
///
366+
/// [`breadth_first`]: struct.ThreadPoolBuilder.html#method.breadth_first
367+
/// [RFC #1]: https://github.com/rayon-rs/rfcs/blob/master/accepted/rfc0001-scope-scheduling.md
368+
///
369+
/// # Panics
370+
///
371+
/// If a panic occurs, either in the closure given to `scope_fifo()` or
372+
/// in any of the spawned jobs, that panic will be propagated and the
373+
/// call to `scope_fifo()` will panic. If multiple panics occurs, it is
374+
/// non-deterministic which of their panic values will propagate.
375+
/// Regardless, once a task is spawned using `scope.spawn_fifo()`, it
376+
/// will execute, even if the spawning task should later panic.
377+
/// `scope_fifo()` returns once all spawned jobs have completed, and any
378+
/// panics are propagated at that point.
286379
pub fn scope_fifo<'scope, OP, R>(op: OP) -> R
287380
where
288381
OP: for<'s> FnOnce(&'s ScopeFifo<'scope>) -> R + 'scope + Send,

0 commit comments

Comments
 (0)