// Copyright Epic Games, Inc. All Rights Reserved. using System; using System.Threading; using System.Threading.Tasks; namespace EpicGames.Core { /// /// Runs a task in the background and allows stopping it on demand /// public sealed class BackgroundTask : IAsyncDisposable { readonly Func _runTask; Task? _task; readonly CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource(); /// /// Accessor for the underlying task /// public Task? Task => _task; /// /// Constructor. Note that the task does not start until is called. /// /// public BackgroundTask(Func runTask) { _runTask = runTask; } /// public async ValueTask DisposeAsync() { await StopAsync().ConfigureAwait(false); _cancellationTokenSource.Dispose(); } /// /// Creates and starts a new background task instance /// /// /// public static BackgroundTask StartNew(Func runTask) { BackgroundTask task = new BackgroundTask(runTask); task.Start(); return task; } /// /// Creates and starts a new background task instance /// /// /// New background task instance public static BackgroundTask StartNew(Func> runTask) { BackgroundTask task = new BackgroundTask(runTask); task.Start(); return task; } /// /// Starts the task /// public void Start() { if (_task != null) { throw new InvalidOperationException("Background task is already running"); } _task = Task.Run(() => _runTask(_cancellationTokenSource.Token), _cancellationTokenSource.Token); } /// /// Signals the cancellation token and waits for the task to finish /// /// Cancellation token for the operation public async Task StopAsync(CancellationToken cancellationToken = default) { if (_task != null && !_task.IsCompleted) { try { await _cancellationTokenSource.CancelAsync(); await _task.WaitAsync(cancellationToken).ConfigureAwait(false); } catch (OperationCanceledException) { } } } /// /// Waits for the task to complete /// /// Cancellation token for the operation public async Task WaitAsync(CancellationToken cancellationToken) { if (_task == null) { throw new InvalidOperationException("Task has not been started."); } await _task.WaitAsync(cancellationToken); } } /// /// Runs a task in the background and allows stopping it on demand /// public sealed class BackgroundTask : IAsyncDisposable { readonly Func> _runTask; Task? _task; readonly CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource(); /// /// Accessor for the underlying task /// public Task? Task => _task; /// /// Constructor. Note that the task does not start until is called. /// /// public BackgroundTask(Func> runTask) { _runTask = runTask; } /// public async ValueTask DisposeAsync() { await StopAsync(); _cancellationTokenSource.Dispose(); } /// /// Starts the task /// public void Start() { if (_task != null) { throw new InvalidOperationException("Background task is already running"); } _task = System.Threading.Tasks.Task.Run(() => _runTask(_cancellationTokenSource.Token), _cancellationTokenSource.Token); } /// /// Signals the cancellation token and waits for the task to finish /// /// Cancellation token for the operation public async Task StopAsync(CancellationToken cancellationToken = default) { if (_task != null && !_task.IsCompleted) { try { await _cancellationTokenSource.CancelAsync(); await _task.WaitAsync(cancellationToken); } catch (OperationCanceledException) { } } } /// /// Waits for the task to complete /// public Task WaitAsync(CancellationToken cancellationToken = default) { if (_task == null) { throw new InvalidOperationException("Task has not been started."); } return _task.WaitAsync(cancellationToken); } } }