예제 #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();
        }
예제 #2
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);
            }
        }
예제 #3
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();
            }
        }
예제 #4
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);
            }
        }