/// <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 void CompleteRead(int processedCount, ref ChannelReaderBufferSlice <T> slice) { if ((processedCount < 0) || (processedCount > slice.Length)) { throw new ArgumentOutOfRangeException( nameof(processedCount), "processedCount should be non-negative and less than allocated count"); } using (var guard = new StateReadGuard <TSyncObject>(_syncObject, _owner)) { if (!_owner._idToSliceEntries.TryGetValue(slice.Id, out var sliceEntryNode)) { throw new ArgumentException("Wrong slice variable."); } if (!_readInProgress) { throw new InvalidOperationException("CompleteWrite operation has not been started."); } var sliceEntry = sliceEntryNode.Value; if (slice.Length != sliceEntry.Length) { throw new ArgumentException("The provided slice have an invalid state", nameof(slice)); } guard.NotifyRead(processedCount, sliceEntry.Length); int nonProcessedCount = sliceEntry.Length - processedCount; if (processedCount == 0) { sliceEntry.Status = SliceEntryStatus.Data; } else if (nonProcessedCount == 0) { _owner._sliceEntries.Remove(sliceEntryNode); } else { sliceEntry.Length = nonProcessedCount; sliceEntry.Start += processedCount; sliceEntry.Status = SliceEntryStatus.Data; } _readInProgress = false; } }
/// <inheritdoc/> public void PartialFree(int newCount, ref ChannelReaderBufferSlice <T> slice) { if ((newCount < 1) || (newCount > slice.Length)) { throw new ArgumentOutOfRangeException( nameof(newCount), "New count should be greater than zero and less or equal to already allocated length."); } using (var guard = new StateReadGuard <TSyncObject>()) { if (!_owner._idToSliceEntries.TryGetValue(slice.Id, out var sliceEntryNode)) { throw new ArgumentException("Wrong slice variable."); } if (!_readInProgress) { throw new InvalidOperationException("PartialFree operation has not been started."); } if (slice.Length != sliceEntryNode.Value.Length) { throw new ArgumentException("The provided slice have an invalid state", nameof(slice)); } int decreaseSize = sliceEntryNode.Value.Length - newCount; if (decreaseSize == 0) { // Shit it - shit out return; } slice.DecreaseLength(decreaseSize); var sliceEntry = sliceEntryNode.Value; sliceEntry.Length = newCount; // Splitting released part to the new entry. var newEntry = _owner.AllocateEmptyEntryAfter(sliceEntryNode); newEntry.Buffer = sliceEntry.Buffer; newEntry.Start = sliceEntry.Start + sliceEntry.Length; newEntry.Length = decreaseSize; newEntry.Status = SliceEntryStatus.Data; guard.NotifyRead(0, decreaseSize); } }
/// <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); } } }