[System.Security.SecuritySafeCritical] // auto-generated unsafe private Task<int> ReadInternalCoreAsync(byte[] bytes, int offset, int numBytes, int numBufferedBytesRead, CancellationToken cancellationToken) { Debug.Assert(!_handle.IsClosed, "!_handle.IsClosed"); Debug.Assert(_parent.CanRead, "_parent.CanRead"); Debug.Assert(bytes != null, "bytes != null"); Debug.Assert(_writePos == 0, "_writePos == 0"); Debug.Assert(_isAsync, "ReadInternalCoreAsync doesn't work on synchronous file streams!"); Debug.Assert(offset >= 0, "offset is negative"); Debug.Assert(numBytes >= 0, "numBytes is negative"); // Create and store async stream class library specific data in the async result FileStreamCompletionSource completionSource = new FileStreamCompletionSource(this, numBufferedBytesRead, bytes, cancellationToken); NativeOverlapped* intOverlapped = completionSource.Overlapped; // Calculate position in the file we should be at after the read is done if (_parent.CanSeek) { long len = _parent.Length; // Make sure we are reading from the position that we think we are if (_exposedHandle) VerifyOSHandlePosition(); if (_pos + numBytes > len) { if (_pos <= len) numBytes = (int)(len - _pos); else numBytes = 0; } // Now set the position to read from in the NativeOverlapped struct // For pipes, we should leave the offset fields set to 0. intOverlapped->OffsetLow = unchecked((int)_pos); intOverlapped->OffsetHigh = (int)(_pos >> 32); // When using overlapped IO, the OS is not supposed to // touch the file pointer location at all. We will adjust it // ourselves. This isn't threadsafe. // WriteFile should not update the file pointer when writing // in overlapped mode, according to MSDN. But it does update // the file pointer when writing to a UNC path! // So changed the code below to seek to an absolute // location, not a relative one. ReadFile seems consistent though. SeekCore(numBytes, SeekOrigin.Current); } // queue an async ReadFile operation and pass in a packed overlapped int errorCode = 0; int r = ReadFileNative(_handle, bytes, offset, numBytes, intOverlapped, out errorCode); // ReadFile, the OS version, will return 0 on failure. But // my ReadFileNative wrapper returns -1. My wrapper will return // the following: // On error, r==-1. // On async requests that are still pending, r==-1 w/ errorCode==ERROR_IO_PENDING // on async requests that completed sequentially, r==0 // You will NEVER RELIABLY be able to get the number of bytes // 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 && numBytes != -1) { // For pipes, when they hit EOF, they will come here. if (errorCode == ERROR_BROKEN_PIPE) { // Not an error, but EOF. AsyncFSCallback will NOT be // called. Call the user callback here. // We clear the overlapped status bit for this special case. // Failure to do so looks like we are freeing a pending overlapped later. intOverlapped->InternalLow = IntPtr.Zero; completionSource.SetCompletedSynchronously(0); } else if (errorCode != ERROR_IO_PENDING) { if (!_handle.IsClosed && _parent.CanSeek) // Update Position - It could be anywhere. { SeekCore(0, SeekOrigin.Current); } completionSource.ReleaseNativeResource(); if (errorCode == ERROR_HANDLE_EOF) { throw Error.GetEndOfFile(); } else { throw Win32Marshal.GetExceptionForWin32Error(errorCode); } } else { // Only once the IO is pending do we register for cancellation completionSource.RegisterForCancellation(); } } else { // Due to a workaround for a race condition in NT's ReadFile & // WriteFile routines, we will always be returning 0 from ReadFileNative // when we do async IO instead of the number of bytes read, // irregardless of whether the operation completed // synchronously or asynchronously. We absolutely must not // set asyncResult._numBytes here, since will never have correct // results. //Console.WriteLine("ReadFile returned: "+r+" (0x"+Int32.Format(r, "x")+") The IO completed synchronously, but the user callback was called on a separate thread"); } return completionSource.Task; }
[System.Security.SecuritySafeCritical] // auto-generated private unsafe Task WriteInternalCoreAsync(byte[] bytes, int offset, int numBytes, CancellationToken cancellationToken) { Debug.Assert(!_handle.IsClosed, "!_handle.IsClosed"); Debug.Assert(_parent.CanWrite, "_parent.CanWrite"); Debug.Assert(bytes != null, "bytes != null"); Debug.Assert(_readPos == _readLen, "_readPos == _readLen"); Debug.Assert(_isAsync, "WriteInternalCoreAsync doesn't work on synchronous file streams!"); Debug.Assert(offset >= 0, "offset is negative"); Debug.Assert(numBytes >= 0, "numBytes is negative"); // Create and store async stream class library specific data in the async result FileStreamCompletionSource completionSource = new FileStreamCompletionSource(this, 0, bytes, cancellationToken); NativeOverlapped* intOverlapped = completionSource.Overlapped; if (_parent.CanSeek) { // Make sure we set the length of the file appropriately. long len = _parent.Length; //Console.WriteLine("WriteInternalCoreAsync - Calculating end pos. pos: "+pos+" len: "+len+" numBytes: "+numBytes); // Make sure we are writing to the position that we think we are if (_exposedHandle) VerifyOSHandlePosition(); if (_pos + numBytes > len) { //Console.WriteLine("WriteInternalCoreAsync - Setting length to: "+(pos + numBytes)); SetLengthCore(_pos + numBytes); } // Now set the position to read from in the NativeOverlapped struct // For pipes, we should leave the offset fields set to 0. intOverlapped->OffsetLow = (int)_pos; intOverlapped->OffsetHigh = (int)(_pos >> 32); // When using overlapped IO, the OS is not supposed to // touch the file pointer location at all. We will adjust it // ourselves. This isn't threadsafe. SeekCore(numBytes, SeekOrigin.Current); } //Console.WriteLine("WriteInternalCoreAsync finishing. pos: "+pos+" numBytes: "+numBytes+" _pos: "+_pos+" Position: "+Position); int errorCode = 0; // queue an async WriteFile operation and pass in a packed overlapped int r = WriteFileNative(_handle, bytes, offset, numBytes, intOverlapped, out errorCode); // WriteFile, the OS version, will return 0 on failure. But // my WriteFileNative wrapper returns -1. My wrapper will return // the following: // On error, r==-1. // On async requests that are still pending, r==-1 w/ errorCode==ERROR_IO_PENDING // On async requests that completed sequentially, r==0 // You will NEVER RELIABLY be able to get the number of bytes // written back from this call when using overlapped IO! You must // not pass in a non-null lpNumBytesWritten to WriteFile when using // overlapped structures! This is ByDesign NT behavior. if (r == -1 && numBytes != -1) { //Console.WriteLine("WriteFile returned 0; Write will complete asynchronously (if errorCode==3e5) errorCode: 0x{0:x}", errorCode); // For pipes, when they are closed on the other side, they will come here. if (errorCode == ERROR_NO_DATA) { // Not an error, but EOF. AsyncFSCallback will NOT be called. // Completing TCS and return cached task allowing the GC to collect TCS. completionSource.SetCompletedSynchronously(0); return Task.CompletedTask; } else if (errorCode != ERROR_IO_PENDING) { if (!_handle.IsClosed && _parent.CanSeek) // Update Position - It could be anywhere. { SeekCore(0, SeekOrigin.Current); } completionSource.ReleaseNativeResource(); if (errorCode == ERROR_HANDLE_EOF) { throw Error.GetEndOfFile(); } else { throw Win32Marshal.GetExceptionForWin32Error(errorCode); } } else // ERROR_IO_PENDING { // Only once the IO is pending do we register for cancellation completionSource.RegisterForCancellation(); } } else { // Due to a workaround for a race condition in NT's ReadFile & // WriteFile routines, we will always be returning 0 from WriteFileNative // when we do async IO instead of the number of bytes written, // irregardless of whether the operation completed // synchronously or asynchronously. We absolutely must not // set asyncResult._numBytes here, since will never have correct // results. //Console.WriteLine("WriteFile returned: "+r+" (0x"+Int32.Format(r, "x")+") The IO completed synchronously, but the user callback was called on another thread."); } return completionSource.Task; }
// Instance method to help code external to this MarshalByRefObject avoid // accessing its fields by ref. This avoids a compiler warning. private FileStreamCompletionSource CompareExchangeCurrentOverlappedOwner(FileStreamCompletionSource newSource, FileStreamCompletionSource existingSource) => Interlocked.CompareExchange(ref _currentOverlappedOwner, newSource, existingSource);