Skip to content

Commit 820b454

Browse files
authored
Merge pull request #1780 from ahoppen/withtimeout-priority-escalation
Fix a race condition that caused `withTimeout` to not escalate the priority of the body
2 parents 5b07ae6 + 0b7b565 commit 820b454

File tree

2 files changed

+8
-4
lines changed

2 files changed

+8
-4
lines changed

Sources/SwiftExtensions/AsyncUtils.swift

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -178,9 +178,14 @@ package func withTimeout<T: Sendable>(
178178
_ duration: Duration,
179179
_ body: @escaping @Sendable () async throws -> T
180180
) async throws -> T {
181+
// Get the priority with which to launch the body task here so that we can pass the same priority as the initial
182+
// priority to `withTaskPriorityChangedHandler`. Otherwise, we can get into a race condition where bodyTask gets
183+
// launched with a low priority, then the priority gets elevated before we call with `withTaskPriorityChangedHandler`,
184+
// we thus don't receive a `taskPriorityChanged` and hence never increase the priority of `bodyTask`.
185+
let priority = Task.currentPriority
181186
var mutableTasks: [Task<Void, Error>] = []
182187
let stream = AsyncThrowingStream<T, Error> { continuation in
183-
let bodyTask = Task<Void, Error> {
188+
let bodyTask = Task<Void, Error>(priority: priority) {
184189
do {
185190
let result = try await body()
186191
continuation.yield(result)
@@ -189,7 +194,7 @@ package func withTimeout<T: Sendable>(
189194
}
190195
}
191196

192-
let timeoutTask = Task {
197+
let timeoutTask = Task(priority: priority) {
193198
try await Task.sleep(for: duration)
194199
continuation.yield(with: .failure(TimeoutError()))
195200
bodyTask.cancel()
@@ -199,7 +204,7 @@ package func withTimeout<T: Sendable>(
199204

200205
let tasks = mutableTasks
201206

202-
return try await withTaskPriorityChangedHandler {
207+
return try await withTaskPriorityChangedHandler(initialPriority: priority) {
203208
for try await value in stream {
204209
return value
205210
}

Tests/SKSupportTests/AsyncUtilsTests.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,6 @@ final class AsyncUtilsTests: XCTestCase {
5252
}
5353

5454
func testWithTimeoutEscalatesPriority() async throws {
55-
try XCTSkipIf(true, "Flakey test: rdar://137640122")
5655
let expectation = self.expectation(description: "Timeout started")
5756
let task = Task(priority: .background) {
5857
// We don't actually hit the timeout. It's just a large value.

0 commit comments

Comments
 (0)