|
|
|
@ -14,32 +14,16 @@ namespace Avalonia.Threading |
|
|
|
/// </summary>
|
|
|
|
internal class JobRunner |
|
|
|
{ |
|
|
|
|
|
|
|
|
|
|
|
private IPlatformThreadingInterface _platform; |
|
|
|
|
|
|
|
private Queue<Job>[] _queues = Enumerable.Range(0, (int) DispatcherPriority.MaxValue + 1) |
|
|
|
.Select(_ => new Queue<Job>()).ToArray(); |
|
|
|
private readonly Queue<IJob>[] _queues = Enumerable.Range(0, (int) DispatcherPriority.MaxValue + 1) |
|
|
|
.Select(_ => new Queue<IJob>()).ToArray(); |
|
|
|
|
|
|
|
public JobRunner(IPlatformThreadingInterface platform) |
|
|
|
{ |
|
|
|
_platform = platform; |
|
|
|
} |
|
|
|
|
|
|
|
Job GetNextJob(DispatcherPriority minimumPriority) |
|
|
|
{ |
|
|
|
for (int c = (int) DispatcherPriority.MaxValue; c >= (int) minimumPriority; c--) |
|
|
|
{ |
|
|
|
var q = _queues[c]; |
|
|
|
lock (q) |
|
|
|
{ |
|
|
|
if (q.Count > 0) |
|
|
|
return q.Dequeue(); |
|
|
|
} |
|
|
|
} |
|
|
|
return null; |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Runs continuations pushed on the loop.
|
|
|
|
/// </summary>
|
|
|
|
@ -52,24 +36,8 @@ namespace Avalonia.Threading |
|
|
|
var job = GetNextJob(minimumPriority); |
|
|
|
if (job == null) |
|
|
|
return; |
|
|
|
|
|
|
|
|
|
|
|
if (job.TaskCompletionSource == null) |
|
|
|
{ |
|
|
|
job.Action(); |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
try |
|
|
|
{ |
|
|
|
job.Action(); |
|
|
|
job.TaskCompletionSource.SetResult(null); |
|
|
|
} |
|
|
|
catch (Exception e) |
|
|
|
{ |
|
|
|
job.TaskCompletionSource.SetException(e); |
|
|
|
} |
|
|
|
} |
|
|
|
job.Run(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
@ -83,7 +51,20 @@ namespace Avalonia.Threading |
|
|
|
{ |
|
|
|
var job = new Job(action, priority, false); |
|
|
|
AddJob(job); |
|
|
|
return job.TaskCompletionSource.Task; |
|
|
|
return job.Task; |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Invokes a method on the main loop.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="function">The method.</param>
|
|
|
|
/// <param name="priority">The priority with which to invoke the method.</param>
|
|
|
|
/// <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); |
|
|
|
AddJob(job); |
|
|
|
return job.Task; |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
@ -105,9 +86,9 @@ namespace Avalonia.Threading |
|
|
|
_platform = AvaloniaLocator.Current.GetService<IPlatformThreadingInterface>(); |
|
|
|
} |
|
|
|
|
|
|
|
private void AddJob(Job job) |
|
|
|
private void AddJob(IJob job) |
|
|
|
{ |
|
|
|
var needWake = false; |
|
|
|
bool needWake; |
|
|
|
var queue = _queues[(int) job.Priority]; |
|
|
|
lock (queue) |
|
|
|
{ |
|
|
|
@ -118,38 +99,129 @@ namespace Avalonia.Threading |
|
|
|
_platform?.Signal(job.Priority); |
|
|
|
} |
|
|
|
|
|
|
|
private IJob GetNextJob(DispatcherPriority minimumPriority) |
|
|
|
{ |
|
|
|
for (int c = (int) DispatcherPriority.MaxValue; c >= (int) minimumPriority; c--) |
|
|
|
{ |
|
|
|
var q = _queues[c]; |
|
|
|
lock (q) |
|
|
|
{ |
|
|
|
if (q.Count > 0) |
|
|
|
return q.Dequeue(); |
|
|
|
} |
|
|
|
} |
|
|
|
return null; |
|
|
|
} |
|
|
|
|
|
|
|
private interface IJob |
|
|
|
{ |
|
|
|
/// <summary>
|
|
|
|
/// Gets the job priority.
|
|
|
|
/// </summary>
|
|
|
|
DispatcherPriority Priority { get; } |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Runs the job.
|
|
|
|
/// </summary>
|
|
|
|
void Run(); |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// A job to run.
|
|
|
|
/// </summary>
|
|
|
|
private class Job |
|
|
|
private sealed class Job : IJob |
|
|
|
{ |
|
|
|
/// <summary>
|
|
|
|
/// The method to call.
|
|
|
|
/// </summary>
|
|
|
|
private readonly Action _action; |
|
|
|
/// <summary>
|
|
|
|
/// The task completion source.
|
|
|
|
/// </summary>
|
|
|
|
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="priority">The job priority.</param>
|
|
|
|
/// <param name="throwOnUiThread">Do not wrap excepption in TaskCompletionSource</param>
|
|
|
|
/// <param name="throwOnUiThread">Do not wrap exception in TaskCompletionSource</param>
|
|
|
|
public Job(Action action, DispatcherPriority priority, bool throwOnUiThread) |
|
|
|
{ |
|
|
|
Action = action; |
|
|
|
_action = action; |
|
|
|
Priority = priority; |
|
|
|
TaskCompletionSource = throwOnUiThread ? null : new TaskCompletionSource<object>(); |
|
|
|
_taskCompletionSource = throwOnUiThread ? null : new TaskCompletionSource<object>(); |
|
|
|
} |
|
|
|
|
|
|
|
/// <inheritdoc/>
|
|
|
|
public DispatcherPriority Priority { get; } |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Gets the method to call.
|
|
|
|
/// The task.
|
|
|
|
/// </summary>
|
|
|
|
public Action Action { get; } |
|
|
|
public Task Task => _taskCompletionSource?.Task; |
|
|
|
|
|
|
|
/// <inheritdoc/>
|
|
|
|
void IJob.Run() |
|
|
|
{ |
|
|
|
if (_taskCompletionSource == null) |
|
|
|
{ |
|
|
|
_action(); |
|
|
|
return; |
|
|
|
} |
|
|
|
try |
|
|
|
{ |
|
|
|
_action(); |
|
|
|
_taskCompletionSource.SetResult(null); |
|
|
|
} |
|
|
|
catch (Exception e) |
|
|
|
{ |
|
|
|
_taskCompletionSource.SetException(e); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// A job to run.
|
|
|
|
/// </summary>
|
|
|
|
private sealed class Job<TResult> : IJob |
|
|
|
{ |
|
|
|
private readonly Func<TResult> _function; |
|
|
|
private readonly TaskCompletionSource<TResult> _taskCompletionSource; |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Gets the job priority.
|
|
|
|
/// Initializes a new instance of the <see cref="Job"/> class.
|
|
|
|
/// </summary>
|
|
|
|
public DispatcherPriority Priority { get; } |
|
|
|
/// <param name="function">The method to call.</param>
|
|
|
|
/// <param name="priority">The job priority.</param>
|
|
|
|
public Job(Func<TResult> function, DispatcherPriority priority) |
|
|
|
{ |
|
|
|
_function = function; |
|
|
|
Priority = priority; |
|
|
|
_taskCompletionSource = new TaskCompletionSource<TResult>(); |
|
|
|
} |
|
|
|
|
|
|
|
/// <inheritdoc/>
|
|
|
|
public DispatcherPriority Priority { get; } |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Gets the task completion source.
|
|
|
|
/// The task.
|
|
|
|
/// </summary>
|
|
|
|
public TaskCompletionSource<object> TaskCompletionSource { get; } |
|
|
|
public Task<TResult> Task => _taskCompletionSource.Task; |
|
|
|
|
|
|
|
/// <inheritdoc/>
|
|
|
|
void IJob.Run() |
|
|
|
{ |
|
|
|
try |
|
|
|
{ |
|
|
|
var result = _function(); |
|
|
|
_taskCompletionSource.SetResult(result); |
|
|
|
} |
|
|
|
catch (Exception e) |
|
|
|
{ |
|
|
|
_taskCompletionSource.SetException(e); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|