@@ -116,10 +116,12 @@ public func withDiscardingTaskGroup<GroupResult>(
116116/// be the case with a ``TaskGroup``.
117117///
118118/// ### 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:
121120///
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
123125/// automatically propagated through all of its child-tasks (and their child
124126/// tasks).
125127///
@@ -158,6 +160,13 @@ public struct DiscardingTaskGroup {
158160 let _: Void ? = try await _taskGroupWaitAll ( group: _group, bodyError: nil )
159161 }
160162
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.
161170 @_alwaysEmitIntoClient
162171 #if SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY
163172 @available ( * , unavailable, message: " Unavailable in task-to-thread concurrency model " , renamed: " addTask(operation:) " )
@@ -184,6 +193,15 @@ public struct DiscardingTaskGroup {
184193 _ = Builtin . createAsyncTaskInGroup ( flags, _group, operation)
185194 }
186195
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`.
187205 @_alwaysEmitIntoClient
188206 #if SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY
189207 @available ( * , unavailable, message: " Unavailable in task-to-thread concurrency model " , renamed: " addTask(operation:) " )
@@ -232,6 +250,12 @@ public struct DiscardingTaskGroup {
232250 _ = Builtin . createAsyncTaskInGroup ( flags, _group, operation)
233251 }
234252
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`.
235259#if SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY
236260 @available ( * , unavailable, message: " Unavailable in task-to-thread concurrency model " , renamed: " addTaskUnlessCancelled(operation:) " )
237261#endif
@@ -262,14 +286,46 @@ public struct DiscardingTaskGroup {
262286#endif
263287 }
264288
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`.
265298 public var isEmpty : Bool {
266299 _taskGroupIsEmpty ( _group)
267300 }
268301
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`
269318 public func cancelAll( ) {
270319 _taskGroupCancelAll ( group: _group)
271320 }
272321
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.
273329 public var isCancelled : Bool {
274330 return _taskGroupIsCancelled ( group: _group)
275331 }
@@ -431,10 +487,26 @@ public func withThrowingDiscardingTaskGroup<GroupResult>(
431487/// be the case with a ``TaskGroup``.
432488///
433489/// ### 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
438510/// automatically propagated through all of its child-tasks (and their child
439511/// tasks).
440512///
@@ -524,14 +596,46 @@ public struct ThrowingDiscardingTaskGroup<Failure: Error> {
524596#endif
525597 }
526598
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`.
527608 public var isEmpty : Bool {
528609 _taskGroupIsEmpty ( _group)
529610 }
530611
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`
531628 public func cancelAll( ) {
532629 _taskGroupCancelAll ( group: _group)
533630 }
534631
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.
535639 public var isCancelled : Bool {
536640 return _taskGroupIsCancelled ( group: _group)
537641 }
0 commit comments