/// <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);
                }
            }
Esempio n. 2
0
            /// <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);
                }
            }
Esempio n. 3
0
            /// <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);
                    }
                }
            }
Esempio n. 6
0
            /// <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();
                        }
                    }
                }
            }
Esempio n. 7
0
            /// <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();
                    }
                }
            }