private Task<int> ReadAsyncCorePrivate(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
        {
            Debug.Assert(_handle != null, "_handle is null");
            Debug.Assert(!_handle.IsClosed, "_handle is closed");
            Debug.Assert(CanRead, "can't read");
            Debug.Assert(buffer != null, "buffer == null");
            Debug.Assert(_isAsync, "ReadAsyncCorePrivate doesn't work on synchronous file streams!");
            Debug.Assert(offset >= 0, "offset is negative");
            Debug.Assert(count >= 0, "count is negative");

            if (buffer.Length == 0)
            {
                UpdateMessageCompletion(false);
                return s_zeroTask;
            }
            else
            {
                var completionSource = new PipeStreamCompletionSource(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.mincore.Errors.ERROR_BROKEN_PIPE:
                        case Interop.mincore.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.mincore.Errors.ERROR_IO_PENDING:
                            break;

                        default:
                            throw Win32Marshal.GetExceptionForWin32Error(errorCode);
                    }
                }

                completionSource.RegisterForCancellation();
                return completionSource.Task;
            }
        }
        private Task WriteAsyncCorePrivate(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
        {
            Debug.Assert(_handle != null, "_handle is null");
            Debug.Assert(!_handle.IsClosed, "_handle is closed");
            Debug.Assert(CanWrite, "can't write");
            Debug.Assert(buffer != null, "buffer == null");
            Debug.Assert(_isAsync, "WriteAsyncCorePrivate doesn't work on synchronous file streams!");
            Debug.Assert(offset >= 0, "offset is negative");
            Debug.Assert(count >= 0, "count is negative");

            if (buffer.Length == 0)
            {
                return Task.CompletedTask;
            }
            else
            {
                var completionSource = new PipeStreamCompletionSource(this, buffer, cancellationToken, isWrite: true);
                int errorCode = 0;

                // Queue an async WriteFile operation and pass in a packed overlapped
                int r;
                unsafe
                {
                    r = WriteFileNative(_handle, buffer, offset, count, completionSource.Overlapped, out errorCode);
                }

                // WriteFile, the OS version, will return 0 on failure, but this WriteFileNative 
                // 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 written back from this 
                // call when using overlapped structures!  You must not pass in a non-null 
                // lpNumBytesWritten to WriteFile when using overlapped structures!  This is by design 
                // NT behavior.
                if (r == -1 && errorCode != Interop.mincore.Errors.ERROR_IO_PENDING)
                {
                    completionSource.ReleaseResources();
                    throw WinIOError(errorCode);
                }

                completionSource.RegisterForCancellation();
                return completionSource.Task;
            }
        }