File: async_taskgroup_throw_rethrow.swift

package info (click to toggle)
swiftlang 6.0.3-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,519,992 kB
  • sloc: cpp: 9,107,863; ansic: 2,040,022; asm: 1,135,751; python: 296,500; objc: 82,456; f90: 60,502; lisp: 34,951; pascal: 19,946; sh: 18,133; perl: 7,482; ml: 4,937; javascript: 4,117; makefile: 3,840; awk: 3,535; xml: 914; fortran: 619; cs: 573; ruby: 573
file content (221 lines) | stat: -rw-r--r-- 7,174 bytes parent folder | download
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()
  }
}