// 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; } } } }