/// <summary> /// Writes a new buffer into the channel. 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(OwnedMemory <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 Channel"); } // If Reading has stopped, we cancel. We don't write unless there's a reader ready in this channel. if (Reading.Status != TaskStatus.WaitingForActivation) { throw new OperationCanceledException("Reading has ceased on this Channel"); } // Register for cancellation on this token for the duration of the write using (cancellationToken.Register(state => ((UnownedBufferChannel)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.Memory.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(); } }
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); }
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; _isOwner = true; _first = buffer._first; _length = buffer._length; }
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 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; }