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