@@ -248,21 +248,26 @@ package final class TestSourceKitLSPClient: MessageHandler, Sendable {
248
248
/// - Note: This also returns any notifications sent before the call to
249
249
/// `nextNotification`.
250
250
package func nextNotification( timeout: Duration = . seconds( defaultTimeout) ) async throws -> any NotificationType {
251
- return try await withThrowingTaskGroup ( of: ( any NotificationType ) . self) { taskGroup in
252
- taskGroup. addTask {
253
- for await notification in self . notifications {
254
- return notification
255
- }
256
- throw NotificationTimeoutError ( )
257
- }
258
- taskGroup. addTask {
259
- try await Task . sleep ( for: timeout)
260
- throw NotificationTimeoutError ( )
251
+ // The task that gets the next notification from `self.notifications`.
252
+ let notificationYielder = Task {
253
+ for await notification in self . notifications {
254
+ return notification
261
255
}
262
- let result = try await taskGroup. next ( ) !
263
- taskGroup. cancelAll ( )
264
- return result
256
+ throw NotificationTimeoutError ( )
257
+ }
258
+ // After `timeout`, we tell `notificationYielder` that we are no longer interested in its result by cancelling it.
259
+ // We wait for `notificationYielder` to accept this cancellation instead of returning immediately to avoid a
260
+ // situation where `notificationYielder` continues running, eats the first notification but it then never gets
261
+ // delivered to the test because we already delivered a timeout.
262
+ let cancellationTask = Task {
263
+ try await Task . sleep ( for: timeout)
264
+ notificationYielder. cancel ( )
265
+ }
266
+ defer {
267
+ // We have received a value and don't need the cancellationTask anymore
268
+ cancellationTask. cancel ( )
265
269
}
270
+ return try await notificationYielder. value
266
271
}
267
272
268
273
/// Await the next diagnostic notification sent to the client.
0 commit comments