-
Notifications
You must be signed in to change notification settings - Fork 10.5k
[Docs][Concurrency] Fix documentation about cancellation, copy docs to a few methods #63960
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
5943d60
18bcdba
14d07cc
f8b8ee9
591402b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -116,10 +116,12 @@ public func withDiscardingTaskGroup<GroupResult>( | |
/// be the case with a ``TaskGroup``. | ||
/// | ||
/// ### Cancellation behavior | ||
/// A task group becomes cancelled in one of two ways: when ``cancelAll()`` is | ||
/// invoked on it, or when the ``Task`` running this task group is cancelled. | ||
/// A discarding task group becomes cancelled in one of the following ways: | ||
/// | ||
/// Since a `TaskGroup` is a structured concurrency primitive, cancellation is | ||
/// - when ``cancelAll()`` is invoked on it, | ||
/// - when the ``Task`` running this task group is cancelled. | ||
/// | ||
/// Since a `DiscardingTaskGroup` is a structured concurrency primitive, cancellation is | ||
/// automatically propagated through all of its child-tasks (and their child | ||
/// tasks). | ||
/// | ||
|
@@ -158,6 +160,13 @@ public struct DiscardingTaskGroup { | |
let _: Void? = try await _taskGroupWaitAll(group: _group, bodyError: nil) | ||
} | ||
|
||
/// Adds a child task to the group. | ||
/// | ||
/// - Parameters: | ||
/// - priority: The priority of the operation task. | ||
/// Omit this parameter or pass `.unspecified` | ||
/// to set the child task's priority to the priority of the group. | ||
/// - operation: The operation to execute as part of the task group. | ||
@_alwaysEmitIntoClient | ||
#if SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY | ||
@available(*, unavailable, message: "Unavailable in task-to-thread concurrency model", renamed: "addTask(operation:)") | ||
|
@@ -184,6 +193,15 @@ public struct DiscardingTaskGroup { | |
_ = Builtin.createAsyncTaskInGroup(flags, _group, operation) | ||
} | ||
|
||
/// Adds a child task to the group, unless the group has been canceled. | ||
/// | ||
/// - Parameters: | ||
/// - priority: The priority of the operation task. | ||
/// Omit this parameter or pass `.unspecified` | ||
/// to set the child task's priority to the priority of the group. | ||
/// - operation: The operation to execute as part of the task group. | ||
/// - Returns: `true` if the child task was added to the group; | ||
/// otherwise `false`. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. missed to copy docs on this occurrence of the method |
||
@_alwaysEmitIntoClient | ||
#if SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY | ||
@available(*, unavailable, message: "Unavailable in task-to-thread concurrency model", renamed: "addTask(operation:)") | ||
|
@@ -232,6 +250,12 @@ public struct DiscardingTaskGroup { | |
_ = Builtin.createAsyncTaskInGroup(flags, _group, operation) | ||
} | ||
|
||
/// Adds a child task to the group, unless the group has been canceled. | ||
/// | ||
/// - Parameters: | ||
/// - operation: The operation to execute as part of the task group. | ||
/// - Returns: `true` if the child task was added to the group; | ||
/// otherwise `false`. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. missed to copy docs on this occurrence of the method |
||
#if SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY | ||
@available(*, unavailable, message: "Unavailable in task-to-thread concurrency model", renamed: "addTaskUnlessCancelled(operation:)") | ||
#endif | ||
|
@@ -262,14 +286,46 @@ public struct DiscardingTaskGroup { | |
#endif | ||
} | ||
|
||
/// A Boolean value that indicates whether the group has any remaining tasks. | ||
/// | ||
/// At the start of the body of a `withDiscardingTaskGroup(of:returning:body:)` call, | ||
/// the task group is always empty. | ||
/// | ||
/// It's guaranteed to be empty when returning from that body | ||
/// because a task group waits for all child tasks to complete before returning. | ||
/// | ||
/// - Returns: `true` if the group has no pending tasks; otherwise `false`. | ||
public var isEmpty: Bool { | ||
_taskGroupIsEmpty(_group) | ||
} | ||
|
||
/// Cancel all of the remaining tasks in the group. | ||
/// | ||
/// If you add a task to a group after canceling the group, | ||
/// that task is canceled immediately after being added to the group. | ||
/// | ||
/// Immediately cancelled child tasks should therefore cooperatively check for and | ||
/// react to cancellation, e.g. by throwing an `CancellationError` at their | ||
/// earliest convenience, or otherwise handling the cancellation. | ||
/// | ||
/// There are no restrictions on where you can call this method. | ||
/// Code inside a child task or even another task can cancel a group, | ||
/// however one should be very careful to not keep a reference to the | ||
/// group longer than the `with...TaskGroup(...) { ... }` method body is executing. | ||
/// | ||
/// - SeeAlso: `Task.isCancelled` | ||
/// - SeeAlso: `DiscardingTaskGroup.isCancelled` | ||
public func cancelAll() { | ||
_taskGroupCancelAll(group: _group) | ||
} | ||
|
||
/// A Boolean value that indicates whether the group was canceled. | ||
/// | ||
/// To cancel a group, call the `DiscardingTaskGroup.cancelAll()` method. | ||
/// | ||
/// If the task that's currently running this group is canceled, | ||
/// the group is also implicitly canceled, | ||
/// which is also reflected in this property's value. | ||
public var isCancelled: Bool { | ||
return _taskGroupIsCancelled(group: _group) | ||
} | ||
|
@@ -431,10 +487,26 @@ public func withThrowingDiscardingTaskGroup<GroupResult>( | |
/// be the case with a ``TaskGroup``. | ||
/// | ||
/// ### Cancellation behavior | ||
/// A task group becomes cancelled in one of two ways: when ``cancelAll()`` is | ||
/// invoked on it, or when the ``Task`` running this task group is cancelled. | ||
/// | ||
/// Since a `TaskGroup` is a structured concurrency primitive, cancellation is | ||
/// A throwing discarding task group becomes cancelled in one of the following ways: | ||
/// | ||
/// - when ``cancelAll()`` is invoked on it, | ||
/// - when an error is thrown out of the `withThrowingDiscardingTaskGroup(...) { }` closure, | ||
/// - when the ``Task`` running this task group is cancelled. | ||
/// | ||
/// But also, and uniquely in *discarding* task groups: | ||
/// - when *any* of its child tasks throws. | ||
/// | ||
/// The group becoming cancelled automatically, and cancelling all of its child tasks, | ||
/// whenever *any* child task throws an error is a behavior unique to discarding task groups, | ||
/// because achieving such semantics is not possible otherwise, due to the missing `next()` method | ||
/// on discarding groups. Accumulating task groups can implement this by manually polling `next()` | ||
/// and deciding to `cancelAll()` when they decide an error should cause the group to become cancelled, | ||
/// however a discarding group cannot poll child tasks for results and therefore assumes that child | ||
/// task throws are an indication of a group wide failure. In order to avoid such behavior, | ||
/// use a ``DiscardingTaskGroup`` instead of a throwing one, or catch specific errors in | ||
/// operations submitted using `addTask` | ||
/// | ||
/// Since a `ThrowingDiscardingTaskGroup` is a structured concurrency primitive, cancellation is | ||
/// automatically propagated through all of its child-tasks (and their child | ||
/// tasks). | ||
/// | ||
|
@@ -524,14 +596,46 @@ public struct ThrowingDiscardingTaskGroup<Failure: Error> { | |
#endif | ||
} | ||
|
||
/// A Boolean value that indicates whether the group has any remaining tasks. | ||
/// | ||
/// At the start of the body of a `withThrowingDiscardingTaskGroup(of:returning:body:)` call, | ||
/// the task group is always empty. | ||
/// | ||
/// It's guaranteed to be empty when returning from that body | ||
/// because a task group waits for all child tasks to complete before returning. | ||
/// | ||
/// - Returns: `true` if the group has no pending tasks; otherwise `false`. | ||
public var isEmpty: Bool { | ||
_taskGroupIsEmpty(_group) | ||
} | ||
|
||
/// Cancel all of the remaining tasks in the group. | ||
/// | ||
/// If you add a task to a group after canceling the group, | ||
/// that task is canceled immediately after being added to the group. | ||
/// | ||
/// Immediately cancelled child tasks should therefore cooperatively check for and | ||
/// react to cancellation, e.g. by throwing an `CancellationError` at their | ||
/// earliest convenience, or otherwise handling the cancellation. | ||
/// | ||
/// There are no restrictions on where you can call this method. | ||
/// Code inside a child task or even another task can cancel a group, | ||
/// however one should be very careful to not keep a reference to the | ||
/// group longer than the `with...TaskGroup(...) { ... }` method body is executing. | ||
/// | ||
/// - SeeAlso: `Task.isCancelled` | ||
/// - SeeAlso: `ThrowingDiscardingTaskGroup.isCancelled` | ||
public func cancelAll() { | ||
_taskGroupCancelAll(group: _group) | ||
} | ||
|
||
/// A Boolean value that indicates whether the group was canceled. | ||
/// | ||
/// To cancel a group, call the `ThrowingDiscardingTaskGroup.cancelAll()` method. | ||
/// | ||
/// If the task that's currently running this group is canceled, | ||
/// the group is also implicitly canceled, | ||
/// which is also reflected in this property's value. | ||
public var isCancelled: Bool { | ||
return _taskGroupIsCancelled(group: _group) | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -218,15 +218,11 @@ public func withThrowingTaskGroup<ChildTaskResult, GroupResult>( | |
/// Tasks added to a task group execute concurrently, and may be scheduled in | ||
/// any order. | ||
/// | ||
/// ### Discarding behavior | ||
/// A discarding task group eagerly discards and releases its child tasks as | ||
/// soon as they complete. This allows for the efficient releasing of memory used | ||
/// by those tasks, which are not retained for future `next()` calls, as would | ||
/// be the case with a ``TaskGroup``. | ||
/// | ||
/// ### Cancellation behavior | ||
/// A task group becomes cancelled in one of two ways: when ``cancelAll()`` is | ||
/// invoked on it, or when the ``Task`` running this task group is cancelled. | ||
/// A task group becomes cancelled in one of the following ways: | ||
/// | ||
/// - when ``cancelAll()`` is invoked on it, | ||
/// - when the ``Task`` running this task group is cancelled. | ||
/// | ||
/// Since a `TaskGroup` is a structured concurrency primitive, cancellation is | ||
/// automatically propagated through all of its child-tasks (and their child | ||
|
@@ -262,7 +258,7 @@ public struct TaskGroup<ChildTaskResult: Sendable> { | |
/// Adds a child task to the group. | ||
/// | ||
/// - Parameters: | ||
/// - overridingPriority: The priority of the operation task. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. wrong param name |
||
/// - priority: The priority of the operation task. | ||
/// Omit this parameter or pass `.unspecified` | ||
/// to set the child task's priority to the priority of the group. | ||
/// - operation: The operation to execute as part of the task group. | ||
|
@@ -336,7 +332,7 @@ public struct TaskGroup<ChildTaskResult: Sendable> { | |
fatalError("Unsupported Swift compiler") | ||
#endif | ||
} | ||
#else | ||
#else // if SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY | ||
@available(SwiftStdlib 5.7, *) | ||
@available(*, unavailable, message: "Unavailable in task-to-thread concurrency model", renamed: "addTask(operation:)") | ||
public mutating func addTask( | ||
|
@@ -491,15 +487,17 @@ public struct TaskGroup<ChildTaskResult: Sendable> { | |
|
||
/// Cancel all of the remaining tasks in the group. | ||
/// | ||
/// After cancellation, | ||
/// any new results from the tasks in this group | ||
/// are silently discarded. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This isn't true, cancelled tasks run as usual and can be collected. FYI @amartini51 in case we used this assumption somewhere else in some docs? |
||
/// | ||
/// If you add a task to a group after canceling the group, | ||
/// that task is canceled immediately after being added to the group. | ||
/// | ||
/// This method can only be called by the parent task that created the task | ||
/// group. | ||
/// Immediately cancelled child tasks should therefore cooperatively check for and | ||
/// react to cancellation, e.g. by throwing an `CancellationError` at their | ||
/// earliest convenience, or otherwise handling the cancellation. | ||
/// | ||
/// There are no restrictions on where you can call this method. | ||
/// Code inside a child task or even another task can cancel a group, | ||
/// however one should be very careful to not keep a reference to the | ||
/// group longer than the `with...TaskGroup(...) { ... }` method body is executing. | ||
/// | ||
/// - SeeAlso: `Task.isCancelled` | ||
/// - SeeAlso: `TaskGroup.isCancelled` | ||
|
@@ -558,17 +556,14 @@ extension TaskGroup: Sendable { } | |
/// Tasks added to a task group execute concurrently, and may be scheduled in | ||
/// any order. | ||
/// | ||
/// ### Discarding behavior | ||
/// A discarding task group eagerly discards and releases its child tasks as | ||
/// soon as they complete. This allows for the efficient releasing of memory used | ||
/// by those tasks, which are not retained for future `next()` calls, as would | ||
/// be the case with a ``TaskGroup``. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Wrong group to have those docs |
||
/// | ||
/// ### Cancellation behavior | ||
/// A task group becomes cancelled in one of two ways: when ``cancelAll()`` is | ||
/// invoked on it, or when the ``Task`` running this task group is cancelled. | ||
/// A task group becomes cancelled in one of the following ways: | ||
/// | ||
/// Since a `TaskGroup` is a structured concurrency primitive, cancellation is | ||
/// - when ``cancelAll()`` is invoked on it, | ||
/// - when an error is thrown out of the `withThrowingTaskGroup(...) { }` closure, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. throwing groups have this semantic, non throwing dont. Technically they could, but the APIs are fully non throwing today in those, including the with closure |
||
/// - when the ``Task`` running this task group is cancelled. | ||
/// | ||
/// Since a `ThrowingTaskGroup` is a structured concurrency primitive, cancellation is | ||
/// automatically propagated through all of its child-tasks (and their child | ||
/// tasks). | ||
/// | ||
|
@@ -906,6 +901,7 @@ public struct ThrowingTaskGroup<ChildTaskResult: Sendable, Failure: Error> { | |
/// | ||
/// At the start of the body of a `withThrowingTaskGroup(of:returning:body:)` call, | ||
/// the task group is always empty. | ||
/// | ||
/// It's guaranteed to be empty when returning from that body | ||
/// because a task group waits for all child tasks to complete before returning. | ||
/// | ||
|
@@ -916,15 +912,17 @@ public struct ThrowingTaskGroup<ChildTaskResult: Sendable, Failure: Error> { | |
|
||
/// Cancel all of the remaining tasks in the group. | ||
/// | ||
/// After cancellation, | ||
/// any new results or errors from the tasks in this group | ||
/// are silently discarded. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ditto again wrong cancellation semantics assumption, also removed and clarified below. |
||
/// | ||
/// If you add a task to a group after canceling the group, | ||
/// that task is canceled immediately after being added to the group. | ||
/// | ||
/// Immediately cancelled child tasks should therefore cooperatively check for and | ||
/// react to cancellation, e.g. by throwing an `CancellationError` at their | ||
/// earliest convenience, or otherwise handling the cancellation. | ||
/// | ||
/// There are no restrictions on where you can call this method. | ||
/// Code inside a child task or even another task can cancel a group. | ||
/// Code inside a child task or even another task can cancel a group, | ||
/// however one should be very careful to not keep a reference to the | ||
/// group longer than the `with...TaskGroup(...) { ... }` method body is executing. | ||
/// | ||
/// - SeeAlso: `Task.isCancelled` | ||
/// - SeeAlso: `ThrowingTaskGroup.isCancelled` | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
missed to copy docs on this occurrence of the method
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it was intentional that we didn't copy/paste the documentation onto deprecated versions.