// ----------------------------------------------------------------------- // // Copyright 2014 MIT Licence. See licence.md for more information. // // ----------------------------------------------------------------------- namespace Perspex.Win32.Threading { using System; using System.Threading; using System.Threading.Tasks; using NGenerics.DataStructures.Queues; using Perspex.Platform; using Perspex.Threading; using Splat; /// /// A main loop in a . /// internal class MainLoop { private static IPlatformThreadingInterface platform; private PriorityQueue queue = new PriorityQueue(PriorityQueueType.Maximum); /// /// Initializes static members of the class. /// static MainLoop() { platform = Locator.Current.GetService(); } /// /// Runs the main loop. /// /// /// A cancellation token used to exit the main loop. /// public void Run(CancellationToken cancellationToken) { while (!cancellationToken.IsCancellationRequested) { RunJobs(); platform.ProcessMessage(); } } /// /// Runs continuations pushed on the loop. /// public void RunJobs() { Job job = null; while (job != null || this.queue.Count > 0) { if (job == null) { lock (this.queue) { job = this.queue.Dequeue(); } } if (job.Priority < DispatcherPriority.Input && platform.HasMessages()) { break; } if (job.TaskCompletionSource == null) { job.Action(); } else { try { job.Action(); job.TaskCompletionSource.SetResult(null); } catch (Exception e) { job.TaskCompletionSource.SetException(e); } } job = null; } } /// /// Invokes a method on the main loop. /// /// The method. /// The priority with which to invoke the method. /// A task that can be used to track the method's execution. public Task InvokeAsync(Action action, DispatcherPriority priority) { var job = new Job(action, priority, false); this.AddJob(job); return job.TaskCompletionSource.Task; } /// /// Post action that will be invoked on main thread /// /// The method. /// /// The priority with which to invoke the method. internal void Post(Action action, DispatcherPriority priority) { this.AddJob(new Job(action, priority, true)); } private void AddJob(Job job) { lock (this.queue) { this.queue.Add(job, job.Priority); } platform.Wake(); } /// /// A job to run. /// private class Job { /// /// Initializes a new instance of the class. /// /// The method to call. /// The job priority. /// Do not wrap excepption in TaskCompletionSource public Job(Action action, DispatcherPriority priority, bool throwOnUiThread) { this.Action = action; this.Priority = priority; this.TaskCompletionSource = throwOnUiThread ? null : new TaskCompletionSource(); } /// /// Gets the method to call. /// public Action Action { get; } /// /// Gets the job priority. /// public DispatcherPriority Priority { get; } /// /// Gets the task completion source. /// public TaskCompletionSource TaskCompletionSource { get; } } } }