private Task<int> ReadAsyncCore(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
var completionSource = new ReadWriteCompletionSource(this, buffer, cancellationToken, isWrite: false);
// Queue an async ReadFile operation and pass in a packed overlapped
int errorCode = 0;
int r;
unsafe
{
r = ReadFileNative(_handle, buffer, offset, count, completionSource.Overlapped, out errorCode);
}
// ReadFile, the OS version, will return 0 on failure, but this ReadFileNative wrapper
// returns -1. This will return the following:
// - On error, r==-1.
// - On async requests that are still pending, r==-1 w/ hr==ERROR_IO_PENDING
// - On async requests that completed sequentially, r==0
//
// You will NEVER RELIABLY be able to get the number of buffer read back from this call
// when using overlapped structures! You must not pass in a non-null lpNumBytesRead to
// ReadFile when using overlapped structures! This is by design NT behavior.
if (r == -1)
{
switch (errorCode)
{
// One side has closed its handle or server disconnected.
// Set the state to Broken and do some cleanup work
case Interop.Errors.ERROR_BROKEN_PIPE:
case Interop.Errors.ERROR_PIPE_NOT_CONNECTED:
State = PipeState.Broken;
unsafe
{
// Clear the overlapped status bit for this special case. Failure to do so looks
// like we are freeing a pending overlapped.
completionSource.Overlapped->InternalLow = IntPtr.Zero;
}
completionSource.ReleaseResources();
UpdateMessageCompletion(true);
return s_zeroTask;
case Interop.Errors.ERROR_IO_PENDING:
break;
default:
throw Win32Marshal.GetExceptionForWin32Error(errorCode);
}
}
completionSource.RegisterForCancellation();
return completionSource.Task;
}