예제 #1
0
        /// <summary>
        /// Writes a new buffer into the pipeline. The task returned by this operation only completes when the next
        /// Read has been queued, or the Reader has completed, since the buffer provided here needs to be kept alive
        /// until the matching Read finishes (because we don't have ownership tracking when working with unowned buffers)
        /// </summary>
        /// <param name="buffer"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        // Called by the WRITER
        public async Task WriteAsync(OwnedBuffer <byte> buffer, CancellationToken cancellationToken)
        {
            // If Writing has stopped, why is the caller writing??
            if (Writing.Status != TaskStatus.WaitingForActivation)
            {
                throw new OperationCanceledException("Writing has ceased on this pipeline");
            }

            // If Reading has stopped, we cancel. We don't write unless there's a reader ready in this pipeline.
            if (Reading.Status != TaskStatus.WaitingForActivation)
            {
                throw new OperationCanceledException("Reading has ceased on this pipeline");
            }

            // Register for cancellation on this token for the duration of the write
            using (cancellationToken.Register(state => ((UnownedBufferReader)state).CancelWriter(), this))
            {
                // Wait for reading to start
                await ReadingStarted;

                // Cancel this task if this write is cancelled
                cancellationToken.ThrowIfCancellationRequested();

                // Allocate a new segment to hold the buffer being written.
                using (var segment = new BufferSegment(buffer))
                {
                    segment.End = buffer.Buffer.Length;

                    if (_head == null || _head.ReadableBytes == 0)
                    {
                        // Update the head to point to the head of the buffer.
                        _head = segment;
                    }
                    else if (_tail != null)
                    {
                        // Add this segment to the end of the chain
                        _tail.Next = segment;
                    }

                    // Always update tail to the buffer's tail
                    _tail = segment;

                    // Trigger the continuation
                    Complete();

                    // Wait for another read to come (or for the end of Reading, which will also trigger this gate to open) in before returning
                    await _readWaiting;

                    if (_head.ReadableBytes > 0)
                    {
                        // We need to preserve any buffers that haven't been consumed
                        _head = BufferSegment.Clone(new ReadCursor(_head), new ReadCursor(_tail, _tail?.End ?? 0), out _tail);
                    }
                }

                // Cancel this task if this write is cancelled
                cancellationToken.ThrowIfCancellationRequested();
            }
        }
예제 #2
0
        private ReadableBuffer Clone(ref ReadableBuffer buffer)
        {
            var segmentHead = BufferSegment.Clone(buffer.BufferStart, buffer.BufferEnd, out var segmentTail);

            return(new ReadableBuffer
            {
                BufferStart = new ReadCursor(segmentHead),
                BufferEnd = new ReadCursor(segmentTail, segmentTail.End),
                BufferLength = buffer.BufferLength
            });
        }
예제 #3
0
        private ReadableBuffer(ref ReadableBuffer buffer)
        {
            var begin = buffer.BufferStart;
            var end   = buffer.BufferEnd;

            BufferSegment segmentTail;
            var           segmentHead = BufferSegment.Clone(begin, end, out segmentTail);

            begin = new ReadCursor(segmentHead);
            end   = new ReadCursor(segmentTail, segmentTail.End);

            BufferStart = begin;
            BufferEnd   = end;

            BufferLength = buffer.BufferLength;
        }
예제 #4
0
        private ReadableBuffer(ref ReadableBuffer buffer)
        {
            var begin = buffer._start;
            var end   = buffer._end;

            BufferSegment segmentTail;
            var           segmentHead = BufferSegment.Clone(begin, end, out segmentTail);

            begin = new ReadCursor(segmentHead);
            end   = new ReadCursor(segmentTail, segmentTail.End);

            _start = begin;
            _end   = end;

            _length = buffer._length;

            begin.TryGetBuffer(end, out _first, out begin);
        }
예제 #5
0
        internal void Append(ReadableBuffer buffer)
        {
            if (buffer.IsEmpty)
            {
                return; // nothing to do
            }

            EnsureAlloc();

            BufferSegment clonedEnd;
            var           clonedBegin = BufferSegment.Clone(buffer.Start, buffer.End, out clonedEnd);

            if (_writingHead == null)
            {
                // No active write
                lock (_sync)
                {
                    if (_commitHead == null)
                    {
                        // No allocated buffers yet, not locking as _readHead will be null
                        _commitHead = clonedBegin;
                    }
                    else
                    {
                        Debug.Assert(_commitHead.Next == null);
                        // Allocated buffer, append as next segment
                        _commitHead.Next = clonedBegin;
                    }
                }
            }
            else
            {
                Debug.Assert(_writingHead.Next == null);
                // Active write, append as next segment
                _writingHead.Next = clonedBegin;
            }

            // Move write head to end of buffer
            _writingHead         = clonedEnd;
            _currentWriteLength += buffer.Length;
        }