Browse Source

feat: Implement Post<T> method of IDispatcher.

pull/6527/head
Giuseppe Lippolis 5 years ago
parent
commit
75bd449a69
  1. 3
      src/Avalonia.Base/ApiCompatBaseline.txt
  2. 11
      src/Avalonia.Base/Threading/Dispatcher.cs
  3. 84
      src/Avalonia.Base/Threading/JobRunner.cs
  4. 6
      tests/Avalonia.UnitTests/ImmediateDispatcher.cs

3
src/Avalonia.Base/ApiCompatBaseline.txt

@ -0,0 +1,3 @@
Compat issues with assembly Avalonia.Base:
InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Threading.IDispatcher.Post<T>(System.Action<T>, T, Avalonia.Threading.DispatcherPriority)' is present in the implementation but not in the contract.
Total Issues: 1

11
src/Avalonia.Base/Threading/Dispatcher.cs

@ -81,7 +81,7 @@ namespace Avalonia.Threading
Contract.Requires<ArgumentNullException>(action != null);
return _jobRunner.InvokeAsync(action, priority);
}
/// <inheritdoc/>
public Task<TResult> InvokeAsync<TResult>(Func<TResult> function, DispatcherPriority priority = DispatcherPriority.Normal)
{
@ -110,6 +110,13 @@ namespace Avalonia.Threading
_jobRunner.Post(action, priority);
}
/// <inheritdoc/>
public void Post<T>(Action<T> action, T arg, DispatcherPriority priority = DispatcherPriority.Normal)
{
Contract.Requires<ArgumentNullException>(action != null);
_jobRunner.Post(action, arg, priority);
}
/// <summary>
/// This is needed for platform backends that don't have internal priority system (e. g. win32)
/// To ensure that there are no jobs with higher priority
@ -142,4 +149,4 @@ namespace Avalonia.Threading
}
}
}
}
}

84
src/Avalonia.Base/Threading/JobRunner.cs

@ -13,7 +13,7 @@ namespace Avalonia.Threading
{
private IPlatformThreadingInterface _platform;
private readonly Queue<IJob>[] _queues = Enumerable.Range(0, (int) DispatcherPriority.MaxValue + 1)
private readonly Queue<IJob>[] _queues = Enumerable.Range(0, (int)DispatcherPriority.MaxValue + 1)
.Select(_ => new Queue<IJob>()).ToArray();
public JobRunner(IPlatformThreadingInterface platform)
@ -59,7 +59,7 @@ namespace Avalonia.Threading
/// <returns>A task that can be used to track the method's execution.</returns>
public Task<TResult> InvokeAsync<TResult>(Func<TResult> function, DispatcherPriority priority)
{
var job = new Job<TResult>(function, priority);
var job = new JobWithResult<TResult>(function, priority);
AddJob(job);
return job.Task;
}
@ -75,6 +75,17 @@ namespace Avalonia.Threading
AddJob(new Job(action, priority, true));
}
/// <summary>
/// Post action that will be invoked on main thread
/// </summary>
/// <param name="action">The method to call.</param>
/// <param name="parameter">The parameter of method to call.</param>
/// <param name="priority">The priority with which to invoke the method.</param>
internal void Post<T>(Action<T> action, T parameter, DispatcherPriority priority)
{
AddJob(new Job<T>(action, parameter, priority, true));
}
/// <summary>
/// Allows unit tests to change the platform threading interface.
/// </summary>
@ -86,7 +97,7 @@ namespace Avalonia.Threading
private void AddJob(IJob job)
{
bool needWake;
var queue = _queues[(int) job.Priority];
var queue = _queues[(int)job.Priority];
lock (queue)
{
needWake = queue.Count == 0;
@ -98,7 +109,7 @@ namespace Avalonia.Threading
private IJob GetNextJob(DispatcherPriority minimumPriority)
{
for (int c = (int) DispatcherPriority.MaxValue; c >= (int) minimumPriority; c--)
for (int c = (int)DispatcherPriority.MaxValue; c >= (int)minimumPriority; c--)
{
var q = _queues[c];
lock (q)
@ -109,14 +120,14 @@ namespace Avalonia.Threading
}
return null;
}
private interface IJob
{
/// <summary>
/// Gets the job priority.
/// </summary>
DispatcherPriority Priority { get; }
/// <summary>
/// Runs the job.
/// </summary>
@ -157,7 +168,7 @@ namespace Avalonia.Threading
/// The task.
/// </summary>
public Task Task => _taskCompletionSource?.Task;
/// <inheritdoc/>
void IJob.Run()
{
@ -177,11 +188,60 @@ namespace Avalonia.Threading
}
}
}
/// <summary>
/// A job to run.
/// A tipizzed job to run.
/// </summary>
private sealed class Job<TResult> : IJob
private sealed class Job<T> : IJob
{
private readonly Action<T> _action;
private readonly T _parameter;
private readonly TaskCompletionSource<object> _taskCompletionSource;
/// <summary>
/// Initializes a new instance of the <see cref="Job"/> class.
/// </summary>
/// <param name="action">The method to call.</param>
/// <param name="parameter">The parameter of method to call.</param>
/// <param name="priority">The job priority.</param>
/// <param name="throwOnUiThread">Do not wrap exception in TaskCompletionSource</param>
public Job(Action<T> action, T parameter, DispatcherPriority priority, bool throwOnUiThread)
{
_action = action;
_parameter = parameter;
Priority = priority;
_taskCompletionSource = throwOnUiThread ? null : new TaskCompletionSource<object>();
}
/// <inheritdoc/>
public DispatcherPriority Priority { get; }
/// <inheritdoc/>
void IJob.Run()
{
if (_taskCompletionSource == null)
{
_action(_parameter);
return;
}
try
{
_action(_parameter);
_taskCompletionSource.SetResult(null);
}
catch (Exception e)
{
_taskCompletionSource.SetException(e);
}
}
}
/// <summary>
/// A job to run thath return value.
/// </summary>
private sealed class JobWithResult<TResult> : IJob
{
private readonly Func<TResult> _function;
private readonly TaskCompletionSource<TResult> _taskCompletionSource;
@ -191,7 +251,7 @@ namespace Avalonia.Threading
/// </summary>
/// <param name="function">The method to call.</param>
/// <param name="priority">The job priority.</param>
public Job(Func<TResult> function, DispatcherPriority priority)
public JobWithResult(Func<TResult> function, DispatcherPriority priority)
{
_function = function;
Priority = priority;
@ -200,7 +260,7 @@ namespace Avalonia.Threading
/// <inheritdoc/>
public DispatcherPriority Priority { get; }
/// <summary>
/// The task.
/// </summary>

6
tests/Avalonia.UnitTests/ImmediateDispatcher.cs

@ -21,6 +21,12 @@ namespace Avalonia.UnitTests
action();
}
/// <inheritdoc/>
public void Post<T>(Action<T> action, T arg, DispatcherPriority priority = DispatcherPriority.Normal)
{
action(arg);
}
/// <inheritdoc/>
public Task InvokeAsync(Action action, DispatcherPriority priority = DispatcherPriority.Normal)
{

Loading…
Cancel
Save