/// <summary> /// Receive multiple messages from a pipe /// </summary> public static async IAsyncEnumerable <T> ReceiveAsync <T>(PipeReader source, MessagePipeOptions options = default) { if (source == null) { throw new ArgumentNullException(nameof(source)); } options = options.Normalize(); while (true) { // obtain the next buffer options.OnLog("ReceiveAsync obtaining buffer..."); if (!source.TryRead(out var result)) { result = await source.ReadAsync(options.CancellationToken).ConfigureAwait(false); } if (result.IsCanceled) { ThrowCancelled(); } // consume the buffer var buffer = result.Buffer; options.OnLog($"ReceiveAsync got {buffer.Length} bytes"); int processed = 0; while (options.TryRead <T>(ref buffer, out var item)) { processed++; yield return(item); } if (processed == 0 && result.IsCompleted) // check for termination { // we have everything, and there will never be any more; was it clean? if (!buffer.IsEmpty) { ThrowInputCompletedWithPartialPayload(); } break; } // mark what we consumed (the "ref buffer" means we've sliced it already) source.AdvanceTo(buffer.Start, buffer.End); } if (options.MarkComplete) { await source.CompleteAsync().ConfigureAwait(false); } options.OnLog("ReceiveAsync completed successfully"); }
/// <summary> /// Send multiple messages over a pipe /// </summary> public static async ValueTask SendAsync <T>(PipeWriter destination, IAsyncEnumerable <T> source, MessagePipeOptions options = default) { if (source == null) { throw new ArgumentNullException(nameof(source)); } options = options.Normalize(); await foreach (var message in source.WithCancellation(options.CancellationToken).ConfigureAwait(false)) { options.OnLog("SendAsync writing..."); options.Write(destination, message); options.OnLog("SendAsync flushing..."); CheckFlushResult(await destination.FlushAsync(options.CancellationToken).ConfigureAwait(false)); options.OnLog("SendAsync flushed"); } if (options.MarkComplete) { await destination.CompleteAsync().ConfigureAwait(false); } }