This PR has a large blast radius because it takes the final step and bumps up the Controller to be fully async. We have historically done a piece-by-piece update, so all operations were already async but the controller interface was kept synchronous. With this update, the controller is now fully async and all tests are updated. Most places where the new C# compiler warns about function names not ending in `Async` were also adressed, giving a massive refactor change. Functionally, no changes are done.
145 lines
5.2 KiB
C#
145 lines
5.2 KiB
C#
using System;
|
|
using System.IO;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using Duplicati.Library.Interface;
|
|
using Duplicati.Library.Main.Operation.Common;
|
|
using Duplicati.StreamUtil;
|
|
|
|
namespace Duplicati.Library.Main.Backend;
|
|
|
|
#nullable enable
|
|
|
|
partial class BackendManager
|
|
{
|
|
/// <summary>
|
|
/// Execution context for the backend manager
|
|
/// </summary>
|
|
/// <param name="ProgressHandler">The progress handler</param>
|
|
/// <param name="Statwriter">The stat writer</param>
|
|
/// <param name="Database">The database collector</param>
|
|
/// <param name="UploadThrottleManager">The upload throttle manager</param>
|
|
/// <param name="DownloadThrottleManager">The download throttle manager</param>
|
|
/// <param name="TaskReader">The task reader</param>
|
|
/// <param name="IsThrottleDisabled">Whether throttling is disabled</param>
|
|
/// <param name="Options">The options</param>
|
|
private sealed record ExecuteContext(
|
|
ProgressHandler ProgressHandler,
|
|
IBackendWriter Statwriter,
|
|
DatabaseCollector Database,
|
|
ThrottleManager UploadThrottleManager,
|
|
ThrottleManager DownloadThrottleManager,
|
|
ITaskReader TaskReader,
|
|
bool IsThrottleDisabled,
|
|
Options Options);
|
|
|
|
/// <summary>
|
|
/// A base non-generic pending operation
|
|
/// </summary>
|
|
private abstract class PendingOperationBase
|
|
{
|
|
/// <summary>
|
|
/// The execution context
|
|
/// </summary>
|
|
public ExecuteContext Context { get; }
|
|
/// <summary>
|
|
/// Whether to wait for the operation to complete.
|
|
/// If false, the operation is queued and the task is returned after the operation is queued.
|
|
/// </summary>
|
|
public bool WaitForComplete { get; }
|
|
/// <summary>
|
|
/// The cancellation token
|
|
/// </summary>
|
|
public CancellationToken CancelToken { get; set; }
|
|
/// <summary>
|
|
/// The type of operation the pending operation represents
|
|
/// </summary>
|
|
public abstract BackendActionType Operation { get; }
|
|
/// <summary>
|
|
/// The remote filename, if any
|
|
/// </summary>
|
|
public virtual string RemoteFilename => string.Empty;
|
|
/// <summary>
|
|
/// The remote size, if any
|
|
/// </summary>
|
|
public virtual long Size => -1L;
|
|
|
|
/// <summary>
|
|
/// Sets the operation as cancelled
|
|
/// </summary>
|
|
public abstract void SetCancelled();
|
|
/// <summary>
|
|
/// Sets the operation as failed
|
|
/// </summary>
|
|
/// <param name="ex">The exception</param>
|
|
public abstract void SetFailed(Exception ex);
|
|
|
|
/// <summary>
|
|
/// Creates a new pending operation
|
|
/// </summary>
|
|
/// <param name="context">The execution context</param>
|
|
/// <param name="waitForComplete">Whether to wait for the operation to complete</param>
|
|
/// <param name="cancelToken">The cancellation token</param>
|
|
public PendingOperationBase(ExecuteContext context, bool waitForComplete, CancellationToken cancelToken)
|
|
{
|
|
Context = context;
|
|
WaitForComplete = waitForComplete;
|
|
CancelToken = cancelToken;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// The basic queued backend operation
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Creates a new pending operation
|
|
/// </remarks>
|
|
/// <param name="context">The execution context</param>
|
|
/// <param name="waitForComplete">Whether to wait for the operation to complete</param>
|
|
/// <param name="cancelToken">The cancellation token</param>
|
|
private abstract class PendingOperation<TResult>(ExecuteContext context, bool waitForComplete, CancellationToken cancelToken) : PendingOperationBase(context, waitForComplete, cancelToken)
|
|
{
|
|
/// <summary>
|
|
/// A signal for the task completion
|
|
/// </summary>
|
|
private readonly TaskCompletionSource<TResult> taskCompleteSignal = new TaskCompletionSource<TResult>(TaskCreationOptions.RunContinuationsAsynchronously);
|
|
/// <summary>
|
|
/// The task that is completed when the operation is done
|
|
/// </summary>
|
|
public Task<TResult> GetResultAsync() => taskCompleteSignal.Task;
|
|
|
|
/// <summary>
|
|
/// Sets the operation as complete
|
|
/// </summary>
|
|
public void SetComplete(TResult result)
|
|
{
|
|
taskCompleteSignal.TrySetResult(result);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets the operation as failed
|
|
/// </summary>
|
|
/// <param name="ex">The exception</param>
|
|
public override void SetFailed(Exception ex)
|
|
{
|
|
taskCompleteSignal.TrySetException(ex);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets the operation as cancelled
|
|
/// </summary>
|
|
public override void SetCancelled()
|
|
{
|
|
taskCompleteSignal.TrySetCanceled();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Executes the operation
|
|
/// </summary>
|
|
/// <param name="backend">The backend to execute the operation on</param>
|
|
/// <param name="cancelToken">The cancellation token</param>
|
|
/// <returns>An awaitable task</returns>
|
|
public abstract Task<TResult> ExecuteAsync(IBackend backend, CancellationToken cancelToken);
|
|
}
|
|
|
|
} |