@@ -77,55 +77,50 @@ final class AsyncQueueTests: XCTestCase {
77
77
var systemUnderTest : AsyncQueue ? = AsyncQueue ( )
78
78
let counter = Counter ( )
79
79
let expectation = self . expectation ( description: #function)
80
- await withThrowingTaskGroup ( of: Void . self) { taskGroup in
81
- let foreverSleep = Task {
82
- try await Task . sleep ( nanoseconds: UInt64 . max)
83
- }
84
- taskGroup. addTask {
85
- try await foreverSleep. value
86
- }
87
- systemUnderTest? . async {
88
- // Make the queue wait.
89
- try ? await foreverSleep. value
90
- await counter. incrementAndExpectCount ( equals: 1 )
91
- }
92
- systemUnderTest? . async {
93
- // This async task should not execute until the sleep is cancelled.
94
- await counter. incrementAndExpectCount ( equals: 2 )
95
- expectation. fulfill ( )
96
- }
97
- // Nil out our reference to the queue to show that the enqueued tasks will still complete
98
- systemUnderTest = nil
99
- // Cancel the sleep timer to unlock the remaining enqueued tasks.
100
- foreverSleep. cancel ( )
101
-
102
- await waitForExpectations ( timeout: 1.0 )
80
+ let semaphore = Semaphore ( )
81
+ systemUnderTest? . async {
82
+ // Make the queue wait.
83
+ await semaphore. wait ( )
84
+ await counter. incrementAndExpectCount ( equals: 1 )
85
+ }
86
+ systemUnderTest? . async {
87
+ // This async task should not execute until the sleep is cancelled.
88
+ await counter. incrementAndExpectCount ( equals: 2 )
89
+ expectation. fulfill ( )
103
90
}
91
+ // Nil out our reference to the queue to show that the enqueued tasks will still complete
92
+ systemUnderTest = nil
93
+ // Signal the semaphore to unlock the remaining enqueued tasks.
94
+ await semaphore. signal ( )
95
+
96
+ await waitForExpectations ( timeout: 1.0 )
104
97
}
105
98
106
- func test_async_doesNotRetainTaskAfterExecution( ) async {
99
+ func test_async_doesNotRetainTaskAfterExecution( ) async throws {
107
100
final class Reference : Sendable { }
108
101
final class ReferenceHolder : @unchecked Sendable {
109
102
var reference : Reference ? = Reference ( )
110
103
}
111
104
let referenceHolder = ReferenceHolder ( )
112
105
weak var weakReference = referenceHolder. reference
113
- let expectation = self . expectation ( description: #function)
114
- let foreverSleep = Task {
115
- try await Task . sleep ( nanoseconds: UInt64 . max)
116
- }
106
+ let asyncSemaphore = Semaphore ( )
107
+ let syncSemaphore = Semaphore ( )
117
108
systemUnderTest. async { [ reference = referenceHolder. reference] in
118
- // Wait for the setup to complete.
119
- try ? await foreverSleep. value
109
+ // Now that we've started the task and captured the reference, release the syncronous code.
110
+ await syncSemaphore. signal ( )
111
+ // Wait for the synchronous setup to complete and the reference to be nil'd out.
112
+ await asyncSemaphore. wait ( )
120
113
// Retain the unsafe counter until the task is completed.
121
114
_ = reference
122
- expectation. fulfill ( )
123
115
}
116
+ // Wait for the asynchronous task to start.
117
+ await syncSemaphore. wait ( )
124
118
referenceHolder. reference = nil
125
119
XCTAssertNotNil ( weakReference)
126
- // Cancel the sleep timer to allow the enqueued task to complete.
127
- foreverSleep. cancel ( )
128
- await waitForExpectations ( timeout: 1.0 )
120
+ // Allow the enqueued task to complete.
121
+ await asyncSemaphore. signal ( )
122
+ // Make sure the task has completed.
123
+ await systemUnderTest. await { /* Drain the queue */ }
129
124
XCTAssertNil ( weakReference)
130
125
}
131
126
@@ -197,4 +192,37 @@ final class AsyncQueueTests: XCTestCase {
197
192
var count = 0
198
193
}
199
194
195
+ // MARK: - Semaphore
196
+
197
+ private actor Semaphore {
198
+
199
+ func wait( ) async {
200
+ count -= 1
201
+ guard count < 0 else {
202
+ // We don't need to wait because count is greater than or equal to zero.
203
+ return
204
+ }
205
+
206
+ await withCheckedContinuation { continuation in
207
+ continuations. append ( continuation)
208
+ }
209
+ }
210
+
211
+ func signal( ) {
212
+ count += 1
213
+ guard count >= 0 else {
214
+ // Continue waiting.
215
+ return
216
+ }
217
+
218
+ for continuation in continuations {
219
+ continuation. resume ( )
220
+ }
221
+
222
+ continuations. removeAll ( )
223
+ }
224
+
225
+ private var continuations = [ CheckedContinuation < Void , Never > ] ( )
226
+ private var count = 0
227
+ }
200
228
}
0 commit comments