Exemple #1
0
        private unsafe ValueTask WriteAsyncInternal(ReadOnlyMemory <byte> source, CancellationToken cancellationToken)
        {
            if (!CanWrite)
            {
                ThrowHelper.ThrowNotSupportedException_UnwritableStream();
            }

            // Rent the reusable ValueTaskSource, or create a new one to use if we couldn't get one (which
            // should only happen on first use or if the FileStream is being used concurrently).
            ValueTaskSource vts = Interlocked.Exchange(ref _reusableValueTaskSource, null) ?? new ValueTaskSource(this);

            try
            {
                NativeOverlapped *nativeOverlapped = vts.PrepareForOperation(source);
                Debug.Assert(vts._memoryHandle.Pointer != null);

                long positionBefore = _filePosition;
                if (CanSeek)
                {
                    // Now set the position to read from in the NativeOverlapped struct
                    // For pipes, we should leave the offset fields set to 0.
                    nativeOverlapped->OffsetLow  = (int)positionBefore;
                    nativeOverlapped->OffsetHigh = (int)(positionBefore >> 32);

                    // When using overlapped IO, the OS is not supposed to
                    // touch the file pointer location at all.  We will adjust it
                    // ourselves, but only in memory.  This isn't threadsafe.
                    _filePosition += source.Length;
                    UpdateLengthOnChangePosition();
                }

                // Queue an async WriteFile operation.
                if (Interop.Kernel32.WriteFile(_fileHandle, (byte *)vts._memoryHandle.Pointer, source.Length, IntPtr.Zero, nativeOverlapped) == 0)
                {
                    // The operation failed, or it's pending.
                    int errorCode = FileStreamHelpers.GetLastWin32ErrorAndDisposeHandleIfInvalid(_fileHandle);
                    if (errorCode == Interop.Errors.ERROR_IO_PENDING)
                    {
                        // Common case: IO was initiated, completion will be handled by callback.
                        // Register for cancellation now that the operation has been initiated.
                        vts.RegisterForCancellation(cancellationToken);
                    }
                    else
                    {
                        // Error. Callback will not be invoked.
                        vts.Dispose();
                        return(errorCode == Interop.Errors.ERROR_NO_DATA ? // EOF on a pipe. IO callback will not be called.
                               ValueTask.CompletedTask :
                               ValueTask.FromException(HandleIOError(positionBefore, errorCode)));
                    }
                }
            }
            catch
            {
                vts.Dispose();
                throw;
            }

            // Completion handled by callback.
            vts.FinishedScheduling();
            return(new ValueTask(vts, vts.Version));
        }
Exemple #2
0
        private unsafe ValueTask <int> ReadAsyncInternal(Memory <byte> destination, CancellationToken cancellationToken = default)
        {
            if (!CanRead)
            {
                ThrowHelper.ThrowNotSupportedException_UnreadableStream();
            }

            // Rent the reusable ValueTaskSource, or create a new one to use if we couldn't get one (which
            // should only happen on first use or if the FileStream is being used concurrently).
            ValueTaskSource vts = Interlocked.Exchange(ref _reusableValueTaskSource, null) ?? new ValueTaskSource(this);

            try
            {
                NativeOverlapped *nativeOverlapped = vts.PrepareForOperation(destination);
                Debug.Assert(vts._memoryHandle.Pointer != null);

                // Calculate position in the file we should be at after the read is done
                long positionBefore = _filePosition;
                if (CanSeek)
                {
                    long len = Length;

                    if (positionBefore + destination.Length > len)
                    {
                        destination = positionBefore <= len?
                                      destination.Slice(0, (int)(len - positionBefore)) :
                                          default;
                    }

                    // Now set the position to read from in the NativeOverlapped struct
                    // For pipes, we should leave the offset fields set to 0.
                    nativeOverlapped->OffsetLow  = unchecked ((int)positionBefore);
                    nativeOverlapped->OffsetHigh = (int)(positionBefore >> 32);

                    // When using overlapped IO, the OS is not supposed to
                    // touch the file pointer location at all.  We will adjust it
                    // ourselves, but only in memory. This isn't threadsafe.
                    _filePosition += destination.Length;
                }

                // Queue an async ReadFile operation.
                if (Interop.Kernel32.ReadFile(_fileHandle, (byte *)vts._memoryHandle.Pointer, destination.Length, IntPtr.Zero, nativeOverlapped) == 0)
                {
                    // The operation failed, or it's pending.
                    int errorCode = FileStreamHelpers.GetLastWin32ErrorAndDisposeHandleIfInvalid(_fileHandle);
                    switch (errorCode)
                    {
                    case Interop.Errors.ERROR_IO_PENDING:
                        // Common case: IO was initiated, completion will be handled by callback.
                        // Register for cancellation now that the operation has been initiated.
                        vts.RegisterForCancellation(cancellationToken);
                        break;

                    case Interop.Errors.ERROR_BROKEN_PIPE:
                        // EOF on a pipe. Callback will not be called.
                        // We clear the overlapped status bit for this special case (failure
                        // to do so looks like we are freeing a pending overlapped later).
                        nativeOverlapped->InternalLow = IntPtr.Zero;
                        vts.Dispose();
                        return(ValueTask.FromResult(0));

                    default:
                        // Error. Callback will not be called.
                        vts.Dispose();
                        return(ValueTask.FromException <int>(HandleIOError(positionBefore, errorCode)));
                    }
                }
            }
            catch
            {
                vts.Dispose();
                throw;
            }

            // Completion handled by callback.
            vts.FinishedScheduling();
            return(new ValueTask <int>(vts, vts.Version));
        }