Example #1
0
        /// <inheritdoc />
        protected override void Dispose(bool disposing)
        {
            base.Dispose(disposing);

            if (disposing)
            {
                using (StreamOperationToken op = StartNewOperation(StreamOperation.Dispose))
                {
                    BackgroundOperationSlot slot = op.WaitForBackgroundOperationSlot();
                    if (slot.Usability != StreamUsability.Broken)
                    {
                        FlushOrDiscardBufferAsync(slot).GetAwaiter().GetResult();
                    }

                    if (m_ownsFile)
                    {
                        m_file.Close();
                    }

                    if (slot.Usability == StreamUsability.Broken)
                    {
                        throw slot.ThrowExceptionForBrokenStream();
                    }
                }
            }

            // TODO: Consider FailFast for the !disposing (finalizer) case.
            internalBuffer.Dispose();
        }
Example #2
0
        /// <summary>
        /// Adjusts the current stream position based on having read or written data in the buffer.
        /// </summary>
        protected void AdvancePosition(StreamOperationToken token, int advance)
        {
            Contract.Requires(advance >= 0);
            Analysis.IgnoreArgument(token);

            m_position += advance;
            Contract.Assume(m_position >= 0);
        }
Example #3
0
        /// <inheritdoc />
        public override async Task <int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
        {
            using (StreamOperationToken op = StartNewOperation(StreamOperation.Read))
            {
                BackgroundOperationSlot backgroundOperationSlot = await op.WaitForBackgroundOperationSlotAsync();

                Contract.Assume(
                    internalBuffer.State != FileBuffer.BufferState.Locked,
                    "Buffer should not be locked, since no background operation is running.");

                int  bytesReadFromBuffer;
                bool fillStarted = ReadBufferAndStartFillIfEmptiedButUsable(backgroundOperationSlot, buffer, offset, count, out bytesReadFromBuffer);

                if (bytesReadFromBuffer == 0)
                {
                    if (fillStarted)
                    {
                        backgroundOperationSlot = await op.WaitForBackgroundOperationSlotAsync();
                    }

                    // We just ran a fill and waited for it (usability may be updated on its completion) or we were unable to start a fill.
                    // In either case, we should now respond to usability. We don't have any bytes read, and so we are now in some sense at the position
                    // where the unusability occurs, rather than behind it (e.g. we should quietly exhaust the buffer before complaining about EOF).
                    switch (backgroundOperationSlot.Usability)
                    {
                    case StreamUsability.Usable:
                        Contract.Assume(
                            fillStarted,
                            "ReadBufferAndStartFillIfEmptiedButUsable should have started a fill, since the stream is usable");
                        Analysis.IgnoreResult(
                            ReadBufferAndStartFillIfEmptiedButUsable(backgroundOperationSlot, buffer, offset, count, out bytesReadFromBuffer));
                        Contract.Assume(
                            bytesReadFromBuffer > 0,
                            "Usable stream implies that the completed fill obtained bytes. Zero bytes returned from ReadFile implies failure.");
                        break;

                    case StreamUsability.EndOfFileReached:
                        Contract.Assume(
                            internalBuffer.State == FileBuffer.BufferState.Empty,
                            "EndOfFileReached usability coincides with a totally-failed fill (nothing read)");
                        break;

                    case StreamUsability.Broken:
                        throw backgroundOperationSlot.ThrowExceptionForBrokenStream();

                    default:
                        throw Contract.AssertFailure("Unhandled StreamUsability");
                    }
                }

                // We've satisfied a read request for 'bytesReadFromBuffer' bytes, which advances our virtual file position.
                op.AdvancePosition(bytesReadFromBuffer);
                return(bytesReadFromBuffer);
            }
        }
Example #4
0
        /// <summary>
        /// <see cref="Stream.Flush"/>
        /// </summary>
        public override void Flush()
        {
            using (StreamOperationToken op = StartNewOperation(StreamOperation.Flush))
            {
                BackgroundOperationSlot slot = op.WaitForBackgroundOperationSlot();
                if (slot.Usability == StreamUsability.Broken)
                {
                    throw slot.ThrowExceptionForBrokenStream();
                }

                // TODO: Shouldn't drop the read buffer unless seeking to a new position.
                FlushOrDiscardBufferAndResetPositionAsync(op, slot, m_position).GetAwaiter().GetResult();
            }
        }
Example #5
0
        /// <summary>
        /// Attempts to complete the current operation. This is atomic to <see cref="StreamOperation.None"/>.
        /// </summary>
        private void CompleteOperation(StreamOperationToken token)
        {
            Analysis.IgnoreArgument(token);

            lock (m_operationLock)
            {
                if (m_currentOperation == StreamOperation.None)
                {
                    Contract.Assume(false, "Attempted to complete an operation, but no operation was in progress");
                }

                // Dispose is terminal.
                if (m_currentOperation != StreamOperation.Dispose)
                {
                    m_currentOperation = StreamOperation.None;
                }
            }
        }
Example #6
0
        /// <summary>
        /// Waits for the current background operation to complete, if any.
        /// </summary>
        protected Task <BackgroundOperationSlot> WaitForBackgroundOperationSlotAsync(StreamOperationToken token)
        {
            Analysis.IgnoreArgument(token);

            lock (m_backgroundOperationLock)
            {
                if (m_currentBackgroundOperation == StreamBackgroundOperation.None)
                {
                    return(m_completedBackgroundOperationTask);
                }

                if (m_currentBackgroundOperationCompletionSource == null)
                {
                    m_currentBackgroundOperationCompletionSource = new TaskCompletionSource <BackgroundOperationSlot>();
                }

                return(m_currentBackgroundOperationCompletionSource.Task);
            }
        }
Example #7
0
        /// <summary>
        /// <see cref="Stream.Seek(long, SeekOrigin)"/>
        /// </summary>
        public override long Seek(long offset, SeekOrigin origin)
        {
            using (StreamOperationToken token = StartNewOperation(StreamOperation.Seek))
            {
                BackgroundOperationSlot slot = token.WaitForBackgroundOperationSlot();
                if (slot.Usability == StreamUsability.Broken)
                {
                    throw slot.ThrowExceptionForBrokenStream();
                }

                long offsetFromStart;
                switch (origin)
                {
                case SeekOrigin.Begin:
                    Contract.Assume(offset >= 0, "Attempted to seek to a negative offset");
                    offsetFromStart = offset;
                    break;

                case SeekOrigin.Current:
                    Contract.Assume(m_position >= offset, "Attempted to seek (relative to current) to a negative offset");
                    offsetFromStart = m_position + offset;
                    break;

                case SeekOrigin.End:
                    throw new NotSupportedException("Seeking relative to stream end is not supported");

                default:
                    throw Contract.AssertFailure("Unknwon SeekOrigin");
                }

                if (m_position != offsetFromStart)
                {
                    FlushOrDiscardBufferAndResetPositionAsync(token, slot, offsetFromStart).GetAwaiter().GetResult();
                }

                return(offsetFromStart);
            }
        }
Example #8
0
        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");
                }
            }
        }
Example #9
0
 /// <summary>
 /// Waits for the current background operation to complete, if any.
 /// </summary>
 protected BackgroundOperationSlot WaitForBackgroundOperationSlot(StreamOperationToken token)
 {
     return(WaitForBackgroundOperationSlotAsync(token).GetAwaiter().GetResult());
 }