private async Task FlushOrDiscardBufferAndResetPositionAsync(StreamOperationToken token, BackgroundOperationSlot slot, long newPosition) { Contract.Requires(slot.Usability != StreamUsability.Broken); Analysis.IgnoreArgument(token); m_position = newPosition; await FlushOrDiscardBufferAsync(slot); lock (m_backgroundOperationLock) { // Buffer is now empty, and so its position should be in sync with the virtual position (recall how both begin at zero on stream open). m_bufferPosition = m_position; // Buffer position changed, so an end-of-file indication is no longer valid. if (m_usability == StreamUsability.EndOfFileReached) { m_usability = StreamUsability.Usable; } else { Contract.Assume(m_usability == StreamUsability.Usable, "m_usability == slot.Usability is not Broken"); } } }
void IIOCompletionTarget.OnCompletion(FileAsyncIOResult result) { // Note that this may be called on the same stack as StartBackgroundOperation (sync completion), Contract.Assume(result.Status != FileAsyncIOStatus.Pending); bool failed = result.Status == FileAsyncIOStatus.Failed; if (!failed) { // Note, that FileAsyncIOResult constructor can't enforce this check because in some other cases // 0 transfered bytes is ok for a successful IO operation. Contract.Assert(result.BytesTransferred > 0, "Zero bytes transferred is a failure indication (otherwise can be confused with EOF)"); } TaskCompletionSource <BackgroundOperationSlot> completionSource; lock (m_backgroundOperationLock) { // Capture the completion source to use outside of the lock. completionSource = m_currentBackgroundOperationCompletionSource; switch (m_currentBackgroundOperation) { case StreamBackgroundOperation.Fill: m_bufferPosition += result.BytesTransferred; internalBuffer.FinishFillAndUnlock(numberOfBytesFilled: result.BytesTransferred); break; case StreamBackgroundOperation.Flush: m_bufferPosition += result.BytesTransferred; internalBuffer.FinishFlushAndUnlock(numberOfBytesFlushed: result.BytesTransferred); break; case StreamBackgroundOperation.None: Contract.Assume(false, "Unexpected I/O completion (no background operation in progress)"); throw new InvalidOperationException("Unreachable"); default: throw Contract.AssertFailure("Unhandled StreamBackgroundOperation"); } if (failed) { StreamUsability newUsability; if (result.ErrorIndicatesEndOfFile) { newUsability = StreamUsability.EndOfFileReached; } else { newUsability = StreamUsability.Broken; m_brokenStreamException = new IOException( "An error occurred while reading or writing to a file stream. The stream will no longer be usable.", new NativeWin32Exception(result.Error)); } m_usability = newUsability; } else { Contract.Assume(m_usability == StreamUsability.Usable); } m_currentBackgroundOperation = StreamBackgroundOperation.None; m_currentBackgroundOperationCompletionSource = null; } // Since the lock is no longer held, it is safe to resume any waiters (note that they may run on this stack). if (completionSource != null) { completionSource.SetResult(new BackgroundOperationSlot(this)); } }