Files
UnrealEngine/Engine/Source/Programs/Horde/HordeServer.Shared/Utilities/AsyncTaskQueue.cs
2025-05-18 13:04:45 +08:00

75 lines
1.8 KiB
C#

// Copyright Epic Games, Inc. All Rights Reserved.
using Microsoft.Extensions.Logging;
namespace HordeServer.Utilities
{
/// <summary>
/// Allows spawning async tasks that will run sequentially, and allows waiting for them to complete
/// </summary>
public sealed class AsyncTaskQueue : IAsyncDisposable
{
readonly object _lockObject = new object();
Task _task;
readonly ILogger _logger;
CancellationTokenSource _cancellationSource;
/// <summary>
/// Constructor
/// </summary>
/// <param name="logger">Logger for any exceptions thrown by tasks</param>
public AsyncTaskQueue(ILogger logger)
{
_task = Task.CompletedTask;
_logger = logger;
_cancellationSource = new CancellationTokenSource();
}
/// <inheritdoc/>
public async ValueTask DisposeAsync()
{
if (_cancellationSource != null)
{
await _cancellationSource.CancelAsync();
await _task;
_cancellationSource.Dispose();
_cancellationSource = null!;
}
}
/// <summary>
/// Adds a new task to the queue
/// </summary>
/// <param name="func">Method to create the new task</param>
public void Enqueue(Func<CancellationToken, Task> func)
{
lock (_lockObject)
{
Task prevTask = _task;
_task = Task.Run(() => RunAsync(prevTask, func));
}
}
/// <summary>
/// Waits for any queued tasks to complete
/// </summary>
public Task FlushAsync(CancellationToken cancellationToken = default)
=> _task.WaitAsync(cancellationToken);
async Task RunAsync(Task prevTask, Func<CancellationToken, Task> func)
{
try
{
await prevTask;
}
catch (Exception ex)
{
_logger.LogError(ex, "Unhandled exception while running task in AsyncTaskQueue: {Message}", ex.Message);
}
await func(_cancellationSource.Token);
}
}
}