@@ -116,10 +116,12 @@ public func withDiscardingTaskGroup<GroupResult>(
116
116
/// be the case with a ``TaskGroup``.
117
117
///
118
118
/// ### Cancellation behavior
119
- /// A task group becomes cancelled in one of two ways: when ``cancelAll()`` is
120
- /// invoked on it, or when the ``Task`` running this task group is cancelled.
119
+ /// A discarding task group becomes cancelled in one of the following ways:
121
120
///
122
- /// Since a `TaskGroup` is a structured concurrency primitive, cancellation is
121
+ /// - when ``cancelAll()`` is invoked on it,
122
+ /// - when the ``Task`` running this task group is cancelled.
123
+ ///
124
+ /// Since a `DiscardingTaskGroup` is a structured concurrency primitive, cancellation is
123
125
/// automatically propagated through all of its child-tasks (and their child
124
126
/// tasks).
125
127
///
@@ -158,6 +160,13 @@ public struct DiscardingTaskGroup {
158
160
let _: Void ? = try await _taskGroupWaitAll ( group: _group, bodyError: nil )
159
161
}
160
162
163
+ /// Adds a child task to the group.
164
+ ///
165
+ /// - Parameters:
166
+ /// - priority: The priority of the operation task.
167
+ /// Omit this parameter or pass `.unspecified`
168
+ /// to set the child task's priority to the priority of the group.
169
+ /// - operation: The operation to execute as part of the task group.
161
170
@_alwaysEmitIntoClient
162
171
#if SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY
163
172
@available ( * , unavailable, message: " Unavailable in task-to-thread concurrency model " , renamed: " addTask(operation:) " )
@@ -184,6 +193,15 @@ public struct DiscardingTaskGroup {
184
193
_ = Builtin . createAsyncTaskInGroup ( flags, _group, operation)
185
194
}
186
195
196
+ /// Adds a child task to the group, unless the group has been canceled.
197
+ ///
198
+ /// - Parameters:
199
+ /// - priority: The priority of the operation task.
200
+ /// Omit this parameter or pass `.unspecified`
201
+ /// to set the child task's priority to the priority of the group.
202
+ /// - operation: The operation to execute as part of the task group.
203
+ /// - Returns: `true` if the child task was added to the group;
204
+ /// otherwise `false`.
187
205
@_alwaysEmitIntoClient
188
206
#if SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY
189
207
@available ( * , unavailable, message: " Unavailable in task-to-thread concurrency model " , renamed: " addTask(operation:) " )
@@ -232,6 +250,12 @@ public struct DiscardingTaskGroup {
232
250
_ = Builtin . createAsyncTaskInGroup ( flags, _group, operation)
233
251
}
234
252
253
+ /// Adds a child task to the group, unless the group has been canceled.
254
+ ///
255
+ /// - Parameters:
256
+ /// - operation: The operation to execute as part of the task group.
257
+ /// - Returns: `true` if the child task was added to the group;
258
+ /// otherwise `false`.
235
259
#if SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY
236
260
@available ( * , unavailable, message: " Unavailable in task-to-thread concurrency model " , renamed: " addTaskUnlessCancelled(operation:) " )
237
261
#endif
@@ -262,14 +286,46 @@ public struct DiscardingTaskGroup {
262
286
#endif
263
287
}
264
288
289
+ /// A Boolean value that indicates whether the group has any remaining tasks.
290
+ ///
291
+ /// At the start of the body of a `withDiscardingTaskGroup(of:returning:body:)` call,
292
+ /// the task group is always empty.
293
+ ///
294
+ /// It's guaranteed to be empty when returning from that body
295
+ /// because a task group waits for all child tasks to complete before returning.
296
+ ///
297
+ /// - Returns: `true` if the group has no pending tasks; otherwise `false`.
265
298
public var isEmpty : Bool {
266
299
_taskGroupIsEmpty ( _group)
267
300
}
268
301
302
+ /// Cancel all of the remaining tasks in the group.
303
+ ///
304
+ /// If you add a task to a group after canceling the group,
305
+ /// that task is canceled immediately after being added to the group.
306
+ ///
307
+ /// Immediately cancelled child tasks should therefore cooperatively check for and
308
+ /// react to cancellation, e.g. by throwing an `CancellationError` at their
309
+ /// earliest convenience, or otherwise handling the cancellation.
310
+ ///
311
+ /// There are no restrictions on where you can call this method.
312
+ /// Code inside a child task or even another task can cancel a group,
313
+ /// however one should be very careful to not keep a reference to the
314
+ /// group longer than the `with...TaskGroup(...) { ... }` method body is executing.
315
+ ///
316
+ /// - SeeAlso: `Task.isCancelled`
317
+ /// - SeeAlso: `DiscardingTaskGroup.isCancelled`
269
318
public func cancelAll( ) {
270
319
_taskGroupCancelAll ( group: _group)
271
320
}
272
321
322
+ /// A Boolean value that indicates whether the group was canceled.
323
+ ///
324
+ /// To cancel a group, call the `DiscardingTaskGroup.cancelAll()` method.
325
+ ///
326
+ /// If the task that's currently running this group is canceled,
327
+ /// the group is also implicitly canceled,
328
+ /// which is also reflected in this property's value.
273
329
public var isCancelled : Bool {
274
330
return _taskGroupIsCancelled ( group: _group)
275
331
}
@@ -431,10 +487,26 @@ public func withThrowingDiscardingTaskGroup<GroupResult>(
431
487
/// be the case with a ``TaskGroup``.
432
488
///
433
489
/// ### Cancellation behavior
434
- /// A task group becomes cancelled in one of two ways: when ``cancelAll()`` is
435
- /// invoked on it, or when the ``Task`` running this task group is cancelled.
436
- ///
437
- /// Since a `TaskGroup` is a structured concurrency primitive, cancellation is
490
+ /// A throwing discarding task group becomes cancelled in one of the following ways:
491
+ ///
492
+ /// - when ``cancelAll()`` is invoked on it,
493
+ /// - when an error is thrown out of the `withThrowingDiscardingTaskGroup(...) { }` closure,
494
+ /// - when the ``Task`` running this task group is cancelled.
495
+ ///
496
+ /// But also, and uniquely in *discarding* task groups:
497
+ /// - when *any* of its child tasks throws.
498
+ ///
499
+ /// The group becoming cancelled automatically, and cancelling all of its child tasks,
500
+ /// whenever *any* child task throws an error is a behavior unique to discarding task groups,
501
+ /// because achieving such semantics is not possible otherwise, due to the missing `next()` method
502
+ /// on discarding groups. Accumulating task groups can implement this by manually polling `next()`
503
+ /// and deciding to `cancelAll()` when they decide an error should cause the group to become cancelled,
504
+ /// however a discarding group cannot poll child tasks for results and therefore assumes that child
505
+ /// task throws are an indication of a group wide failure. In order to avoid such behavior,
506
+ /// use a ``DiscardingTaskGroup`` instead of a throwing one, or catch specific errors in
507
+ /// operations submitted using `addTask`
508
+ ///
509
+ /// Since a `ThrowingDiscardingTaskGroup` is a structured concurrency primitive, cancellation is
438
510
/// automatically propagated through all of its child-tasks (and their child
439
511
/// tasks).
440
512
///
@@ -524,14 +596,46 @@ public struct ThrowingDiscardingTaskGroup<Failure: Error> {
524
596
#endif
525
597
}
526
598
599
+ /// A Boolean value that indicates whether the group has any remaining tasks.
600
+ ///
601
+ /// At the start of the body of a `withThrowingDiscardingTaskGroup(of:returning:body:)` call,
602
+ /// the task group is always empty.
603
+ ///
604
+ /// It's guaranteed to be empty when returning from that body
605
+ /// because a task group waits for all child tasks to complete before returning.
606
+ ///
607
+ /// - Returns: `true` if the group has no pending tasks; otherwise `false`.
527
608
public var isEmpty : Bool {
528
609
_taskGroupIsEmpty ( _group)
529
610
}
530
611
612
+ /// Cancel all of the remaining tasks in the group.
613
+ ///
614
+ /// If you add a task to a group after canceling the group,
615
+ /// that task is canceled immediately after being added to the group.
616
+ ///
617
+ /// Immediately cancelled child tasks should therefore cooperatively check for and
618
+ /// react to cancellation, e.g. by throwing an `CancellationError` at their
619
+ /// earliest convenience, or otherwise handling the cancellation.
620
+ ///
621
+ /// There are no restrictions on where you can call this method.
622
+ /// Code inside a child task or even another task can cancel a group,
623
+ /// however one should be very careful to not keep a reference to the
624
+ /// group longer than the `with...TaskGroup(...) { ... }` method body is executing.
625
+ ///
626
+ /// - SeeAlso: `Task.isCancelled`
627
+ /// - SeeAlso: `ThrowingDiscardingTaskGroup.isCancelled`
531
628
public func cancelAll( ) {
532
629
_taskGroupCancelAll ( group: _group)
533
630
}
534
631
632
+ /// A Boolean value that indicates whether the group was canceled.
633
+ ///
634
+ /// To cancel a group, call the `ThrowingDiscardingTaskGroup.cancelAll()` method.
635
+ ///
636
+ /// If the task that's currently running this group is canceled,
637
+ /// the group is also implicitly canceled,
638
+ /// which is also reflected in this property's value.
535
639
public var isCancelled : Bool {
536
640
return _taskGroupIsCancelled ( group: _group)
537
641
}
0 commit comments