Skip to content

Commit e4c8760

Browse files
committed
Fix #63147 waitForAll impl in ThrowingTaskGroup, used to not wait enough
rdar://104507347
1 parent a25aa2c commit e4c8760

File tree

2 files changed

+45
-3
lines changed

2 files changed

+45
-3
lines changed

stdlib/public/Concurrency/TaskGroup.swift

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -616,11 +616,25 @@ public struct ThrowingTaskGroup<ChildTaskResult: Sendable, Failure: Error> {
616616
}
617617

618618
/// Wait for all of the group's remaining tasks to complete.
619-
///
620-
/// - Throws: only during
621619
@_alwaysEmitIntoClient
622620
public mutating func waitForAll() async throws {
623-
await self.awaitAllRemainingTasks()
621+
var firstError: Error? = nil
622+
623+
// Make sure we loop until all child tasks have completed
624+
while !isEmpty {
625+
do {
626+
while let _ = try await next() {}
627+
} catch {
628+
// Upon error throws, capture the first one
629+
if firstError == nil {
630+
firstError = error
631+
}
632+
}
633+
}
634+
635+
if let firstError {
636+
throw firstError
637+
}
624638
}
625639

626640
#if !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY

test/Concurrency/Runtime/async_taskgroup_throw_rethrow.swift

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,33 @@ func test_taskGroup_noThrow_ifNotAwaitedThrowingTask() async {
6868
print("Expected no error to be thrown, got: \(got)") // CHECK: Expected no error to be thrown, got: 1
6969
}
7070

71+
func test_taskGroup_throw_rethrows_waitForAll() async {
72+
print("==== \(#function) ------") // CHECK-LABEL: test_taskGroup_throw_rethrows_waitForAll
73+
do {
74+
_ = try await withThrowingTaskGroup(of: Int.self) { group in
75+
group.addTask {
76+
throw CancellationError()
77+
}
78+
group.addTask {
79+
1
80+
}
81+
82+
do {
83+
try await group.waitForAll()
84+
} catch {
85+
print("waitAll rethrown: ", error)
86+
// CHECK: waitAll rethrown: CancellationError()
87+
print("isEmpty: ", group.isEmpty)
88+
// CHECK: isEmpty: true
89+
throw error
90+
}
91+
}
92+
} catch {
93+
print("rethrown: ", error)
94+
// CHECK: rethrown: CancellationError()
95+
}
96+
}
97+
7198
func test_discardingTaskGroup_automaticallyRethrows() async {
7299
print("==== \(#function) ------") // CHECK-LABEL: test_discardingTaskGroup_automaticallyRethrows
73100
do {
@@ -182,6 +209,7 @@ func test_discardingTaskGroup_automaticallyRethrows_first_withThrowingBodySecond
182209
static func main() async {
183210
await test_taskGroup_throws_rethrows()
184211
await test_taskGroup_noThrow_ifNotAwaitedThrowingTask()
212+
await test_taskGroup_throw_rethrows_waitForAll()
185213
await test_discardingTaskGroup_automaticallyRethrows()
186214
await test_discardingTaskGroup_automaticallyRethrowsOnlyFirst()
187215
await test_discardingTaskGroup_automaticallyRethrows_first_withThrowingBodyFirst()

0 commit comments

Comments
 (0)