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