`TaskExtensions`: Re-implement using `Task.WaitAsync(..)`.
MODIFY
WalletWasabi.Tests/UnitTests/AbandonedTasksTests.cs
WalletWasabi.Tests/UnitTests/AbandonedTasksTests.cs
@@ -46,7 +46,7 @@ namespace WalletWasabi.Tests.UnitTests
Assert.False(waitAllTask.IsCompleted);
// Try to await but it should not finish before the cancellation so we will get OperationCanceledException.
- await Assert.ThrowsAsync<OperationCanceledException>(async () => await waitAllTask.WithAwaitCancellationAsync(50));
+ await Assert.ThrowsAsync<OperationCanceledException>(async () => await waitAllTask.WithAwaitCancellationAsync(TimeSpan.FromMilliseconds(50)));
// Ok now cancel the last Task.
cts2.Cancel();

MODIFY
WalletWasabi/Extensions/TaskExtensions.cs
WalletWasabi/Extensions/TaskExtensions.cs
namespace System.Threading.Tasks
{
+ /// <remarks>The class implements methods for backward compatibility.</remarks>
public static class TaskExtensions
{
/// <summary>
- /// UNSAFE! You are cancelling the wait on the callback of the original Task, not cancelling the operation itself.
- /// https://stackoverflow.com/questions/14524209/what-is-the-correct-way-to-cancel-an-async-operation-that-doesnt-accept-a-cance/14524565#14524565
+ /// Implements method that behaves as <see cref="Task{T}.WaitAsync(CancellationToken)"/> but
+ /// it throws <see cref="OperationCanceledException"/> instead of <see cref="TimeoutException"/>.
/// </summary>
- public static async Task<T> WithAwaitCancellationAsync<T>(this Task<T> me, CancellationToken cancel, int waitForGracefulTerminationMilliseconds = 0)
+ public static async Task<T> WithAwaitCancellationAsync<T>(this Task<T> task, CancellationToken cancellationToken)
{
- // The task completion source.
- var tcs = new TaskCompletionSource<bool>();
-
- // Register with the cancellation token.
- using (cancel.Register(s => (s as TaskCompletionSource<bool>)?.TrySetResult(true), tcs))
- {
- // If the task waited on is the cancellation token...
- if (me != await Task.WhenAny(me, tcs.Task).ConfigureAwait(false))
+ try
{
- if (waitForGracefulTerminationMilliseconds <= 0)
- {
- throw new OperationCanceledException(cancel);
+ return await task.WaitAsync(cancellationToken).ConfigureAwait(false);
}
- else
+ catch (TimeoutException e)
{
- using var cts = new CancellationTokenSource(waitForGracefulTerminationMilliseconds);
- return await me.WithAwaitCancellationAsync(cts.Token).ConfigureAwait(false);
- }
+ throw new OperationCanceledException("Timed out.", innerException: e);
}
}
- // Wait for one or the other to complete.
- return await me.ConfigureAwait(false);
- }
-
/// <summary>
- /// UNSAFE! You are cancelling the wait on the callback of the original Task, not cancelling the operation itself.
- /// https://stackoverflow.com/questions/14524209/what-is-the-correct-way-to-cancel-an-async-operation-that-doesnt-accept-a-cance/14524565#14524565
+ /// Implements method that behaves as <see cref="Task.WaitAsync(CancellationToken)"/> but
+ /// it throws <see cref="OperationCanceledException"/> instead of <see cref="TimeoutException"/>.
/// </summary>
- public static async Task WithAwaitCancellationAsync(this Task me, CancellationToken cancel, int waitForGracefulTerminationMilliseconds = 0)
+ public static async Task WithAwaitCancellationAsync(this Task task, CancellationToken cancellationToken)
{
- // The task completion source.
- var tcs = new TaskCompletionSource<bool>();
-
- // Register with the cancellation token.
- using (cancel.Register(s => (s as TaskCompletionSource<bool>)?.TrySetResult(true), tcs))
+ try
{
- // If the task waited on is the cancellation token...
- if (me != await Task.WhenAny(me, tcs.Task).ConfigureAwait(false))
- {
- if (waitForGracefulTerminationMilliseconds <= 0)
+ await task.WaitAsync(cancellationToken).ConfigureAwait(false);
+ }
+ catch (TimeoutException e)
{
- throw new OperationCanceledException(cancel);
+ throw new OperationCanceledException("Timed out.", innerException: e);
+ }
}
- else
+
+ /// <summary>
+ /// Implements method that behaves as <see cref="Task{T}.WaitAsync(TimeSpan)"/> but
+ /// it throws <see cref="OperationCanceledException"/> instead of <see cref="TimeoutException"/>.
+ /// </summary>
+ public static async Task<T> WithAwaitCancellationAsync<T>(this Task<T> task, TimeSpan timeout)
{
- using var cts = new CancellationTokenSource(waitForGracefulTerminationMilliseconds);
- await me.WithAwaitCancellationAsync(cts.Token).ConfigureAwait(false);
- return;
+ try
+ {
+ return await task.WaitAsync(timeout).ConfigureAwait(false);
}
+ catch (TimeoutException e)
+ {
+ throw new OperationCanceledException("Timed out.", innerException: e);
}
}
- // Wait for one or the other to complete.
- await me.ConfigureAwait(false);
+ /// <summary>
+ /// Implements method that behaves as <see cref="Task.WaitAsync(TimeSpan)"/> but
+ /// it throws <see cref="OperationCanceledException"/> instead of <see cref="TimeoutException"/>.
+ /// </summary>
+ public static async Task WithAwaitCancellationAsync(this Task task, TimeSpan timeout)
+ {
+ try
+ {
+ await task.WaitAsync(timeout).ConfigureAwait(false);
+ }
+ catch (TimeoutException e)
+ {
+ throw new OperationCanceledException("Timed out.", innerException: e);
+ }
}
-
- public static async Task<T> WithAwaitCancellationAsync<T>(this Task<T> me, int millisecondsDelay)
- => await me.WithAwaitCancellationAsync(ThreadingHelpers.Cancelled, millisecondsDelay).ConfigureAwait(false);
-
- public static async Task<T> WithAwaitCancellationAsync<T>(this Task<T> me, TimeSpan timeout)
- => await me.WithAwaitCancellationAsync(ThreadingHelpers.Cancelled, (int)timeout.TotalMilliseconds).ConfigureAwait(false);
-
- public static async Task WithAwaitCancellationAsync(this Task me, int millisecondsDelay)
- => await me.WithAwaitCancellationAsync(ThreadingHelpers.Cancelled, millisecondsDelay).ConfigureAwait(false);
-
- public static async Task WithAwaitCancellationAsync(this Task me, TimeSpan timeout)
- => await me.WithAwaitCancellationAsync(ThreadingHelpers.Cancelled, (int)timeout.TotalMilliseconds).ConfigureAwait(false);
}
}

MODIFY
WalletWasabi/Helpers/EventAwaiter.cs
WalletWasabi/Helpers/EventAwaiter.cs
using System.Linq;
-using System.Threading;
using System.Threading.Tasks;
namespace System
@@ -17,11 +16,5 @@ namespace System
public new async Task<TEventArgs> WaitAsync(TimeSpan timeout)
=> await Task.WithAwaitCancellationAsync(timeout).ConfigureAwait(false);
-
- public new async Task<TEventArgs> WaitAsync(int millisecondsTimeout)
- => await Task.WithAwaitCancellationAsync(millisecondsTimeout).ConfigureAwait(false);
-
- public new async Task<TEventArgs> WaitAsync(CancellationToken cancel, int waitForGracefulTerminationMilliseconds = 0)
- => await Task.WithAwaitCancellationAsync(cancel, waitForGracefulTerminationMilliseconds).ConfigureAwait(false);
}
}

MODIFY
WalletWasabi/Helpers/EventsAwaiter.cs
WalletWasabi/Helpers/EventsAwaiter.cs
using System.Collections.Generic;
using System.Linq;
-using System.Threading;
using System.Threading.Tasks;
using WalletWasabi.Helpers;
@@ -48,11 +47,5 @@ namespace System
public async Task<IEnumerable<TEventArgs>> WaitAsync(TimeSpan timeout)
=> await Task.WhenAll(Tasks).WithAwaitCancellationAsync(timeout).ConfigureAwait(false);
-
- public async Task<IEnumerable<TEventArgs>> WaitAsync(int millisecondsTimeout)
- => await Task.WhenAll(Tasks).WithAwaitCancellationAsync(millisecondsTimeout).ConfigureAwait(false);
-
- public async Task<IEnumerable<TEventArgs>> WaitAsync(CancellationToken cancel, int waitForGracefulTerminationMilliseconds = 0)
- => await Task.WhenAll(Tasks).WithAwaitCancellationAsync(cancel, waitForGracefulTerminationMilliseconds).ConfigureAwait(false);
}
}

`TaskExtensions`: Re-implement using `Task.WaitAsync(..)`.