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();
			}
		}
	}
}