public void OnCompletion(FileAsyncIOResult asyncIOResult) { m_result = asyncIOResult; Contract.Assume(m_pinningHandle.IsAllocated); m_pinningHandle.Free(); m_event.Set(); }
/// <inheritdoc /> public unsafe Overlapped *ReadFileOverlapped( IIOCompletionTarget target, SafeFileHandle handle, byte *pinnedBuffer, int bytesToRead, long fileOffset) { Contract.Requires(target != null); Contract.Requires(handle != null && !handle.IsInvalid); Contract.Requires(pinnedBuffer != null); TaggedOverlapped *overlapped = m_overlappedPool.ReserveOverlappedWithTarget(target); TraceStartIfEnabled(overlapped, target); bool needOverlappedRelease = true; bool overlappedHasBeenReleased = false; try { FileAsyncIOResult result = FileSystemWin.ReadFileOverlapped( handle, pinnedBuffer, bytesToRead, fileOffset, (Overlapped *)overlapped); if (result.Status != FileAsyncIOStatus.Pending) { Contract.Assert( result.Status == FileAsyncIOStatus.Succeeded || result.Status == FileAsyncIOStatus.Failed); // We could call the target directly. // However, since the target may itself issue more I/O, we need to prevent unbounded stack growth. // TODO: We could set a recursion-limit to allow some fraction of repeated IOs to complete synchronously, without // queueing to the threadpool. Sync completions are the common case for files cached in memory. ReleaseOvelappedAndQueueCompletionNotification(overlapped, result); overlappedHasBeenReleased = true; } // At this point overlapped is either needed (pending status) // or already released by ReleaseOvelappedAndQueueCompletionNotification needOverlappedRelease = false; return(!overlappedHasBeenReleased ? (Overlapped *)overlapped : null); } finally { if (needOverlappedRelease) { IIOCompletionTarget releasedTarget = ReleaseOverlappedAndGetTarget(overlapped); Contract.Assume(releasedTarget == target); } } }
private unsafe void QueueCompletionNotification(IIOCompletionTarget target, FileAsyncIOResult result) { var notificationArgs = new IOCompletionNotificationArgs { Result = result, Target = target, }; ThreadPool.QueueUserWorkItem( state => { var stateArgs = (IOCompletionNotificationArgs)state; stateArgs.Target.OnCompletion(stateArgs.Result); }, notificationArgs); }
/// <inheritdoc /> public unsafe Overlapped *ReadOverlapped( IIOCompletionTarget target, byte *pinnedBuffer, int bytesToRead, long fileOffset) { FileAsyncIOResult result; if (GetCurrentLength() == 0 || fileOffset >= GetCurrentLength()) { result = new FileAsyncIOResult(FileAsyncIOStatus.Failed, bytesTransferred: 0, error: NativeIOConstants.ErrorHandleEof); } else { lock (m_fileStream) { try { byte[] buffer = new byte[bytesToRead]; m_fileStream.Seek(fileOffset, SeekOrigin.Begin); int bytesRead = m_fileStream.Read(buffer, 0, bytesToRead); for (int i = 0; i < bytesRead; i++) { pinnedBuffer[i] = buffer[i]; } result = new FileAsyncIOResult(FileAsyncIOStatus.Succeeded, bytesTransferred: bytesRead, error: NativeIOConstants.ErrorSuccess); } catch (Exception ex) { int errorCode = (int)NativeErrorCodeForException(ex); result = new FileAsyncIOResult(FileAsyncIOStatus.Failed, bytesTransferred: 0, error: errorCode); } } } QueueCompletionNotification(target, result); return(null); }
public async Task ReadEmptyFile() { string path = GetFullPath("file"); using (File.Create(path)) { } #if NET_FRAMEWORK using (var io = new IOCompletionManager()) #else IIOCompletionManager io = null; #endif { using ( IAsyncFile file = AsyncFileFactory.CreateOrOpen( path, FileDesiredAccess.GenericRead, FileShare.Read | FileShare.Delete, FileMode.Open, FileFlagsAndAttributes.None, io)) { XAssert.IsTrue(file.CanRead); XAssert.IsFalse(file.CanWrite); var buffer = new byte[10]; FileAsyncIOResult result = await file.ReadAsync(buffer, buffer.Length, 0); XAssert.AreEqual(FileAsyncIOStatus.Failed, result.Status); XAssert.IsTrue(result.ErrorIndicatesEndOfFile); result = await file.ReadAsync(buffer, buffer.Length, 16); XAssert.AreEqual(FileAsyncIOStatus.Failed, result.Status); XAssert.IsTrue(result.ErrorIndicatesEndOfFile); } } }
public void ReadFileToEnd() { const int NumberOfWords = 1024 * 1024; const int TotalSize = NumberOfWords * 4; string path = GetFullPath("file"); using (var fs = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Delete)) using (var writer = new BinaryWriter(fs, Encoding.UTF8, leaveOpen: true)) { for (int i = 0; i < NumberOfWords; i++) { writer.Write((int)i); } } using (var memoryStream = new MemoryStream(capacity: NumberOfWords * 4)) { using (var io = new IOCompletionManager()) using (var target = new BlockingCompletionTarget(bufferSize: 64 * 1024)) using ( SafeFileHandle handle = CreateOrOpenFile( path, FileDesiredAccess.GenericRead, FileShare.Read | FileShare.Delete, FileMode.Open)) { io.BindFileHandle(handle); while (true) { unsafe { io.ReadFileOverlapped(target, handle, target.PinBuffer(), target.Buffer.Length, fileOffset: memoryStream.Length); } FileAsyncIOResult result = target.Wait(); if (result.Status == FileAsyncIOStatus.Failed) { XAssert.IsTrue(result.ErrorIndicatesEndOfFile, "Unexpected non-EOF read failure."); break; } else { XAssert.AreEqual(FileAsyncIOStatus.Succeeded, result.Status); } XAssert.AreNotEqual(0, result.BytesTransferred); XAssert.IsTrue( memoryStream.Length + result.BytesTransferred <= TotalSize, "Too many bytes read; Read {0} so far and just got another {1} bytes", memoryStream.Length, result.BytesTransferred); memoryStream.Write(target.Buffer, 0, result.BytesTransferred); } } memoryStream.Position = 0; using (var reader = new BinaryReader(memoryStream, Encoding.UTF8, leaveOpen: true)) { for (int i = 0; i < NumberOfWords; i++) { XAssert.AreEqual(i, reader.ReadInt32(), "File contents read incorrectly; offset {0}", i * 4); } } } }
public void ConcurrentReadsSingleBatch() { const int NumberOfConcurrentReads = 128; // This should be larger than the batch size at which we allocate OVERLAPPED structures. WithPipePair( (server, client) => { using (var io = new IOCompletionManager()) { io.BindFileHandle(client); var targets = new BlockingCompletionTarget[NumberOfConcurrentReads]; var completions = new bool[NumberOfConcurrentReads]; var bufferToWrite = new byte[NumberOfConcurrentReads * 4]; using (var writeBufferStream = new MemoryStream(bufferToWrite, writable: true)) { using (var writer = new BinaryWriter(writeBufferStream)) { for (int i = 0; i < NumberOfConcurrentReads; i++) { writer.Write((int)i); } } } try { for (int i = 0; i < targets.Length; i++) { targets[i] = new BlockingCompletionTarget(bufferSize: 8); unsafe { // Note that non-seekable streams mandate an offset of 0 for all requests. // https://msdn.microsoft.com/en-us/library/windows/desktop/ms684342%28v=vs.85%29.aspx io.ReadFileOverlapped(targets[i], client, targets[i].PinBuffer(), 4, fileOffset: 0); } } server.Write(bufferToWrite, 0, bufferToWrite.Length); server.Flush(); for (int i = 0; i < targets.Length; i++) { FileAsyncIOResult result = targets[i].Wait(); XAssert.AreEqual(FileAsyncIOStatus.Succeeded, result.Status); XAssert.AreEqual(result.BytesTransferred, 4); int completedValue = BitConverter.ToInt32(targets[i].Buffer, 0); XAssert.IsTrue(completedValue >= 0 && completedValue < completions.Length); XAssert.IsFalse(completions[completedValue], "Value {0} completed multiple times", completedValue); completions[completedValue] = true; } } finally { foreach (BlockingCompletionTarget target in targets) { if (target != null) { target.Dispose(); } } } } }); }
/// <summary> /// This is the async callback function. Only one thread could/should call this. /// </summary> void IIOCompletionTarget.OnCompletion(FileAsyncIOResult asyncIOResult) { Contract.Assume(asyncIOResult.Status != FileAsyncIOStatus.Pending); bool cancel = false; lock (m_lock) { switch (m_state) { case State.Initialized: Contract.Assume(false, "Read completion can only happen once in the Reading state"); throw new InvalidOperationException("Unreachable"); case State.Reading: break; case State.Stopping: // If Stopping, we had to defer reaching Stopped until an outstanding read completed - which is now. // We throw away remaining data in the pipe (call WaitForEofAsync if all data is needed before dispose). // Any current waiters for WaitForEofAsync will also resume (though possibly with an exception, due to this pre-EOF cancelation). OnStopped(); // Signaling cancelation needs to happen outside of the lock (otherwise continuations on the completion source // may run synchronously, and have the lock by accident). cancel = true; break; case State.Stopped: Contract.Assume(false, "The Stopped state should not be reached while a read is outstanding"); throw new InvalidOperationException("Unreachable"); default: throw Contract.AssertFailure("Unhandled state"); } } if (cancel) { SignalCompletion(reachedEof: false); return; } int byteLen; if (asyncIOResult.Status == FileAsyncIOStatus.Failed) { // Treat failures as EOF. // TODO: This is a bad thing to do, but is what the original AsyncStreamReader was doing. byteLen = 0; } else { Contract.Assume(!asyncIOResult.ErrorIndicatesEndOfFile, "End-of-file appears as a failure status"); byteLen = asyncIOResult.BytesTransferred; Contract.Assume(byteLen > 0); } if (byteLen == 0) { // We're at EOF, we won't call this function again from here on. lock (m_lock) { if (StringBuilderInstace.Length != 0) { m_messageQueue.Enqueue(StringBuilderInstace.ToString()); StringBuilderInstace.Length = 0; } m_messageQueue.Enqueue(null); } try { // UserCallback could throw, but we should still signal EOF try { FlushMessageQueue(); } #pragma warning disable ERP022 // Unobserved exception in generic exception handler catch { // We are in an invalid state (maybe the process got killed or crashed). // As the comment above says the could throw. // Make sure we are catching the exception without crashing. } #pragma warning restore ERP022 // Unobserved exception in generic exception handler } finally { lock (m_lock) { // No more reads or callbacks will occur, so enter the Stopped state and clean up. OnStopped(); } m_messageQueue = null; SignalCompletion(reachedEof: true); } } else { int charLen = m_decoder.GetChars(ByteBuffer, 0, byteLen, CharBuffer, 0); GetLinesFromCharBuffers(charLen); // File offset is ignored since we're reading a pipe. m_file.ReadOverlapped(this, m_byteBufferPtr, m_byteBufferSize, fileOffset: 0); } }
public async Task ReadFileRandomAccess() { const int NumberOfReads = 16; const int NumberOfWordsPerRead = 64 * 1024; const int NumberOfBytesPerRead = NumberOfWordsPerRead * 4; const int NumberOfWords = NumberOfWordsPerRead * NumberOfReads; const int TotalSize = NumberOfWords * 4; string path = GetFullPath("file"); using (var fs = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Delete)) { using (var writer = new BinaryWriter(fs, Encoding.UTF8, leaveOpen: true)) { for (int i = 0; i < NumberOfWords; i++) { writer.Write((int)i); } } } #if NET_FRAMEWORK using (var io = new IOCompletionManager()) #else IIOCompletionManager io = null; #endif { using ( IAsyncFile file = AsyncFileFactory.CreateOrOpen( path, FileDesiredAccess.GenericRead, FileShare.Read | FileShare.Delete, FileMode.Open, FileFlagsAndAttributes.None, io)) { XAssert.IsTrue(file.CanRead); XAssert.IsFalse(file.CanWrite); var readBuffer = new byte[TotalSize]; var readTasks = new Task[NumberOfReads]; for (int i = 0; i < readTasks.Length; i++) { int offset = NumberOfBytesPerRead * i; readTasks[i] = Task.Run( async() => { byte[] localBuffer = new byte[NumberOfBytesPerRead]; int readSoFar = 0; while (readSoFar < NumberOfBytesPerRead) { FileAsyncIOResult result = await file.ReadAsync(localBuffer, bytesToRead: NumberOfBytesPerRead - readSoFar, fileOffset: offset + readSoFar); XAssert.AreEqual(FileAsyncIOStatus.Succeeded, result.Status); XAssert.IsTrue(result.BytesTransferred > 0); XAssert.IsTrue(readSoFar + result.BytesTransferred <= NumberOfBytesPerRead); Buffer.BlockCopy(localBuffer, 0, readBuffer, offset + readSoFar, result.BytesTransferred); readSoFar += result.BytesTransferred; } Contract.Assert(readSoFar == NumberOfBytesPerRead); }); } for (int i = 0; i < readTasks.Length; i++) { await readTasks[i]; } using (var reader = new BinaryReader(new MemoryStream(readBuffer, writable: false), Encoding.UTF8, leaveOpen: false)) { for (int i = 0; i < NumberOfWords; i++) { XAssert.AreEqual(i, reader.ReadInt32()); } } } } }
private unsafe void ReleaseOvelappedAndQueueCompletionNotification(TaggedOverlapped *overlapped, FileAsyncIOResult result) { // We can now find the external-code callback that needs notification for this request. // We can't allow it to block this thread, which is dedicated to servicing the completion port. // Were it to block, we could starve I/O completion; blocking on other I/O completions could be a deadlock. // So, we guarantee that IIOCompletionTargets run on thread-pool threads - they suffer no special restrictions. IIOCompletionTarget target = ReleaseOverlappedAndGetTarget(overlapped); var notificationArgs = new IOCompletionNotificationArgs { Result = result, Target = target, }; ThreadPool.QueueUserWorkItem( state => { var stateArgs = (IOCompletionNotificationArgs)state; stateArgs.Target.OnCompletion(stateArgs.Result); }, notificationArgs); }