Browse Source

Make sure that Dispatcher.InvokeAsync unwraps tasks and that correct overload is being chosen

pull/11214/head
Nikita Tsukanov 3 years ago
parent
commit
015768c5bd
  1. 42
      src/Avalonia.Base/Threading/Dispatcher.Invoke.cs
  2. 44
      tests/Avalonia.Base.UnitTests/DispatcherTests.cs

42
src/Avalonia.Base/Threading/Dispatcher.Invoke.cs

@ -248,11 +248,11 @@ public partial class Dispatcher
/// An operation representing the queued delegate to be invoked.
/// </returns>
/// <remarks>
/// Note that the default priority is DispatcherPriority.Normal.
/// Note that the default priority is DispatcherPriority.Default.
/// </remarks>
public DispatcherOperation InvokeAsync(Action callback)
{
return InvokeAsync(callback, DispatcherPriority.Normal, CancellationToken.None);
return InvokeAsync(callback, default, CancellationToken.None);
}
/// <summary>
@ -326,11 +326,11 @@ public partial class Dispatcher
/// An operation representing the queued delegate to be invoked.
/// </returns>
/// <remarks>
/// Note that the default priority is DispatcherPriority.Normal.
/// Note that the default priority is DispatcherPriority.Default.
/// </remarks>
public DispatcherOperation<TResult> InvokeAsync<TResult>(Func<TResult> callback)
{
return InvokeAsync(callback, DispatcherPriority.Normal, CancellationToken.None);
return InvokeAsync(callback, DispatcherPriority.Default, CancellationToken.None);
}
/// <summary>
@ -541,6 +541,18 @@ public partial class Dispatcher
InvokeAsyncImpl(new DispatcherOperation(this, priority, action, true), CancellationToken.None);
}
/// <summary>
/// Executes the specified Func&lt;Task&gt; asynchronously on the
/// thread that the Dispatcher was created on
/// </summary>
/// <param name="callback">
/// A Func&lt;Task&gt; delegate to invoke through the dispatcher.
/// </param>
/// <returns>
/// An task that completes after the task returned from callback finishes.
/// </returns>
public Task InvokeAsync(Func<Task> callback) => InvokeAsync(callback, DispatcherPriority.Default);
/// <summary>
/// Executes the specified Func&lt;Task&gt; asynchronously on the
/// thread that the Dispatcher was created on
@ -556,11 +568,29 @@ public partial class Dispatcher
/// <returns>
/// An task that completes after the task returned from callback finishes
/// </returns>
public Task InvokeAsync(Func<Task> callback, DispatcherPriority priority = default)
public Task InvokeAsync(Func<Task> callback, DispatcherPriority priority)
{
_ = callback ?? throw new ArgumentNullException(nameof(callback));
return InvokeAsync<Task>(callback, priority).GetTask().Unwrap();
}
/// <summary>
/// Executes the specified Func&lt;Task&lt;TResult&gt;&gt; asynchronously on the
/// thread that the Dispatcher was created on
/// </summary>
/// <param name="action">
/// A Func&lt;Task&lt;TResult&gt;&gt; delegate to invoke through the dispatcher.
/// </param>
/// <param name="priority">
/// The priority that determines in what order the specified
/// callback is invoked relative to the other pending operations
/// in the Dispatcher.
/// </param>
/// <returns>
/// An task that completes after the task returned from callback finishes
/// </returns>
public Task<TResult> InvokeAsync<TResult>(Func<Task<TResult>> action) =>
InvokeAsync(action, DispatcherPriority.Default);
/// <summary>
/// Executes the specified Func&lt;Task&lt;TResult&gt;&gt; asynchronously on the
@ -577,7 +607,7 @@ public partial class Dispatcher
/// <returns>
/// An task that completes after the task returned from callback finishes
/// </returns>
public Task<TResult> InvokeAsync<TResult>(Func<Task<TResult>> action, DispatcherPriority priority = default)
public Task<TResult> InvokeAsync<TResult>(Func<Task<TResult>> action, DispatcherPriority priority)
{
_ = action ?? throw new ArgumentNullException(nameof(action));
return InvokeAsync<Task<TResult>>(action, priority).GetTask().Unwrap();

44
tests/Avalonia.Base.UnitTests/DispatcherTests.cs

@ -3,6 +3,8 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Avalonia.Controls.Platform;
using Avalonia.Threading;
using Avalonia.Utilities;
using Xunit;
@ -458,4 +460,46 @@ public class DispatcherTests
}
}
[Fact]
public void DispatcherInvokeAsyncUnwrapsTasks()
{
int asyncMethodStage = 0;
async Task AsyncMethod()
{
asyncMethodStage = 1;
await Task.Delay(200);
asyncMethodStage = 2;
}
async Task<int> AsyncMethodWithResult()
{
await Task.Delay(100);
return 1;
}
async Task Test()
{
await Dispatcher.UIThread.InvokeAsync(AsyncMethod);
Assert.Equal(2, asyncMethodStage);
Assert.Equal(1, await Dispatcher.UIThread.InvokeAsync(AsyncMethodWithResult));
asyncMethodStage = 0;
await Dispatcher.UIThread.InvokeAsync(AsyncMethod, DispatcherPriority.Default);
Assert.Equal(2, asyncMethodStage);
Assert.Equal(1, await Dispatcher.UIThread.InvokeAsync(AsyncMethodWithResult, DispatcherPriority.Default));
Dispatcher.UIThread.ExitAllFrames();
}
using (new DispatcherServices(new ManagedDispatcherImpl(null)))
{
var t = Test();
var cts = new CancellationTokenSource();
Task.Delay(3000).ContinueWith(_ => cts.Cancel());
Dispatcher.UIThread.MainLoop(cts.Token);
Assert.True(t.IsCompletedSuccessfully);
t.GetAwaiter().GetResult();
}
}
}
Loading…
Cancel
Save