Skip to content

Commit 3dca480

Browse files
committed
test: Add ping pong test adapted re-written in Swift from libDispatch/tests/dispatch_pingpong.c.
[Human-Directed AI Assistance]
1 parent f3d4ce5 commit 3dca480

File tree

1 file changed

+60
-0
lines changed

1 file changed

+60
-0
lines changed
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import Testing
2+
3+
@testable import DispatchAsync
4+
5+
/// Ping-Pong queue test is adapted from the test
6+
/// [dispatch_pingpong.c in libdispatch](https://github.com/swiftlang/swift-corelibs-libdispatch/blob/main/tests/dispatch_pingpong.c).
7+
///
8+
/// Two queues recursively schedule work on each other N times. The test
9+
/// succeeds when the hand-off count matches expectations and no deadlock
10+
/// occurs.
11+
@Test
12+
func dispatchPingPongQueues() async throws {
13+
// NOTE: Original test uses 10_000_000, but that makes for a rather slow
14+
// unit test. Using 100_000 here as a "close-enough" tradeoff.
15+
let totalIterations = 100_000 // Total number of hand-offs between ping and pong functions.
16+
17+
let queuePing = DispatchQueue(label: "ping")
18+
let queuePong = DispatchQueue(label: "pong")
19+
20+
// NOTE: We intentionally use a nonisolated
21+
// variable here rather than an actor-protected or
22+
// semaphore-protected variable to force reliance on
23+
// the separate and serial queues waiting to execute
24+
// until another value is enqueued.
25+
//
26+
// This matches the implementation of the dispatch_pinpong.c test.
27+
nonisolated(unsafe) var counter = 0
28+
29+
// Ping
30+
@Sendable
31+
func schedulePing(_ iteration: Int, _ continuation: CheckedContinuation<Void, Never>) {
32+
queuePing.async {
33+
counter += 1
34+
if iteration < totalIterations {
35+
schedulePong(iteration + 1, continuation)
36+
} else {
37+
continuation.resume()
38+
}
39+
}
40+
}
41+
42+
// Pong
43+
@Sendable
44+
func schedulePong(_ iteration: Int, _ continuation: CheckedContinuation<Void, Never>) {
45+
queuePong.async {
46+
counter += 1
47+
schedulePing(iteration, continuation)
48+
}
49+
}
50+
51+
await withCheckedContinuation { continuation in
52+
// Start the chain. Chain will resume continuation when totalIterations
53+
// have been reached.
54+
schedulePing(0, continuation)
55+
}
56+
57+
let finalCount = counter
58+
// Each iteration performs two increments (ping + pong)
59+
#expect(finalCount == totalIterations * 2 + 1) // + 1 is for the final ping increment on the final iteration where i==totalIterations
60+
}

0 commit comments

Comments
 (0)