Skip to content

Commit c1fbce7

Browse files
committed
Fix flaky ActorQueue and MainActor cancellation tests
ActorQueues are re-entrant, so task2 and task3 would complete immediately when task1 suspends, potentially before cancelTasks() is called. Now all three tasks signal when started and wait on semaphores, ensuring they're all still executing when cancelled.
1 parent f20904d commit c1fbce7

File tree

1 file changed

+46
-24
lines changed

1 file changed

+46
-24
lines changed

Tests/AsyncQueueTests/CancellableQueueTests.swift

Lines changed: 46 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -173,28 +173,39 @@ struct CancellableQueueTests {
173173
let counter = Counter()
174174
actorQueue.adoptExecutionContext(of: counter)
175175
let systemUnderTest = CancellableQueue(underlyingQueue: actorQueue)
176-
let taskStarted = Semaphore()
177-
let taskAllowedToEnd = Semaphore()
176+
let task1Started = Semaphore()
177+
let task2Started = Semaphore()
178+
let task3Started = Semaphore()
179+
let tasksAllowedToEnd = Semaphore()
178180

179-
// Create a task that signals when it starts, then waits
181+
// Create tasks that signal when they start, then wait
180182
let task1 = Task(on: systemUnderTest) { _ in
181-
await taskStarted.signal()
182-
await taskAllowedToEnd.wait()
183+
await task1Started.signal()
184+
await tasksAllowedToEnd.wait()
183185
}
184186

185-
// Create additional tasks
186-
let task2 = Task(on: systemUnderTest) { _ in }
187+
let task2 = Task(on: systemUnderTest) { _ in
188+
await task2Started.signal()
189+
await tasksAllowedToEnd.wait()
190+
}
187191

188-
let task3 = Task(on: systemUnderTest) { _ in }
192+
let task3 = Task(on: systemUnderTest) { _ in
193+
await task3Started.signal()
194+
await tasksAllowedToEnd.wait()
195+
}
189196

190-
// Wait for the first task to start executing
191-
await taskStarted.wait()
197+
// Wait for all tasks to start executing
198+
await task1Started.wait()
199+
await task2Started.wait()
200+
await task3Started.wait()
192201

193202
// Cancel all tasks
194203
systemUnderTest.cancelTasks()
195204

196-
// Allow the task to end now that we've cancelled it.
197-
await taskAllowedToEnd.signal()
205+
// Allow tasks to end now that we've cancelled them.
206+
await tasksAllowedToEnd.signal()
207+
await tasksAllowedToEnd.signal()
208+
await tasksAllowedToEnd.signal()
198209

199210
#expect(task1.isCancelled)
200211
#expect(task2.isCancelled)
@@ -266,28 +277,39 @@ struct CancellableQueueTests {
266277
@Test
267278
func cancelTasks_mainActorQueue_cancelsCurrentlyExecutingAndPendingTasks() async {
268279
let systemUnderTest = CancellableQueue(underlyingQueue: MainActor.queue)
269-
let taskStarted = Semaphore()
270-
let taskAllowedToEnd = Semaphore()
280+
let task1Started = Semaphore()
281+
let task2Started = Semaphore()
282+
let task3Started = Semaphore()
283+
let tasksAllowedToEnd = Semaphore()
271284

272-
// Create a task that signals when it starts, then waits
285+
// Create tasks that signal when they start, then wait
273286
let task1 = Task(on: systemUnderTest) {
274-
await taskStarted.signal()
275-
await taskAllowedToEnd.wait()
287+
await task1Started.signal()
288+
await tasksAllowedToEnd.wait()
276289
}
277290

278-
// Create additional tasks
279-
let task2 = Task(on: systemUnderTest) { }
291+
let task2 = Task(on: systemUnderTest) {
292+
await task2Started.signal()
293+
await tasksAllowedToEnd.wait()
294+
}
280295

281-
let task3 = Task(on: systemUnderTest) { }
296+
let task3 = Task(on: systemUnderTest) {
297+
await task3Started.signal()
298+
await tasksAllowedToEnd.wait()
299+
}
282300

283-
// Wait for the first task to start executing
284-
await taskStarted.wait()
301+
// Wait for all tasks to start executing
302+
await task1Started.wait()
303+
await task2Started.wait()
304+
await task3Started.wait()
285305

286306
// Cancel all tasks
287307
systemUnderTest.cancelTasks()
288308

289-
// Allow the task to end now that we've cancelled it.
290-
await taskAllowedToEnd.signal()
309+
// Allow tasks to end now that we've cancelled them.
310+
await tasksAllowedToEnd.signal()
311+
await tasksAllowedToEnd.signal()
312+
await tasksAllowedToEnd.signal()
291313

292314
#expect(task1.isCancelled)
293315
#expect(task2.isCancelled)

0 commit comments

Comments
 (0)