Skip to content

Commit c468479

Browse files
ktosoamartini51
andauthored
docs: improve withTaskCancellationHandler docs (#70035)
* docs: improve withTaskCancellationHandler docs * prefer using non-deprecated withCancHandler method * Apply suggestions from code review Co-authored-by: Alex Martini <[email protected]> --------- Co-authored-by: Alex Martini <[email protected]>
1 parent d41d9bd commit c468479

File tree

3 files changed

+45
-21
lines changed

3 files changed

+45
-21
lines changed

stdlib/public/Concurrency/AsyncStreamBuffer.swift

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -253,12 +253,12 @@ extension AsyncStream {
253253
}
254254

255255
func next() async -> Element? {
256-
await withTaskCancellationHandler { [cancel] in
257-
cancel()
258-
} operation: {
256+
await withTaskCancellationHandler {
259257
await withUnsafeContinuation {
260258
next($0)
261259
}
260+
} onCancel: { [cancel] in
261+
cancel()
262262
}
263263
}
264264

@@ -512,12 +512,12 @@ extension AsyncThrowingStream {
512512
}
513513

514514
func next() async throws -> Element? {
515-
try await withTaskCancellationHandler { [cancel] in
516-
cancel()
517-
} operation: {
515+
try await withTaskCancellationHandler {
518516
try await withUnsafeThrowingContinuation {
519517
next($0)
520518
}
519+
} onCancel: { [cancel] in
520+
cancel()
521521
}
522522
}
523523

stdlib/public/Concurrency/SourceCompatibilityShims.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ extension Task where Success == Never, Failure == Never {
7070
handler: @Sendable () -> Void,
7171
operation: () async throws -> T
7272
) async rethrows -> T {
73-
try await withTaskCancellationHandler(handler: handler, operation: operation)
73+
try await withTaskCancellationHandler(operation: operation, onCancel: handler)
7474
}
7575
}
7676

stdlib/public/Concurrency/TaskCancellation.swift

Lines changed: 38 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -22,27 +22,51 @@ import Swift
2222
/// and reacting to it in that the cancellation handler is _always_ and
2323
/// _immediately_ invoked when the task is canceled. For example, even if the
2424
/// operation is running code that never checks for cancellation, a cancellation
25-
/// handler still runs and provides a chance to run some cleanup code.
25+
/// handler still runs and provides a chance to run some cleanup code:
26+
///
27+
/// ```
28+
/// await withTaskCancellationHandler {
29+
/// var sum = 0
30+
/// while condition {
31+
/// sum += 1
32+
/// }
33+
/// return sum
34+
/// } onCancel: {
35+
/// // This onCancel closure might execute concurrently with the operation.
36+
/// condition.cancel()
37+
/// }
38+
/// ```
39+
///
40+
/// ### Execution order and semantics
41+
/// The `operation` closure is always invoked, even when the
42+
/// `withTaskCancellationHandler(operation:onCancel:)` method is called from a task
43+
/// that was already cancelled.
44+
///
45+
/// When `withTaskCancellationHandler(operation:onCancel:)` is used in a task that has already been
46+
/// cancelled, the cancellation handler will be executed
47+
/// immediately before the `operation` closure gets to execute.
48+
///
49+
/// This allows the cancellation handler to set some external "cancelled" flag
50+
/// that the operation may be *atomically* checking for in order to avoid
51+
/// performing any actual work once the operation gets to run.
52+
///
53+
/// The `operation` closure executes on the calling execution context, and doesn't
54+
/// suspend or change execution context unless code contained within the closure
55+
/// does so. In other words, the potential suspension point of the
56+
/// `withTaskCancellationHandler(operation:onCancel:)` never suspends by itself before
57+
/// executing the operation.
58+
///
59+
/// If cancellation occurs while the operation is running, the cancellation
60+
/// handler executes *concurrently* with the operation.
61+
///
62+
/// ### Cancellation handlers and locks
2663
///
2764
/// Cancellation handlers which acquire locks must take care to avoid deadlock.
2865
/// The cancellation handler may be invoked while holding internal locks
2966
/// associated with the task or other tasks. Other operations on the task, such
3067
/// as resuming a continuation, may acquire these same internal locks.
3168
/// Therefore, if a cancellation handler must acquire a lock, other code should
3269
/// not cancel tasks or resume continuations while holding that lock.
33-
///
34-
/// Doesn't check for cancellation, and always executes the passed `operation`.
35-
///
36-
/// The `operation` executes on the calling execution context and does not suspend by itself,
37-
/// unless the code contained within the closure does. If cancellation occurs while the
38-
/// operation is running, the cancellation `handler` will execute *concurrently* with the `operation`.
39-
///
40-
/// ### Already cancelled tasks
41-
/// When `withTaskCancellationHandler` is used in a `Task` that has already been cancelled,
42-
/// the `onCancel` cancellation ``handler`` will be executed immediately before operation gets
43-
/// to execute. This allows the cancellation handler to set some external "cancelled" flag that the
44-
/// operation may be *atomically* checking for in order to avoid performing any actual work once
45-
/// the operation gets to run.
4670
@_unsafeInheritExecutor // the operation runs on the same executor as we start out with
4771
@available(SwiftStdlib 5.1, *)
4872
@backDeployed(before: SwiftStdlib 5.8)

0 commit comments

Comments
 (0)