| File: src\Graphics\src\Graphics\Platforms\Windows\AsyncPump.cs | Web Access |
| Project: src\src\Graphics\src\Graphics.Win2D\Graphics.Win2D.csproj (Microsoft.Maui.Graphics.Win2D.WinUI.Desktop) |
using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; #if MAUI_GRAPHICS_WIN2D namespace Microsoft.Maui.Graphics.Win2D #else namespace Microsoft.Maui.Graphics.Platform #endif { /// <summary> /// Provides a pump that supports running asynchronous methods on the current thread. /// /// From MSDN blog post: http://blogs.msdn.com/b/pfxteam/archive/2012/01/20/10259049.aspx /// </summary> #if MAUI_GRAPHICS_WIN2D public #else internal #endif static class AsyncPump { /// <summary>Runs the specified asynchronous function.</summary> /// <param name="func">The asynchronous function to execute.</param> public static void Run(Func<Task> func) { if (func == null) throw new ArgumentNullException("func"); var prevCtx = SynchronizationContext.Current; try { // Establish the new context var syncCtx = new SingleThreadSynchronizationContext(); SynchronizationContext.SetSynchronizationContext(syncCtx); // Invoke the function and alert the context to when it completes var t = func(); if (t == null) throw new InvalidOperationException("No task provided."); t.ContinueWith(delegate { syncCtx.Complete(); }, TaskScheduler.Default); // Pump continuations and propagate any exceptions syncCtx.RunOnCurrentThread(); t.GetAwaiter().GetResult(); } finally { SynchronizationContext.SetSynchronizationContext(prevCtx); } } /// <summary>Runs the specified asynchronous method.</summary> /// <param name="asyncMethod">The asynchronous method to execute.</param> public static T Run<T>(Func<Task<T>> asyncMethod) { if (asyncMethod == null) throw new ArgumentNullException("asyncMethod"); var prevCtx = SynchronizationContext.Current; try { // Establish the new context var syncCtx = new SingleThreadSynchronizationContext(); SynchronizationContext.SetSynchronizationContext(syncCtx); // Invoke the function and alert the context to when it completes var t = asyncMethod(); if (t == null) throw new InvalidOperationException("No task provided."); t.ContinueWith(delegate { syncCtx.Complete(); }, TaskScheduler.Default); // Pump continuations and propagate any exceptions syncCtx.RunOnCurrentThread(); return t.GetAwaiter().GetResult(); } finally { SynchronizationContext.SetSynchronizationContext(prevCtx); } } /// <summary>Provides a SynchronizationContext that's single-threaded.</summary> private sealed class SingleThreadSynchronizationContext : SynchronizationContext { /// <summary>The queue of work items.</summary> private readonly BlockingCollection<KeyValuePair<SendOrPostCallback, object>> _mQueue = new BlockingCollection<KeyValuePair<SendOrPostCallback, object>>(); /// <summary>The processing thread.</summary> ///private readonly Thread m_thread = Thread.CurrentThread; /// <summary>Dispatches an asynchronous message to the synchronization context.</summary> /// <param name="d">The System.Threading.SendOrPostCallback delegate to call.</param> /// <param name="state">The object passed to the delegate.</param> public override void Post(SendOrPostCallback d, object state) { if (d == null) throw new ArgumentNullException("d"); _mQueue.Add(new KeyValuePair<SendOrPostCallback, object>(d, state)); } /// <summary>Not supported.</summary> public override void Send(SendOrPostCallback d, object state) { throw new NotSupportedException("Synchronously sending is not supported."); } /// <summary>Runs an loop to process all queued work items.</summary> public void RunOnCurrentThread() { foreach (var workItem in _mQueue.GetConsumingEnumerable()) workItem.Key(workItem.Value); } /// <summary>Notifies the context that no more work will arrive.</summary> public void Complete() { _mQueue.CompleteAdding(); } } } }