// Copyright Epic Games, Inc. All Rights Reserved. using System; using System.IO; using System.Threading; using System.Threading.Tasks; namespace EpicGames.Horde.Compute { /// /// Conventional TCP-like interface for writing data to a socket. Sends are "push", receives are "pull". /// public sealed class ComputeChannel : IDisposable { /// /// Reader for the channel /// public ComputeBufferReader Reader { get; } /// /// Writer for the channel /// public ComputeBufferWriter Writer { get; } /// /// Constructor /// /// /// internal ComputeChannel(ComputeBufferReader recvBufferReader, ComputeBufferWriter sendBufferWriter) { Reader = recvBufferReader.AddRef(); Writer = sendBufferWriter.AddRef(); } /// public void Dispose() { Reader.Dispose(); Writer.Dispose(); } /// /// Sends data to a remote channel /// /// Memory to write /// Cancellation token for the operation public ValueTask SendAsync(ReadOnlyMemory memory, CancellationToken cancellationToken = default) => Writer.WriteAsync(memory, cancellationToken); /// /// Marks a channel as complete /// /// Buffer to receive the data /// Cancellation token for the operation public ValueTask RecvAsync(Memory buffer, CancellationToken cancellationToken = default) => Reader.ReadAsync(buffer, cancellationToken); /// /// Reads a complete message from the given socket, retrying reads until the buffer is full. /// /// Buffer to store the data /// Cancellation token for the operation public async ValueTask RecvMessageAsync(Memory buffer, CancellationToken cancellationToken = default) { if (!await TryRecvMessageAsync(buffer, cancellationToken)) { throw new EndOfStreamException(); } } /// /// Reads either a full message or end of stream from the channel /// /// Buffer to store the data /// Cancellation token for the operation public async ValueTask TryRecvMessageAsync(Memory buffer, CancellationToken cancellationToken = default) { for (int offset = 0; offset < buffer.Length;) { int read = await RecvAsync(buffer.Slice(offset), cancellationToken); if (read == 0) { return false; } offset += read; } return true; } /// /// Mark the channel as complete (ie. that no more data will be sent) /// public void MarkComplete() => Writer.MarkComplete(); } }