/// <inheritdoc/> public async ValueTask <ChannelReaderBufferSlice <T> > StartReadAsync( int count, CancellationToken cancellationToken = default) { if (count <= 0) { throw new ArgumentOutOfRangeException(nameof(count), "Count should be non-zero positive."); } bool isReadStarted = false; while (true) { Task <VoidResult> taskToAwait; using (var guard = new StateReadGuard <TSyncObject>()) { if (!isReadStarted) { EnsureSequentialUsage(); _readInProgress = true; isReadStarted = true; } int nonAllocatedLength = guard.GetAvailableDataForReading(); if (nonAllocatedLength == 0) { try { _owner.VerifyChannelNotClosed(); } catch { _readInProgress = false; throw; } Debug.Assert(_owner._dataAvailableCs != null, "_owner._dataAvailableCs != null"); taskToAwait = _owner._dataAvailableCs.Task; } else { LinkedListNode <SliceEntry> curNode = _owner._sliceEntries.Last; // Searching for node to read from while (curNode != null) { var sliceEntry = curNode.Value; if (sliceEntry.Status == SliceEntryStatus.Data) { Debug.Assert(sliceEntry.Length != 0, "sliceEntry.Length != 0"); int allocateForReadLength = Math.Min(count, sliceEntry.Length); guard.NotifyAllocatedForReading(allocateForReadLength); if (allocateForReadLength == sliceEntry.Length) { sliceEntry.Status = SliceEntryStatus.AllocatedForRead; return(new ChannelReaderBufferSlice <T>( sliceEntry.Buffer, sliceEntry.Start, sliceEntry.Length, sliceEntry.Id)); } int freeSpaceLeft = sliceEntry.Length - allocateForReadLength; sliceEntry.Length -= freeSpaceLeft; sliceEntry.Status = SliceEntryStatus.AllocatedForRead; var newEntry = _owner.AllocateEmptyEntryAfter(curNode); newEntry.Buffer = sliceEntry.Buffer; newEntry.Start = sliceEntry.Start + allocateForReadLength; newEntry.Length = freeSpaceLeft; newEntry.Status = SliceEntryStatus.Data; return(new ChannelReaderBufferSlice <T>( sliceEntry.Buffer, sliceEntry.Start, sliceEntry.Length, sliceEntry.Id)); } curNode = curNode.Next; } Debug.Assert(false, "Available data counter does not corresponds to slice entries."); throw new InvalidOperationException("Impossible state!"); } } await taskToAwait.WithCancellation(cancellationToken); } }
/// <inheritdoc/> public ChannelReaderBufferSlice <T> TryStartRead(int count) { if (count <= 0) { throw new ArgumentOutOfRangeException(nameof(count), "Count should be non-zero positive."); } using (var guard = new StateReadGuard <TSyncObject>()) { EnsureSequentialUsage(); int nonAllocatedLength = guard.GetAvailableDataForReading(); if (nonAllocatedLength == 0) { return(new ChannelReaderBufferSlice <T>(EmptyBuffer, 0, 0, 0)); } LinkedListNode <SliceEntry> curNode = _owner._sliceEntries.Last; // Searching for node to read from while (curNode != null) { var sliceEntry = curNode.Value; if (sliceEntry.Status == SliceEntryStatus.Data) { Debug.Assert(sliceEntry.Length != 0, "sliceEntry.Length != 0"); int allocateForReadLength = Math.Min(count, sliceEntry.Length); guard.NotifyAllocatedForReading(allocateForReadLength); if (allocateForReadLength == sliceEntry.Length) { sliceEntry.Status = SliceEntryStatus.AllocatedForRead; return(new ChannelReaderBufferSlice <T>( sliceEntry.Buffer, sliceEntry.Start, sliceEntry.Length, sliceEntry.Id)); } int freeSpaceLeft = sliceEntry.Length - allocateForReadLength; sliceEntry.Length -= freeSpaceLeft; sliceEntry.Status = SliceEntryStatus.AllocatedForRead; var newEntry = _owner.AllocateEmptyEntryAfter(curNode); newEntry.Buffer = sliceEntry.Buffer; newEntry.Start = sliceEntry.Start + allocateForReadLength; newEntry.Length = freeSpaceLeft; newEntry.Status = SliceEntryStatus.Data; return(new ChannelReaderBufferSlice <T>( sliceEntry.Buffer, sliceEntry.Start, sliceEntry.Length, sliceEntry.Id)); } curNode = curNode.Next; } Debug.Assert(false, "Available data counter does not corresponds to slice entries."); throw new InvalidOperationException("Impossible state!"); } }