// 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.Threading;
using System.Threading.Tasks;
using Avalonia.Platform;
namespace Avalonia.Threading
{
///
/// Provides services for managing work items on a thread.
///
///
/// In Avalonia, there is usually only a single in the application -
/// the one for the UI thread, retrieved via the property.
///
public class Dispatcher : IDispatcher
{
private readonly JobRunner _jobRunner;
private IPlatformThreadingInterface _platform;
public static Dispatcher UIThread { get; } =
new Dispatcher(AvaloniaLocator.Current.GetService());
public Dispatcher(IPlatformThreadingInterface platform)
{
_platform = platform;
_jobRunner = new JobRunner(platform);
if (_platform != null)
{
_platform.Signaled += _jobRunner.RunJobs;
}
}
///
/// Checks that the current thread is the UI thread.
///
public bool CheckAccess() => _platform?.CurrentThreadIsLoopThread ?? true;
///
/// Checks that the current thread is the UI thread and throws if not.
///
///
/// The current thread is not the UI thread.
///
public void VerifyAccess()
{
if (!CheckAccess())
throw new InvalidOperationException("Call from invalid thread");
}
///
/// Runs the dispatcher's main loop.
///
///
/// A cancellation token used to exit the main loop.
///
public void MainLoop(CancellationToken cancellationToken)
{
var platform = AvaloniaLocator.Current.GetService();
cancellationToken.Register(() => platform.Signal(DispatcherPriority.Send));
platform.RunLoop(cancellationToken);
}
///
/// Runs continuations pushed on the loop.
///
public void RunJobs()
{
_jobRunner?.RunJobs(null);
}
///
/// Use this method to ensure that more prioritized tasks are executed
///
///
public void RunJobs(DispatcherPriority minimumPriority) => _jobRunner.RunJobs(minimumPriority);
///
public Task InvokeAsync(Action action, DispatcherPriority priority = DispatcherPriority.Normal)
{
Contract.Requires(action != null);
return _jobRunner?.InvokeAsync(action, priority);
}
///
public void Post(Action action, DispatcherPriority priority = DispatcherPriority.Normal)
{
Contract.Requires(action != null);
_jobRunner?.Post(action, priority);
}
///
/// 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
///
///
internal void EnsurePriority(DispatcherPriority currentPriority)
{
if (currentPriority == DispatcherPriority.MaxValue)
return;
currentPriority += 1;
_jobRunner.RunJobs(currentPriority);
}
///
/// Allows unit tests to change the platform threading interface.
///
internal void UpdateServices()
{
if (_platform != null)
{
_platform.Signaled -= _jobRunner.RunJobs;
}
_platform = AvaloniaLocator.Current.GetService();
_jobRunner.UpdateServices();
if (_platform != null)
{
_platform.Signaled += _jobRunner.RunJobs;
}
}
}
}