/// <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 async ValueTask <bool> WaitToWriteAsync(CancellationToken cancellationToken = default) { Task <VoidResult> taskToAwait = null; using (var seqGuard = new SequentialUsageGuard(this)) { using (var guard = new StateWriteGuard <TSyncObject>(_syncObject, _owner)) { seqGuard.OnStateGuardEntered(); if (guard.GetFreeSpaceForWrite() == 0) { taskToAwait = _owner._writeSpaceAvailableCs?.Task; } } if (taskToAwait != null) { await taskToAwait.WithCancellation(cancellationToken); } // it's safe to use shadow. return(!_owner._isCompleted); } }
/// <inheritdoc/> public async ValueTask WriteAsync(T item, CancellationToken cancellationToken = default) { using (var seqGuard = new SequentialUsageGuard(this)) { while (true) { cancellationToken.ThrowIfCancellationRequested(); Task <VoidResult> taskToAwait; using (var guard = new StateWriteGuard <TSyncObject>(_syncObject, _owner)) { if (_owner._isCompleted) { throw new ChannelClosedException(); } seqGuard.OnStateGuardEntered(); if (guard.GetFreeSpaceForWrite() > 0) { guard.NotifyWritten(1); var sliceEntry = _owner._sliceEntries.Last?.Value; if ((sliceEntry == null) || (sliceEntry.Status != SliceEntryStatus.Data) || !sliceEntry.TryWriteLast(item)) { _owner.AllocateNewEntry().TryWriteLast(item); } return; } Debug.Assert(_owner._writeSpaceAvailableCs != null); taskToAwait = _owner._writeSpaceAvailableCs.Task; } await taskToAwait.WithCancellation(cancellationToken); // TODO: put this to avoid possible recursion. // await Task.Yield(); } } }
/// <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 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); } } }
/// <inheritdoc/> public async ValueTask <int> WriteAsync <TItems>(TItems items, CancellationToken cancellationToken = default) where TItems : IEnumerable <T> { using (var enumerator = items.GetEnumerator()) { if (!enumerator.MoveNext()) { // No items return(0); } using (var seqGuard = new SequentialUsageGuard(this)) { while (true) { cancellationToken.ThrowIfCancellationRequested(); Task <VoidResult> taskToAwait; using (var guard = new StateWriteGuard <TSyncObject>(_syncObject, _owner)) { if (_owner._isCompleted) { throw new ChannelClosedException(); } seqGuard.OnStateGuardEntered(); if (guard.GetFreeSpaceForWrite() > 0) { int writtenCount = 0; while (_owner._allocatedCount < _owner._maxBufferLength) { // ReSharper disable once AccessToDisposedClosure T item = enumerator.Current; guard.NotifyWritten(1); writtenCount++; var sliceEntry = _owner._sliceEntries.Last?.Value; if ((sliceEntry == null) || (sliceEntry.Status != SliceEntryStatus.Data) || !sliceEntry.TryWriteLast(item)) { _owner.AllocateNewEntry().TryWriteLast(item); } // ReSharper disable once AccessToDisposedClosure if (!enumerator.MoveNext()) { return(writtenCount); } } } Debug.Assert(_owner._writeSpaceAvailableCs != null); taskToAwait = _owner._writeSpaceAvailableCs.Task; } await taskToAwait.WithCancellation(cancellationToken); // TODO: put this to avoid possible recursion. // await Task.Yield(); } } } }
/// <inheritdoc/> public async ValueTask <int> WriteAsync(ArraySegment <T> items, CancellationToken cancellationToken = default) { if (items.Count == 0) { return(0); } using (var seqGuard = new SequentialUsageGuard(this)) { while (true) { cancellationToken.ThrowIfCancellationRequested(); Task <VoidResult> taskToAwait; using (var guard = new StateWriteGuard <TSyncObject>(_syncObject, _owner)) { if (_owner._isCompleted) { throw new ChannelClosedException(); } seqGuard.OnStateGuardEntered(); int spaceLeft = guard.GetFreeSpaceForWrite(); if (spaceLeft > 0) { int amountToWrite = Math.Min(spaceLeft, items.Count); guard.NotifyWritten(amountToWrite); int writtenCount = amountToWrite; int sourcePosition = items.Offset; // Perform copying. while (amountToWrite > 0) { void DoWrite() { var target = _owner.TryAllocateUnsafe(amountToWrite); amountToWrite -= target.Length; for (int i = 0; i < target.Length; i++) { target[i] = items.Array[sourcePosition++]; } } DoWrite(); } return(writtenCount); } Debug.Assert(_owner._writeSpaceAvailableCs != null); taskToAwait = _owner._writeSpaceAvailableCs.Task; } await taskToAwait.WithCancellation(cancellationToken); // TODO: put this to avoid possible recursion. // await Task.Yield(); } } }