Skip to content

Commit a0cfda5

Browse files
Fix CPU hang in BatchingChannelReader.WaitToReadAsyncCore
The issue was that after Task.WhenAny completed, the code did not await the source task before looping. This caused a tight CPU-consuming loop when the source channel was closed but the buffer was empty, as the closed source would immediately return a completed ValueTask on each WaitToReadAsync call. The fix properly awaits the source task after WhenAny to check its completion status, preventing the tight loop. This matches the pattern used in the base BufferingChannelReader class. Co-authored-by: electricessence <5899455+electricessence@users.noreply.github.com>
1 parent 08900d6 commit a0cfda5

File tree

1 file changed

+8
-1
lines changed

1 file changed

+8
-1
lines changed

Open.ChannelExtensions/Readers/BatchingChannelReader.cs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -245,13 +245,20 @@ protected override async ValueTask<bool> WaitToReadAsyncCore(
245245
}
246246

247247
// WhenAny will not throw when a task is cancelled.
248-
await Task.WhenAny(s.AsTask(), b).ConfigureAwait(false);
248+
Task<bool> sTask = s.AsTask();
249+
await Task.WhenAny(sTask, b).ConfigureAwait(false);
249250
if (b.IsCompleted) // Assuming it was bufferWait that completed.
250251
{
251252
await tokenSource.CancelAsync().ConfigureAwait(false);
252253
return await b.ConfigureAwait(false);
253254
}
254255

256+
// Await the source task to check if it completed and get its result
257+
// This prevents a tight loop when the source is closed
258+
await sTask.ConfigureAwait(false);
259+
if (b.IsCompleted)
260+
return await b.ConfigureAwait(false);
261+
255262
TryPipeItems(false);
256263

257264
if (b.IsCompleted || token.IsCancellationRequested)

0 commit comments

Comments
 (0)