private static unsafe int ReadSyncUsingAsyncHandle(SafeFileHandle handle, Span <byte> buffer, long fileOffset) { handle.EnsureThreadPoolBindingInitialized(); CallbackResetEvent resetEvent = new CallbackResetEvent(handle.ThreadPoolBinding !); NativeOverlapped * overlapped = null; try { overlapped = GetNativeOverlappedForAsyncHandle(handle, fileOffset, resetEvent); fixed(byte *pinned = &MemoryMarshal.GetReference(buffer)) { Interop.Kernel32.ReadFile(handle, pinned, buffer.Length, IntPtr.Zero, overlapped); int errorCode = FileStreamHelpers.GetLastWin32ErrorAndDisposeHandleIfInvalid(handle); if (errorCode == Interop.Errors.ERROR_IO_PENDING) { resetEvent.WaitOne(); errorCode = Interop.Errors.ERROR_SUCCESS; } if (errorCode == Interop.Errors.ERROR_SUCCESS) { int result = 0; if (Interop.Kernel32.GetOverlappedResult(handle, overlapped, ref result, bWait: false)) { Debug.Assert(result >= 0 && result <= buffer.Length, $"GetOverlappedResult returned {result} for {buffer.Length} bytes request"); return(result); } errorCode = FileStreamHelpers.GetLastWin32ErrorAndDisposeHandleIfInvalid(handle); } switch (errorCode) { case Interop.Errors.ERROR_HANDLE_EOF: // logically success with 0 bytes read (read at end of file) 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). overlapped->InternalLow = IntPtr.Zero; return(0); default: throw Win32Marshal.GetExceptionForWin32Error(errorCode, handle.Path); } } } finally { if (overlapped != null) { resetEvent.FreeNativeOverlapped(overlapped); } resetEvent.Dispose(); } }
private static unsafe (SafeFileHandle.OverlappedValueTaskSource?vts, int errorCode) QueueAsyncReadFile(SafeFileHandle handle, Memory <byte> buffer, long fileOffset, CancellationToken cancellationToken, OSFileStreamStrategy?strategy) { handle.EnsureThreadPoolBindingInitialized(); SafeFileHandle.OverlappedValueTaskSource vts = handle.GetOverlappedValueTaskSource(); int errorCode = Interop.Errors.ERROR_SUCCESS; try { NativeOverlapped *nativeOverlapped = vts.PrepareForOperation(buffer, fileOffset, strategy); Debug.Assert(vts._memoryHandle.Pointer != null); // Queue an async ReadFile operation. if (Interop.Kernel32.ReadFile(handle, (byte *)vts._memoryHandle.Pointer, buffer.Length, IntPtr.Zero, nativeOverlapped) == 0) { // The operation failed, or it's pending. errorCode = FileStreamHelpers.GetLastWin32ErrorAndDisposeHandleIfInvalid(handle); 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_HANDLE_EOF: // logically success with 0 bytes read (read at end of file) 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(null, 0); default: // Error. Callback will not be called. vts.Dispose(); return(null, errorCode); } } } catch { vts.Dispose(); throw; } finally { if (errorCode != Interop.Errors.ERROR_IO_PENDING && errorCode != Interop.Errors.ERROR_SUCCESS) { strategy?.OnIncompleteOperation(buffer.Length, 0); } } // Completion handled by callback. vts.FinishedScheduling(); return(vts, -1); }
private static unsafe (SafeFileHandle.OverlappedValueTaskSource?vts, int errorCode) QueueAsyncWriteFile(SafeFileHandle handle, ReadOnlyMemory <byte> buffer, long fileOffset, CancellationToken cancellationToken, OSFileStreamStrategy?strategy) { handle.EnsureThreadPoolBindingInitialized(); SafeFileHandle.OverlappedValueTaskSource vts = handle.GetOverlappedValueTaskSource(); int errorCode = Interop.Errors.ERROR_SUCCESS; try { NativeOverlapped *nativeOverlapped = vts.PrepareForOperation(buffer, fileOffset, strategy); Debug.Assert(vts._memoryHandle.Pointer != null); // Queue an async WriteFile operation. if (Interop.Kernel32.WriteFile(handle, (byte *)vts._memoryHandle.Pointer, buffer.Length, IntPtr.Zero, nativeOverlapped) == 0) { // The operation failed, or it's pending. errorCode = FileStreamHelpers.GetLastWin32ErrorAndDisposeHandleIfInvalid(handle); 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_NO_DATA: // EOF on a pipe. IO callback will not be called. vts.Dispose(); return(null, 0); default: // Error. Callback will not be invoked. vts.Dispose(); return(null, errorCode); } } } catch { vts.Dispose(); throw; } finally { if (errorCode != Interop.Errors.ERROR_IO_PENDING && errorCode != Interop.Errors.ERROR_SUCCESS) { strategy?.OnIncompleteOperation(buffer.Length, 0); } } // Completion handled by callback. vts.FinishedScheduling(); return(vts, -1); }
private static unsafe ValueTask <int> ReadFileScatterAsync(SafeFileHandle handle, MemoryHandle pinnedSegments, int bytesToRead, long fileOffset, CancellationToken cancellationToken) { handle.EnsureThreadPoolBindingInitialized(); SafeFileHandle.OverlappedValueTaskSource vts = handle.GetOverlappedValueTaskSource(); try { NativeOverlapped *nativeOverlapped = vts.PrepareForOperation(Memory <byte> .Empty, fileOffset); Debug.Assert(pinnedSegments.Pointer != null); if (Interop.Kernel32.ReadFileScatter(handle, (long *)pinnedSegments.Pointer, bytesToRead, IntPtr.Zero, nativeOverlapped) == 0) { // The operation failed, or it's pending. int errorCode = FileStreamHelpers.GetLastWin32ErrorAndDisposeHandleIfInvalid(handle); 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_HANDLE_EOF: // logically success with 0 bytes read (read at end of file) 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>(Win32Marshal.GetExceptionForWin32Error(errorCode, handle.Path))); } } } catch { vts.Dispose(); throw; } // Completion handled by callback. vts.FinishedScheduling(); return(new ValueTask <int>(vts, vts.Version)); }
private static unsafe void WriteSyncUsingAsyncHandle(SafeFileHandle handle, ReadOnlySpan <byte> buffer, long fileOffset) { if (buffer.IsEmpty) { return; } handle.EnsureThreadPoolBindingInitialized(); CallbackResetEvent resetEvent = new CallbackResetEvent(handle.ThreadPoolBinding !); NativeOverlapped * overlapped = null; try { overlapped = GetNativeOverlappedForAsyncHandle(handle, fileOffset, resetEvent); fixed(byte *pinned = &MemoryMarshal.GetReference(buffer)) { Interop.Kernel32.WriteFile(handle, pinned, buffer.Length, IntPtr.Zero, overlapped); int errorCode = FileStreamHelpers.GetLastWin32ErrorAndDisposeHandleIfInvalid(handle); if (errorCode == Interop.Errors.ERROR_IO_PENDING) { resetEvent.WaitOne(); errorCode = Interop.Errors.ERROR_SUCCESS; } if (errorCode == Interop.Errors.ERROR_SUCCESS) { int result = 0; if (Interop.Kernel32.GetOverlappedResult(handle, overlapped, ref result, bWait: false)) { Debug.Assert(result == buffer.Length, $"GetOverlappedResult returned {result} for {buffer.Length} bytes request"); return; } errorCode = FileStreamHelpers.GetLastWin32ErrorAndDisposeHandleIfInvalid(handle); } switch (errorCode) { case Interop.Errors.ERROR_NO_DATA: // For pipes, ERROR_NO_DATA is not an error, but the pipe is closing. return; case Interop.Errors.ERROR_INVALID_PARAMETER: // ERROR_INVALID_PARAMETER may be returned for writes // where the position is too large or for synchronous writes // to a handle opened asynchronously. throw new IOException(SR.IO_FileTooLong); default: throw Win32Marshal.GetExceptionForWin32Error(errorCode, handle.Path); } } } finally { if (overlapped != null) { resetEvent.FreeNativeOverlapped(overlapped); } resetEvent.Dispose(); } }