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)); }
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)); }