Example #1
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);
            }
        }