|
1 | 1 | // RUN: %target-run-simple-swift( -Xfrontend -disable-availability-checking -parse-as-library) | %FileCheck %s --dump-input=always
|
2 | 2 | // TODO: move to target-run-simple-leaks-swift once CI is using at least Xcode 14.3
|
3 | 3 |
|
4 |
| -// rdar://109998145 - Temporarily disable this test |
5 |
| -// REQUIRES: rdar109998145 |
6 |
| - |
7 | 4 | // REQUIRES: concurrency
|
8 | 5 | // REQUIRES: executable_test
|
9 | 6 | // REQUIRES: concurrency_runtime
|
|
16 | 13 |
|
17 | 14 | import _Concurrency
|
18 | 15 |
|
| 16 | +actor SimpleCountDownLatch { |
| 17 | + let from: Int |
| 18 | + var count: Int |
| 19 | + |
| 20 | + var continuation: CheckedContinuation<Void, Never>? |
| 21 | + |
| 22 | + init(from: Int) { |
| 23 | + self.from = from |
| 24 | + self.count = from |
| 25 | + } |
| 26 | + |
| 27 | + func hit() { |
| 28 | + defer { count -= 1 } |
| 29 | + if count == 0 { |
| 30 | + fatalError("Counted down more times than expected! (From: \(from))") |
| 31 | + } else if count == 1 { |
| 32 | + continuation?.resume() |
| 33 | + } |
| 34 | + } |
| 35 | + |
| 36 | + func wait() async { |
| 37 | + guard self.count > 0 else { |
| 38 | + return // we're done |
| 39 | + } |
| 40 | + |
| 41 | + return await withCheckedContinuation { cc in |
| 42 | + self.continuation = cc |
| 43 | + } |
| 44 | + } |
| 45 | +} |
| 46 | + |
19 | 47 | final class PrintDeinit {
|
20 | 48 | let id: String
|
21 | 49 | init(id: String) {
|
@@ -60,96 +88,145 @@ final class SomeClass: @unchecked Sendable {
|
60 | 88 |
|
61 | 89 | // NOTE: Not as StdlibUnittest/TestSuite since these types of tests are unreasonably slow to load/debug.
|
62 | 90 |
|
63 |
| -@main struct Main { |
64 |
| - static func main() async { |
65 |
| - _ = try? await withThrowingDiscardingTaskGroup() { group in |
66 |
| - group.addTask { |
67 |
| - throw Boom(id: "race-boom-class") |
68 |
| - } |
| 91 | +func testTwo() async { |
| 92 | + let latch = SimpleCountDownLatch(from: 2) |
| 93 | + |
| 94 | + _ = try? await withThrowingDiscardingTaskGroup() { group in |
| 95 | + group.addTask { |
| 96 | + await latch.hit() |
| 97 | + throw Boom(id: "race-boom") |
| 98 | + } |
| 99 | + group.addTask { |
| 100 | + await latch.hit() |
| 101 | + SomeClass(id: "race-boom-class") // will be discarded |
| 102 | + } |
| 103 | + |
| 104 | + return 12 |
| 105 | + } |
| 106 | + |
| 107 | + // since values may deinit in any order, we just assert their count basically |
| 108 | + // CHECK-DAG: deinit, id: race-boom |
| 109 | + // CHECK-DAG: deinit, id: race-boom |
| 110 | + await latch.wait() |
| 111 | + try? await Task.sleep(for: .milliseconds(300)) |
| 112 | + |
| 113 | + print("done") // CHECK: done |
| 114 | +} |
| 115 | + |
| 116 | +func manyOk() async { |
| 117 | + let latch = SimpleCountDownLatch(from: 6) |
| 118 | + |
| 119 | + _ = try? await withThrowingDiscardingTaskGroup() { group in |
| 120 | + for i in 0..<6 { |
69 | 121 | group.addTask {
|
70 |
| - SomeClass(id: "race-boom-class") // will be discarded |
| 122 | + await latch.hit() |
| 123 | + _ = SomeClass(id: "many-ok") // will be discarded |
71 | 124 | }
|
72 |
| - // since values may deinit in any order, we just assert their count basically |
73 |
| - // CHECK-DAG: deinit, id: race-boom-class |
74 |
| - // CHECK-DAG: deinit, id: race-boom-class |
75 |
| - |
76 |
| - return 12 |
77 | 125 | }
|
78 | 126 |
|
79 |
| - // many ok |
80 |
| - _ = try? await withThrowingDiscardingTaskGroup() { group in |
| 127 | + return 12 |
| 128 | + } |
| 129 | + // since values may deinit in any order, we just assert their count basically |
| 130 | + // CHECK-DAG: deinit, id: many-ok |
| 131 | + // CHECK-DAG: deinit, id: many-ok |
| 132 | + // CHECK-DAG: deinit, id: many-ok |
| 133 | + // CHECK-DAG: deinit, id: many-ok |
| 134 | + // CHECK-DAG: deinit, id: many-ok |
| 135 | + // CHECK-DAG: deinit, id: many-ok |
| 136 | + |
| 137 | + await latch.wait() |
| 138 | + try? await Task.sleep(for: .milliseconds(300)) |
| 139 | + |
| 140 | + print("done") // CHECK: done |
| 141 | +} |
| 142 | + |
| 143 | +func manyThrows() async { |
| 144 | + let latch = SimpleCountDownLatch(from: 6) |
| 145 | + |
| 146 | + do { |
| 147 | + let value: Void = try await withThrowingDiscardingTaskGroup() { group in |
81 | 148 | for i in 0..<6 {
|
82 | 149 | group.addTask {
|
83 |
| - SomeClass(id: "many-ok") // will be discarded |
| 150 | + await latch.hit() |
| 151 | + throw BoomClass(id: "many-error") // will be rethrown |
84 | 152 | }
|
85 |
| - // since values may deinit in any order, we just assert their count basically |
86 |
| - // CHECK-DAG: deinit, id: many-ok |
87 |
| - // CHECK-DAG: deinit, id: many-ok |
88 |
| - // CHECK-DAG: deinit, id: many-ok |
89 |
| - // CHECK-DAG: deinit, id: many-ok |
90 |
| - // CHECK-DAG: deinit, id: many-ok |
91 |
| - // CHECK-DAG: deinit, id: many-ok |
92 | 153 | }
|
93 | 154 |
|
94 |
| - return 12 |
| 155 | + // since values may deinit in any order, we just assert their count basically |
| 156 | + // CHECK-DAG: deinit, id: many-error |
| 157 | + // CHECK-DAG: deinit, id: many-error |
| 158 | + // CHECK-DAG: deinit, id: many-error |
| 159 | + // CHECK-DAG: deinit, id: many-error |
| 160 | + // CHECK-DAG: deinit, id: many-error |
| 161 | + // CHECK-DAG: deinit, id: many-error |
| 162 | + |
| 163 | + 12 // must be ignored |
95 | 164 | }
|
| 165 | + preconditionFailure("Should throw") |
| 166 | + } catch { |
| 167 | + precondition("\(error)" == "main.BoomClass", "error was: \(error)") |
| 168 | + } |
96 | 169 |
|
97 |
| - // many throws |
98 |
| - do { |
99 |
| - let value = try await withThrowingDiscardingTaskGroup() { group in |
100 |
| - for i in 0..<6 { |
101 |
| - group.addTask { |
102 |
| - throw BoomClass(id: "many-error") // will be rethrown |
103 |
| - } |
104 |
| - } |
| 170 | + await latch.wait() |
| 171 | + try? await Task.sleep(for: .milliseconds(300)) |
105 | 172 |
|
106 |
| - // since values may deinit in any order, we just assert their count basically |
107 |
| - // CHECK-DAG: deinit, id: many-error |
108 |
| - // CHECK-DAG: deinit, id: many-error |
109 |
| - // CHECK-DAG: deinit, id: many-error |
110 |
| - // CHECK-DAG: deinit, id: many-error |
111 |
| - // CHECK-DAG: deinit, id: many-error |
112 |
| - // CHECK-DAG: deinit, id: many-error |
| 173 | + print("done") // CHECK: done |
| 174 | +} |
113 | 175 |
|
114 |
| - 12 // must be ignored |
115 |
| - } |
116 |
| - preconditionFailure("Should throw") |
117 |
| - } catch { |
118 |
| - precondition("\(error)" == "main.BoomClass", "error was: \(error)") |
| 176 | +func manyValuesThrows() async { |
| 177 | + let latch = SimpleCountDownLatch(from: 6) |
| 178 | + |
| 179 | + // many errors, many values |
| 180 | + _ = try? await withThrowingDiscardingTaskGroup() { group in |
| 181 | + group.addTask { |
| 182 | + await latch.hit() |
| 183 | + _ = SomeClass(id: "mixed-ok") // will be discarded |
| 184 | + } |
| 185 | + group.addTask { |
| 186 | + await latch.hit() |
| 187 | + _ = SomeClass(id: "mixed-ok") // will be discarded |
| 188 | + } |
| 189 | + group.addTask { |
| 190 | + await latch.hit() |
| 191 | + _ = SomeClass(id: "mixed-ok") // will be discarded |
| 192 | + } |
| 193 | + group.addTask { |
| 194 | + await latch.hit() |
| 195 | + throw Boom(id: "mixed-error") |
| 196 | + } |
| 197 | + group.addTask { |
| 198 | + await latch.hit() |
| 199 | + throw Boom(id: "mixed-error") |
| 200 | + } |
| 201 | + group.addTask { |
| 202 | + await latch.hit() |
| 203 | + throw Boom(id: "mixed-error") |
119 | 204 | }
|
120 | 205 |
|
121 |
| - // many errors, many values |
122 |
| - _ = try? await withThrowingDiscardingTaskGroup() { group in |
123 |
| - group.addTask { |
124 |
| - SomeClass(id: "mixed-ok") // will be discarded |
125 |
| - } |
126 |
| - group.addTask { |
127 |
| - SomeClass(id: "mixed-ok") // will be discarded |
128 |
| - } |
129 |
| - group.addTask { |
130 |
| - SomeClass(id: "mixed-ok") // will be discarded |
131 |
| - } |
132 |
| - group.addTask { |
133 |
| - throw Boom(id: "mixed-error") |
134 |
| - } |
135 |
| - group.addTask { |
136 |
| - throw Boom(id: "mixed-error") |
137 |
| - } |
138 |
| - group.addTask { |
139 |
| - throw Boom(id: "mixed-error") |
140 |
| - } |
| 206 | + return 12 |
| 207 | + } |
141 | 208 |
|
142 |
| - // since values may deinit in any order, we just assert their count basically |
143 |
| - // three ok's |
144 |
| - // CHECK-DAG: deinit, id: mixed |
145 |
| - // CHECK-DAG: deinit, id: mixed |
146 |
| - // CHECK-DAG: deinit, id: mixed |
147 |
| - // three errors |
148 |
| - // CHECK-DAG: deinit, id: mixed |
149 |
| - // CHECK-DAG: deinit, id: mixed |
150 |
| - // CHECK-DAG: deinit, id: mixed |
151 |
| - |
152 |
| - return 12 |
153 |
| - } |
| 209 | + // since values may deinit in any order, we just assert their count basically |
| 210 | + // three ok's |
| 211 | + // CHECK-DAG: deinit, id: mixed |
| 212 | + // CHECK-DAG: deinit, id: mixed |
| 213 | + // CHECK-DAG: deinit, id: mixed |
| 214 | + // three errors |
| 215 | + // CHECK-DAG: deinit, id: mixed |
| 216 | + // CHECK-DAG: deinit, id: mixed |
| 217 | + // CHECK-DAG: deinit, id: mixed |
| 218 | + |
| 219 | + await latch.wait() |
| 220 | + try? await Task.sleep(for: .milliseconds(300)) |
| 221 | + |
| 222 | + print("done") // CHECK: done |
| 223 | +} |
| 224 | + |
| 225 | +@main struct Main { |
| 226 | + static func main() async { |
| 227 | + await testTwo() |
| 228 | + await manyOk() |
| 229 | + await manyThrows() |
| 230 | + await manyValuesThrows() |
154 | 231 | }
|
155 | 232 | }
|
0 commit comments