2

Currently I'm using the following utility extension to read from a PipeReader with a specified timeout. The timeout is required to properly implement Keep-Alive within a HTTP server.

internal static async Task<ReadResult?> ReadWithTimeoutAsync(this PipeReader reader, TimeSpan timeout)
{
    ReadResult? result = null;

    var readTask = Task.Run(async () =>
    {
        result = await reader.ReadAsync();
    });

    var success = await Task.WhenAny(readTask, Task.Delay(timeout)) == readTask;

    if (!success || (result == null))
    {
        return null;
    }

    return result;
}

This code is problematic in a couple of ways, as it introduces locking (within Task.Delay), a lot of allocations and a thread to be handled by the CPU.

Is there a more efficient way to use a PipeReader with read timeouts?

Gene
  • 4,192
  • 5
  • 32
  • 56
  • 2
    can you not just use `ReadAsync` passing a `CancellationToken` that *is the timeout*? (i.e. [CancelAfter](https://learn.microsoft.com/en-us/dotnet/api/system.threading.cancellationtokensource.cancelafter?view=netframework-4.8)) – Marc Gravell Mar 09 '20 at 11:52
  • @MarcGravell Oh - of course, thank you. – Gene Mar 09 '20 at 12:05
  • as a side note: if you're trying to implement an http server, note that Kestrel is very well implemented, and is easy to hook into; it may be easier just to use it for the heavy lifting – Marc Gravell Mar 09 '20 at 12:31
  • The goal is actually to implement the HTTP server itself (and understand protocols like HTTP/2). I look into the Kestrel sources from time to time to see how things are implemented there. – Gene Mar 09 '20 at 13:49

1 Answers1

3

We can use a CancellationToken to implement the timeout in a more efficient manner:

using var cancellation = new CancellationTokenSource(timout);

try
{
    Data = (await Reader.ReadAsync(cancellation.Token)).Buffer;
}
catch (OperationCanceledException)
{
    return null;
}
Gene
  • 4,192
  • 5
  • 32
  • 56