Dispatcher.cs 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. // Copyright (c) The Avalonia Project. All rights reserved.
  2. // Licensed under the MIT license. See licence.md file in the project root for full license information.
  3. using System;
  4. using System.Threading;
  5. using System.Threading.Tasks;
  6. using Avalonia.Platform;
  7. namespace Avalonia.Threading
  8. {
  9. /// <summary>
  10. /// Provides services for managing work items on a thread.
  11. /// </summary>
  12. /// <remarks>
  13. /// In Avalonia, there is usually only a single <see cref="Dispatcher"/> in the application -
  14. /// the one for the UI thread, retrieved via the <see cref="UIThread"/> property.
  15. /// </remarks>
  16. public class Dispatcher : IDispatcher
  17. {
  18. private readonly JobRunner _jobRunner;
  19. private IPlatformThreadingInterface _platform;
  20. public static Dispatcher UIThread { get; } =
  21. new Dispatcher(AvaloniaLocator.Current.GetService<IPlatformThreadingInterface>());
  22. public Dispatcher(IPlatformThreadingInterface platform)
  23. {
  24. _platform = platform;
  25. _jobRunner = new JobRunner(platform);
  26. if (_platform != null)
  27. {
  28. _platform.Signaled += _jobRunner.RunJobs;
  29. }
  30. }
  31. /// <summary>
  32. /// Checks that the current thread is the UI thread.
  33. /// </summary>
  34. public bool CheckAccess() => _platform?.CurrentThreadIsLoopThread ?? true;
  35. /// <summary>
  36. /// Checks that the current thread is the UI thread and throws if not.
  37. /// </summary>
  38. /// <exception cref="InvalidOperationException">
  39. /// The current thread is not the UI thread.
  40. /// </exception>
  41. public void VerifyAccess()
  42. {
  43. if (!CheckAccess())
  44. throw new InvalidOperationException("Call from invalid thread");
  45. }
  46. /// <summary>
  47. /// Runs the dispatcher's main loop.
  48. /// </summary>
  49. /// <param name="cancellationToken">
  50. /// A cancellation token used to exit the main loop.
  51. /// </param>
  52. public void MainLoop(CancellationToken cancellationToken)
  53. {
  54. var platform = AvaloniaLocator.Current.GetService<IPlatformThreadingInterface>();
  55. cancellationToken.Register(() => platform.Signal(DispatcherPriority.Send));
  56. platform.RunLoop(cancellationToken);
  57. }
  58. /// <summary>
  59. /// Runs continuations pushed on the loop.
  60. /// </summary>
  61. public void RunJobs()
  62. {
  63. _jobRunner?.RunJobs(null);
  64. }
  65. /// <summary>
  66. /// Use this method to ensure that more prioritized tasks are executed
  67. /// </summary>
  68. /// <param name="minimumPriority"></param>
  69. public void RunJobs(DispatcherPriority minimumPriority) => _jobRunner.RunJobs(minimumPriority);
  70. /// <inheritdoc/>
  71. public Task InvokeAsync(Action action, DispatcherPriority priority = DispatcherPriority.Normal)
  72. {
  73. Contract.Requires<ArgumentNullException>(action != null);
  74. return _jobRunner?.InvokeAsync(action, priority);
  75. }
  76. /// <inheritdoc/>
  77. public void Post(Action action, DispatcherPriority priority = DispatcherPriority.Normal)
  78. {
  79. Contract.Requires<ArgumentNullException>(action != null);
  80. _jobRunner?.Post(action, priority);
  81. }
  82. /// <summary>
  83. /// This is needed for platform backends that don't have internal priority system (e. g. win32)
  84. /// To ensure that there are no jobs with higher priority
  85. /// </summary>
  86. /// <param name="currentPriority"></param>
  87. internal void EnsurePriority(DispatcherPriority currentPriority)
  88. {
  89. if (currentPriority == DispatcherPriority.MaxValue)
  90. return;
  91. currentPriority += 1;
  92. _jobRunner.RunJobs(currentPriority);
  93. }
  94. /// <summary>
  95. /// Allows unit tests to change the platform threading interface.
  96. /// </summary>
  97. internal void UpdateServices()
  98. {
  99. if (_platform != null)
  100. {
  101. _platform.Signaled -= _jobRunner.RunJobs;
  102. }
  103. _platform = AvaloniaLocator.Current.GetService<IPlatformThreadingInterface>();
  104. _jobRunner.UpdateServices();
  105. if (_platform != null)
  106. {
  107. _platform.Signaled += _jobRunner.RunJobs;
  108. }
  109. }
  110. }
  111. }