/// <inheritdoc/> public async ValueTask <bool> WaitToReadAsync(CancellationToken cancellationToken = default) { Task <VoidResult> taskToAwait = null; using (var seqGuard = new SequentialUsageGuard(this)) { using (var guard = new StateReadGuard <TSyncObject>(_syncObject, _owner)) { seqGuard.OnStateGuardEntered(); if (guard.GetAvailableDataForReading() == 0) { taskToAwait = _owner._dataAvailableCs?.Task; } } if (taskToAwait != null) { await taskToAwait; } // it's safe to use shadow. return(!_owner._isCompleted); } }
/// <inheritdoc/> public bool TryRead(int count, out ArraySegment <T> items) { if (count <= 0) { throw new ArgumentOutOfRangeException(nameof(count), "Count should be non-zero positive."); } using (var guard = new StateReadGuard <TSyncObject>(_syncObject, _owner)) { EnsureSequentialUsage(); int dataAvailable = guard.GetAvailableDataForReading(); if (dataAvailable == 0) { items = default; return(false); } LinkedListNode <SliceEntry> curNode = _owner._sliceEntries.Last; while (curNode != null) { var sliceEntry = curNode.Value; if (sliceEntry.Status == SliceEntryStatus.Data) { Debug.Assert(sliceEntry.Length != 0, "sliceEntry.Length != 0"); int amountToRead = Math.Min(sliceEntry.Length, count); guard.NotifyRead(amountToRead); items = new ArraySegment <T>(sliceEntry.Buffer, sliceEntry.Start, amountToRead); if (amountToRead == sliceEntry.Length) { _owner._sliceEntries.Remove(curNode); } else { sliceEntry.Start += amountToRead; sliceEntry.Length -= amountToRead; } return(true); } curNode = curNode.Next; } Debug.Assert(false, "Counters are not corresponding to the slice entries list."); throw new InvalidOperationException("Impossible state!"); } }
/// <inheritdoc/> public bool TryRead(out T item) { using (var guard = new StateReadGuard <TSyncObject>()) { EnsureSequentialUsage(); if (guard.GetAvailableDataForReading() == 0) { item = default; return(false); } 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"); guard.NotifyRead(1); item = sliceEntry.Buffer[sliceEntry.Start++]; // Removing empty nodes. if (--sliceEntry.Length == 0) { _owner._sliceEntries.Remove(curNode); } return(true); } curNode = curNode.Next; } Debug.Assert(false, "Available data counter does not corresponds to slice entries."); item = default; return(false); } }
/// <inheritdoc/> public async ValueTask <ArraySegment <T> > ReadAsync(int count, CancellationToken cancellationToken) { if (count <= 0) { throw new ArgumentOutOfRangeException(nameof(count), "Count should be non-zero positive."); } using (var seqGuard = new SequentialUsageGuard(this)) { while (true) { Task <VoidResult> taskToAwait; using (var guard = new StateReadGuard <TSyncObject>(_syncObject, _owner)) { int dataAvailable = guard.GetAvailableDataForReading(); if (dataAvailable == 0) { _owner.VerifyChannelNotClosed(); seqGuard.OnStateGuardEntered(); Debug.Assert(_owner._dataAvailableCs != null, "_owner._dataAvailableCs != null"); taskToAwait = _owner._dataAvailableCs.Task; } else { seqGuard.OnStateGuardEntered(); LinkedListNode <SliceEntry> curNode = _owner._sliceEntries.Last; while (curNode != null) { var sliceEntry = curNode.Value; if (sliceEntry.Status == SliceEntryStatus.Data) { Debug.Assert(sliceEntry.Length != 0, "sliceEntry.Length != 0"); int amountToRead = Math.Min(sliceEntry.Length, count); guard.NotifyRead(amountToRead); var items = new ArraySegment <T>( sliceEntry.Buffer, sliceEntry.Start, amountToRead); if (amountToRead == sliceEntry.Length) { _owner._sliceEntries.Remove(curNode); } else { sliceEntry.Start += amountToRead; sliceEntry.Length -= amountToRead; } return(items); } curNode = curNode.Next; } Debug.Assert(false, "Counters are not corresponding to the slice entries list."); throw new InvalidOperationException("Impossible state!"); } } await taskToAwait.WithCancellation(cancellationToken); } } }
/// <inheritdoc/> public int TryRead(Span <T> outBuffer) { using (var guard = new StateReadGuard <TSyncObject>()) { EnsureSequentialUsage(); if (outBuffer.Length == 0) { return(0); } int availableDataCount = guard.GetAvailableDataForReading(); if (availableDataCount == 0) { return(0); } int amountToRead = Math.Min(availableDataCount, outBuffer.Length); guard.NotifyRead(amountToRead); int readCount = amountToRead; int outPosition = 0; // Searching for node to read from LinkedListNode <SliceEntry> curNode = _owner._sliceEntries.Last; while ((curNode != null) && (amountToRead != 0)) { var sliceEntry = curNode.Value; if (sliceEntry.Status == SliceEntryStatus.Data) { Debug.Assert(sliceEntry.Length != 0, "sliceEntry.Length != 0"); int readChunkLength = Math.Min(amountToRead, curNode.Value.Length); for (int i = 0; i < readChunkLength; i++) { outBuffer[outPosition++] = sliceEntry.Buffer[i + sliceEntry.Start]; } amountToRead -= readChunkLength; if (readChunkLength == curNode.Value.Length) { var nodeToRemove = curNode; curNode = curNode.Next; _owner._sliceEntries.Remove(nodeToRemove); continue; } curNode.Value.Start += readChunkLength; curNode.Value.Length -= readChunkLength; } curNode = curNode.Next; } return(readCount); } }
/// <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!"); } }
/// <inheritdoc/> public async ValueTask <T> ReadAsync(CancellationToken cancellationToken = default) { using (var seqGuard = new SequentialUsageGuard(this)) { while (true) { Task <VoidResult> taskToAwait; using (var guard = new StateReadGuard <TSyncObject>()) { if (guard.GetAvailableDataForReading() == 0) { _owner.VerifyChannelNotClosed(); seqGuard.OnStateGuardEntered(); Debug.Assert(_owner._dataAvailableCs != null, "_owner._dataAvailableCs != null"); taskToAwait = _owner._dataAvailableCs.Task; } else { seqGuard.OnStateGuardEntered(); 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"); guard.NotifyRead(1); var item = sliceEntry.Buffer[sliceEntry.Start++]; // Removing empty nodes. if (--sliceEntry.Length == 0) { _owner._sliceEntries.Remove(curNode); } return(item); } curNode = curNode.Next; } Debug.Assert(false, "Available data counter does not corresponds to slice entries."); throw new InvalidOperationException("Impossible state!"); } } await taskToAwait.WithCancellation(cancellationToken); } } }