// Copyright Epic Games, Inc. All Rights Reserved.
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using EpicGames.Core;
using EpicGames.Horde.Storage;
namespace EpicGames.Horde.Compute
{
///
/// Storage backend which can read bundles over a compute channel
///
public sealed class AgentStorageBackend : IStorageBackend, IDisposable
{
readonly AgentMessageChannel _channel;
readonly SemaphoreSlim _semaphore;
///
public bool SupportsRedirects => false;
///
/// Constructor
///
///
public AgentStorageBackend(AgentMessageChannel channel)
{
_channel = channel;
_semaphore = new SemaphoreSlim(1);
}
///
public void Dispose()
{
_semaphore.Dispose();
}
#region Blobs
///
public async Task OpenBlobAsync(BlobLocator locator, int offset, int? length, CancellationToken cancellationToken = default)
{
ReadOnlyMemory data = await ReadBlobInternalAsync(locator, offset, length, cancellationToken);
return new ReadOnlyMemoryStream(data);
}
///
public async Task> ReadBlobAsync(BlobLocator locator, int offset, int? length, CancellationToken cancellationToken = default)
{
ReadOnlyMemory data = await ReadBlobInternalAsync(locator, offset, length, cancellationToken);
return ReadOnlyMemoryOwner.Create(data);
}
async ValueTask> ReadBlobInternalAsync(BlobLocator locator, int offset, int? length, CancellationToken cancellationToken = default)
{
await _semaphore.WaitAsync(cancellationToken);
try
{
// TODO: Want to pass 0 for length to read here (meaning entire blob), but older streams misinterpret this as a 0 byte read.
return await _channel.ReadBlobAsync(locator.ToString(), offset, length ?? 128 * 1024 * 1024, cancellationToken);
}
finally
{
_semaphore.Release();
}
}
///
public Task WriteBlobAsync(BlobLocator locator, Stream stream, IReadOnlyCollection? imports, CancellationToken cancellationToken = default)
=> throw new NotSupportedException();
///
public Task WriteBlobAsync(Stream stream, IReadOnlyCollection? imports, string? basePath = null, CancellationToken cancellationToken = default)
=> throw new NotSupportedException();
///
public ValueTask TryGetBlobReadRedirectAsync(BlobLocator locator, CancellationToken cancellationToken = default)
=> default;
///
public ValueTask TryGetBlobWriteRedirectAsync(BlobLocator locator, IReadOnlyCollection? imports = null, CancellationToken cancellationToken = default)
=> default;
///
public ValueTask<(BlobLocator, Uri)?> TryGetBlobWriteRedirectAsync(IReadOnlyCollection? imports = null, string? prefix = null, CancellationToken cancellationToken = default)
=> default;
#endregion
#region Aliases
///
public Task AddAliasAsync(string name, BlobLocator locator, int rank, ReadOnlyMemory data, CancellationToken cancellationToken = default) => throw new NotSupportedException();
///
public Task RemoveAliasAsync(string name, BlobLocator locator, CancellationToken cancellationToken = default) => throw new NotSupportedException();
///
public Task FindAliasesAsync(string name, int? maxResults, CancellationToken cancellationToken = default) => throw new NotSupportedException();
#endregion
#region Refs
///
public Task DeleteRefAsync(RefName name, CancellationToken cancellationToken = default) => throw new NotSupportedException();
///
public Task TryReadRefAsync(RefName name, RefCacheTime cacheTime = default, CancellationToken cancellationToken = default) => throw new NotSupportedException();
///
public Task WriteRefAsync(RefName name, HashedBlobRefValue value, RefOptions? options = null, CancellationToken cancellationToken = default) => throw new NotSupportedException();
#endregion
///
public async Task UpdateMetadataAsync(UpdateMetadataRequest request, CancellationToken cancellationToken = default)
{
foreach (AddAliasRequest addAlias in request.AddAliases)
{
await AddAliasAsync(addAlias.Name, addAlias.Target, addAlias.Rank, addAlias.Data, cancellationToken);
}
foreach (RemoveAliasRequest removeAlias in request.RemoveAliases)
{
await RemoveAliasAsync(removeAlias.Name, removeAlias.Target, cancellationToken);
}
foreach (AddRefRequest addRef in request.AddRefs)
{
await WriteRefAsync(addRef.RefName, new HashedBlobRefValue(addRef.Hash, addRef.Target), addRef.Options, cancellationToken);
}
foreach (RemoveRefRequest removeRef in request.RemoveRefs)
{
await DeleteRefAsync(removeRef.RefName, cancellationToken);
}
}
///
public void GetStats(StorageStats stats)
{
}
}
}