@@ -148,17 +148,18 @@ struct ScopeBase<'scope> {
148
148
/// | (start)
149
149
/// |
150
150
/// | (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)
162
163
/// |
163
164
/// | (end)
164
165
/// ```
@@ -171,6 +172,19 @@ struct ScopeBase<'scope> {
171
172
/// will be joined before that scope returns, which in turn occurs
172
173
/// before the creating task (task `s.1.1` in this case) finishes.
173
174
///
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
+ ///
174
188
/// # Accessing stack data
175
189
///
176
190
/// In general, spawned tasks may access stack data in place that
@@ -282,7 +296,86 @@ where
282
296
} )
283
297
}
284
298
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.
286
379
pub fn scope_fifo < ' scope , OP , R > ( op : OP ) -> R
287
380
where
288
381
OP : for < ' s > FnOnce ( & ' s ScopeFifo < ' scope > ) -> R + ' scope + Send ,
0 commit comments