1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221
|
// RUN: %target-run-simple-swift( -Xfrontend -disable-availability-checking -parse-as-library) | %FileCheck %s --dump-input=always
// REQUIRES: executable_test
// REQUIRES: concurrency
// REQUIRES: reflection
// rdar://76038845
// REQUIRES: concurrency_runtime
// UNSUPPORTED: back_deployment_runtime
// FIXME: enable discarding taskgroup on windows; rdar://104762037
// UNSUPPORTED: OS=windows-msvc
struct Boom: Error {
let id: String
init(file: String = #fileID, line: UInt = #line) {
self.id = "\(file):\(line)"
}
init(id: String) {
self.id = id
}
}
struct IgnoredBoom: Error {}
func echo(_ i: Int) async -> Int { i }
func test_taskGroup_throws_rethrows() async {
print("==== \(#function) ------") // CHECK-LABEL: test_taskGroup_throws_rethrows
do {
let got = try await withThrowingTaskGroup(of: Int.self, returning: Int.self) { group in
group.addTask { await echo(1) }
group.addTask { await echo(2) }
group.addTask { throw Boom() }
do {
while let r = try await group.next() {
print("next: \(r)")
}
} catch {
// CHECK: error caught and rethrown in group: Boom(
print("error caught and rethrown in group: \(error)")
throw error
}
print("should have thrown")
return 0
}
print("Expected error to be thrown, but got: \(got)")
} catch {
// CHECK: rethrown: Boom(
print("rethrown: \(error)")
}
}
func test_taskGroup_noThrow_ifNotAwaitedThrowingTask() async {
print("==== \(#function) ------") // CHECK-LABEL: test_taskGroup_noThrow_ifNotAwaitedThrowingTask
let got = await withThrowingTaskGroup(of: Int.self, returning: Int.self) { group in
group.addTask { await echo(1) }
guard let r = try! await group.next() else {
return 0
}
group.addTask { throw Boom() }
// don't consume this task, so we're not throwing here
return r
}
print("Expected no error to be thrown, got: \(got)") // CHECK: Expected no error to be thrown, got: 1
}
func test_taskGroup_throw_rethrows_waitForAll() async {
print("==== \(#function) ------") // CHECK-LABEL: test_taskGroup_throw_rethrows_waitForAll
do {
_ = try await withThrowingTaskGroup(of: Int.self) { group in
group.addTask {
throw CancellationError()
}
group.addTask {
1
}
do {
try await group.waitForAll()
} catch {
print("waitAll rethrown: ", error)
// CHECK: waitAll rethrown: CancellationError()
print("isEmpty: ", group.isEmpty)
// CHECK: isEmpty: true
throw error
}
}
} catch {
print("rethrown: ", error)
// CHECK: rethrown: CancellationError()
}
}
func test_discardingTaskGroup_automaticallyRethrows() async {
print("==== \(#function) ------") // CHECK-LABEL: test_discardingTaskGroup_automaticallyRethrows
do {
let got = try await withThrowingDiscardingTaskGroup(returning: Int.self) { group in
group.addTask { _ = await echo(1) }
group.addTask { throw Boom() }
// add a throwing task, but don't consume it explicitly
// since we're in discard results mode, all will be awaited and the first error it thrown
return 13
}
print("Expected error to be thrown, but got: \(got)")
} catch {
// CHECK: rethrown: Boom(
print("rethrown: \(error)")
}
}
func test_discardingTaskGroup_automaticallyRethrowsOnlyFirst() async {
print("==== \(#function) ------") // CHECK-LABEL: test_discardingTaskGroup_automaticallyRethrowsOnlyFirst
do {
let got = try await withThrowingDiscardingTaskGroup(returning: Int.self) { group in
group.addTask {
_ = await echo(1)
}
group.addTask {
let error = Boom(id: "first, isCancelled:\(Task.isCancelled)")
print("Throwing: \(error)")
throw error
}
group.addTask {
// we wait "forever" but since the group will get cancelled after
// the first error, this will be woken up and throw a cancellation
do {
try await Task.sleep(until: .now + .seconds(120), clock: .continuous)
} catch {
print("Awoken, throwing: \(error)")
throw error
}
}
return 4
}
print("Expected error to be thrown, but got: \(got)")
} catch {
// CHECK: Throwing: Boom(id: "first, isCancelled:false
// CHECK: Awoken, throwing: CancellationError()
// and only then the re-throw happens:
// CHECK: rethrown: Boom(id: "first
print("rethrown: \(error)")
}
}
func test_discardingTaskGroup_automaticallyRethrows_first_withThrowingBodyFirst() async {
print("==== \(#function) ------") // CHECK-LABEL: test_discardingTaskGroup_automaticallyRethrows_first_withThrowingBodyFirst
do {
_ = try await withThrowingDiscardingTaskGroup(returning: Int.self) { group in
group.addTask {
_ = await echo(1)
}
group.addTask {
try? await Task.sleep(until: .now + .seconds(10), clock: .continuous)
let error = Boom(id: "task, second, isCancelled:\(Task.isCancelled)")
print("Throwing: \(error)")
throw error
}
let bodyError = Boom(id: "body, first, isCancelled:\(group.isCancelled)")
print("Throwing: \(bodyError)")
throw bodyError
}
fatalError("Expected error to be thrown")
} catch {
// CHECK: Throwing: Boom(id: "body, first, isCancelled:false
// CHECK: Throwing: Boom(id: "task, second, isCancelled:true
// and only then the re-throw happens:
// CHECK: rethrown: Boom(id: "body, first
print("rethrown: \(error)")
}
}
func test_discardingTaskGroup_automaticallyRethrows_first_withThrowingBodySecond() async {
print("==== \(#function) ------") // CHECK-LABEL: test_discardingTaskGroup_automaticallyRethrows_first_withThrowingBodySecond
do {
_ = try await withThrowingDiscardingTaskGroup(returning: Int.self) { group in
group.addTask {
let error = Boom(id: "task, first, isCancelled:\(Task.isCancelled)")
print("Throwing: \(error)")
throw error
}
try await Task.sleep(until: .now + .seconds(1), clock: .continuous)
let bodyError = Boom(id: "body, second, isCancelled:\(group.isCancelled)")
print("Throwing: \(bodyError)")
throw bodyError
}
fatalError("Expected error to be thrown")
} catch {
// CHECK: Throwing: Boom(id: "task, first, isCancelled:false
// CHECK: Throwing: Boom(id: "body, second, isCancelled:true
// and only then the re-throw happens:
// CHECK: rethrown: Boom(id: "body, second
print("rethrown: \(error)")
}
}
@available(SwiftStdlib 5.1, *)
@main struct Main {
static func main() async {
await test_taskGroup_throws_rethrows()
await test_taskGroup_noThrow_ifNotAwaitedThrowingTask()
await test_taskGroup_throw_rethrows_waitForAll()
await test_discardingTaskGroup_automaticallyRethrows()
await test_discardingTaskGroup_automaticallyRethrowsOnlyFirst()
await test_discardingTaskGroup_automaticallyRethrows_first_withThrowingBodyFirst()
await test_discardingTaskGroup_automaticallyRethrows_first_withThrowingBodySecond()
}
}
|