コード例 #1
0
ファイル: AsyncFileStream.cs プロジェクト: kittinap/kunnjae
        /// <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
        /// <summary>
        /// Starts a read from the buffer. Returns a bool indicating if a fill started as a result. A fill is not started unless the stream is usable.
        /// </summary>
        private bool ReadBufferAndStartFillIfEmptiedButUsable(BackgroundOperationSlot backgroundOperationSlot, byte[] buffer, int offset, int count, out int bytesReadFromBuffer)
        {
            FileBuffer.BufferOperationStatus status = internalBuffer.Read(buffer, offset, count, out bytesReadFromBuffer);
            switch (status)
            {
            case FileBuffer.BufferOperationStatus.ReadExhausted:
                // We start a background fill if the stream is still usable, but we don't need to explicitly handle
                // EOF or broken-ness here:
                // - If the caller gets bytes from the buffer, let them get used (i.e., read buffer bytes before complaining about EOF).
                // - If the caller needs bytes right away but we don't have any, the caller should wait on the fill (if true is returned)
                //   and check stream usability.
                // The key point is that a dedicated caller will eventually hit the second case. We defer EOF / failure until the caller
                // catches up to the stream position where it happens; if they do not ever read up to that point, pretend it never happened.
                if (backgroundOperationSlot.Usability == StreamUsability.Usable)
                {
                    backgroundOperationSlot.StartBackgroundOperation(StreamBackgroundOperation.Fill);
                    return(true);
                }
                else
                {
                    return(false);
                }

            case FileBuffer.BufferOperationStatus.CapacityRemaining:
                Contract.Assume(bytesReadFromBuffer > 0);
                return(false);

            case FileBuffer.BufferOperationStatus.FlushRequired:
                Contract.Assume(false, "Buffer is used only for reads, and so never needs to be write-flushed.");
                throw new InvalidOperationException("Unreachable");

            default:
                throw Contract.AssertFailure("Unhandled BufferOperationStatus");
            }
        }
コード例 #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);
            }
        }
コード例 #4
0
ファイル: AsyncFileStream.cs プロジェクト: kittinap/kunnjae
        /// <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();
            }
        }
コード例 #5
0
ファイル: AsyncFileStream.cs プロジェクト: kittinap/kunnjae
        /// <summary>
        /// Starts a background operation. This causes the <see cref="internalBuffer"/> to fill or flush.
        /// It must be ensured that no other background operation will be in progress, and that the stream is still usable after it.
        /// - First start an operation with <see cref="StartNewOperation"/>; this reserves the right to start the next background operation.
        /// - Then, wait for the current background operation, if any. We've established another one will not follow it due to the prior point.
        /// - Then, respond to the current stream usability (EOF, broken, etc.) as updated by the prior operation.
        /// </summary>
        private unsafe void StartBackgroundOperation(BackgroundOperationSlot slot, StreamBackgroundOperation nextOperation)
        {
            Contract.Requires(nextOperation != StreamBackgroundOperation.None);
            Analysis.IgnoreArgument(slot);

            lock (m_backgroundOperationLock)
            {
                if (m_usability != StreamUsability.Usable)
                {
                    Contract.Assume(false, "Attempting to start a background operation on an unusable stream: " + m_usability.ToString("G"));
                }

                Contract.Assume(
                    m_currentBackgroundOperation == StreamBackgroundOperation.None,
                    "Background operation already in progress; wait on it first?");
                Contract.Assume(m_currentBackgroundOperationCompletionSource == null);
                m_currentBackgroundOperation = nextOperation;
            }

            // Now actually start the async operation.
            // Note that the callback to 'this' (IIOCompletionTarget) can happen on this same stack
            byte *pinnedBuffer;
            int   operationLength;

            switch (nextOperation)
            {
            case StreamBackgroundOperation.Fill:
                internalBuffer.LockForFill(out pinnedBuffer, out operationLength);
                m_file.ReadOverlapped(this, pinnedBuffer, operationLength, m_bufferPosition);
                break;

            case StreamBackgroundOperation.Flush:
                internalBuffer.LockForFlush(out pinnedBuffer, out operationLength);
                m_file.WriteOverlapped(this, pinnedBuffer, operationLength, m_bufferPosition);
                break;

            default:
                throw Contract.AssertFailure("Unhandled StreamBackgroundOperation");
            }
        }
コード例 #6
0
ファイル: AsyncFileStream.cs プロジェクト: kittinap/kunnjae
        /// <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);
            }
        }
コード例 #7
0
ファイル: AsyncFileStream.cs プロジェクト: kittinap/kunnjae
        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");
                }
            }
        }
コード例 #8
0
ファイル: AsyncFileStream.cs プロジェクト: kittinap/kunnjae
 /// <summary>
 /// Inheritor-provided implementation for a 'flush or discard' operation.
 /// A read-only stream discards while a write-only stream flushes.
 /// </summary>
 protected abstract Task FlushOrDiscardBufferAsync(BackgroundOperationSlot slot);
コード例 #9
0
 /// <inheritdoc />
 protected override Task FlushOrDiscardBufferAsync(BackgroundOperationSlot slot)
 {
     Analysis.IgnoreArgument(slot);
     internalBuffer.Discard();
     return(Unit.VoidTask);
 }