// 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();
}
}