csharpc-sharpdotnetxamlavaloniauicross-platformcross-platform-xamlavaloniaguimulti-platformuser-interfacedotnetcore
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
227 lines
7.3 KiB
227 lines
7.3 KiB
// Copyright (c) The Avalonia Project. All rights reserved.
|
|
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Threading.Tasks;
|
|
using Avalonia.Platform;
|
|
|
|
namespace Avalonia.Threading
|
|
{
|
|
/// <summary>
|
|
/// A main loop in a <see cref="Dispatcher"/>.
|
|
/// </summary>
|
|
internal class JobRunner
|
|
{
|
|
private IPlatformThreadingInterface _platform;
|
|
|
|
private readonly Queue<IJob>[] _queues = Enumerable.Range(0, (int) DispatcherPriority.MaxValue + 1)
|
|
.Select(_ => new Queue<IJob>()).ToArray();
|
|
|
|
public JobRunner(IPlatformThreadingInterface platform)
|
|
{
|
|
_platform = platform;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Runs continuations pushed on the loop.
|
|
/// </summary>
|
|
/// <param name="priority">Priority to execute jobs for. Pass null if platform doesn't have internal priority system</param>
|
|
public void RunJobs(DispatcherPriority? priority)
|
|
{
|
|
var minimumPriority = priority ?? DispatcherPriority.MinValue;
|
|
while (true)
|
|
{
|
|
var job = GetNextJob(minimumPriority);
|
|
if (job == null)
|
|
return;
|
|
|
|
job.Run();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Invokes a method on the main loop.
|
|
/// </summary>
|
|
/// <param name="action">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 InvokeAsync(Action action, DispatcherPriority priority)
|
|
{
|
|
var job = new Job(action, priority, false);
|
|
AddJob(job);
|
|
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>
|
|
/// Post action that will be invoked on main thread
|
|
/// </summary>
|
|
/// <param name="action">The method.</param>
|
|
///
|
|
/// <param name="priority">The priority with which to invoke the method.</param>
|
|
internal void Post(Action action, DispatcherPriority priority)
|
|
{
|
|
AddJob(new Job(action, priority, true));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Allows unit tests to change the platform threading interface.
|
|
/// </summary>
|
|
internal void UpdateServices()
|
|
{
|
|
_platform = AvaloniaLocator.Current.GetService<IPlatformThreadingInterface>();
|
|
}
|
|
|
|
private void AddJob(IJob job)
|
|
{
|
|
bool needWake;
|
|
var queue = _queues[(int) job.Priority];
|
|
lock (queue)
|
|
{
|
|
needWake = queue.Count == 0;
|
|
queue.Enqueue(job);
|
|
}
|
|
if (needWake)
|
|
_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 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 exception in TaskCompletionSource</param>
|
|
public Job(Action action, DispatcherPriority priority, bool throwOnUiThread)
|
|
{
|
|
_action = action;
|
|
Priority = priority;
|
|
_taskCompletionSource = throwOnUiThread ? null : new TaskCompletionSource<object>();
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public DispatcherPriority Priority { get; }
|
|
|
|
/// <summary>
|
|
/// The task.
|
|
/// </summary>
|
|
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>
|
|
/// Initializes a new instance of the <see cref="Job"/> class.
|
|
/// </summary>
|
|
/// <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>
|
|
/// The task.
|
|
/// </summary>
|
|
public Task<TResult> Task => _taskCompletionSource.Task;
|
|
|
|
/// <inheritdoc/>
|
|
void IJob.Run()
|
|
{
|
|
try
|
|
{
|
|
var result = _function();
|
|
_taskCompletionSource.SetResult(result);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
_taskCompletionSource.SetException(e);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|