// 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);
}
///
public Task InvokeTaskAsync(Action action, DispatcherPriority priority = DispatcherPriority.Normal)
{
return _jobRunner?.InvokeAsync(action, priority);
}
///
public void InvokeAsync(Action action, DispatcherPriority priority = DispatcherPriority.Normal)
{
_jobRunner?.Post(action, priority);
}
///
/// 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;
}
}
}
}