File tree Expand file tree Collapse file tree 2 files changed +18
-7
lines changed Expand file tree Collapse file tree 2 files changed +18
-7
lines changed Original file line number Diff line number Diff line change @@ -215,16 +215,23 @@ package func withTimeout<T: Sendable>(
215
215
216
216
let tasks = mutableTasks
217
217
218
+ defer {
219
+ // Be extra careful and ensure that we don't leave `bodyTask` or `timeoutTask` running when `withTimeout` finishes,
220
+ // eg. if `withTaskPriorityChangedHandler` adds some behavior that never executes `body` if the task gets cancelled.
221
+ for task in tasks {
222
+ task. cancel ( )
223
+ }
224
+ }
225
+
218
226
return try await withTaskPriorityChangedHandler ( initialPriority: priority) {
219
227
for try await value in stream {
220
228
return value
221
229
}
222
- // The only reason for the loop above to terminate is if the Task got cancelled or if the continuation finishes
230
+ // The only reason for the loop above to terminate is if the Task got cancelled or if the stream finishes
223
231
// (which it never does).
224
232
if Task . isCancelled {
225
- for task in tasks {
226
- task. cancel ( )
227
- }
233
+ // Throwing a `CancellationError` will make us return from `withTimeout`. We will cancel the `bodyTask` from the
234
+ // `defer` method above.
228
235
throw CancellationError ( )
229
236
} else {
230
237
preconditionFailure ( " Continuation never finishes " )
Original file line number Diff line number Diff line change @@ -24,6 +24,11 @@ package func withTaskPriorityChangedHandler<T: Sendable>(
24
24
) async throws -> T {
25
25
let lastPriority = ThreadSafeBox ( initialValue: initialPriority)
26
26
let result : T ? = try await withThrowingTaskGroup ( of: Optional< T> . self ) { taskGroup in
27
+ defer {
28
+ // We leave this closure when either we have received a result or we registered cancellation. In either case, we
29
+ // want to make sure that we don't leave the body task or the priority watching task running.
30
+ taskGroup. cancelAll ( )
31
+ }
27
32
// Run the task priority watcher with high priority instead of inheriting the initial priority. Otherwise a
28
33
// `.background` task might not get its priority elevated because the priority watching task also runs at
29
34
// `.background` priority and might not actually get executed in time.
@@ -54,11 +59,10 @@ package func withTaskPriorityChangedHandler<T: Sendable>(
54
59
taskGroup. addTask {
55
60
try await operation ( )
56
61
}
57
- // The first task that watches the priority never finishes, so we are effectively await the `operation` task here
58
- // and cancelling the priority observation task once the operation task is done .
62
+ // The first task that watches the priority never finishes unless it is cancelled , so we are effectively await the
63
+ // ` operation` task here .
59
64
// We do need to await the observation task as well so that priority escalation also affects the observation task.
60
65
for try await case let value? in taskGroup {
61
- taskGroup. cancelAll ( )
62
66
return value
63
67
}
64
68
return nil
You can’t perform that action at this time.
0 commit comments