using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; namespace KsWare.Threading { public static class AsyncHelper { public static Task RunAsync(Func func) => Task.Run(() => func()); public static Task RunAsync(Func func, T1 p1) => Task.Run(() => func(p1)); public static Task RunAsync(Func func, T1 p1, T2 p2) => Task.Run(() => func(p1, p2)); /// /// Executes an async Task{T} method which has a void return value synchronously /// /// Task{T} method to execute public static void RunSync(Func task) { var oldContext = SynchronizationContext.Current; var synch = new ExclusiveSynchronizationContext(); SynchronizationContext.SetSynchronizationContext(synch); synch.Post(async _ => { try { await task(); } catch (Exception e) { synch.InnerException = e; throw; } finally { synch.EndMessageLoop(); } }, null); synch.BeginMessageLoop(); SynchronizationContext.SetSynchronizationContext(oldContext); } /// /// Executes an async Task{T} method which has a T return type synchronously /// /// Return Type /// Task{T} method to execute /// public static T RunSync(Func> task) { var oldContext = SynchronizationContext.Current; var synch = new ExclusiveSynchronizationContext(); SynchronizationContext.SetSynchronizationContext(synch); T ret = default(T); synch.Post(async _ => { try { ret = await task(); } catch (Exception e) { synch.InnerException = e; throw; } finally { synch.EndMessageLoop(); } }, null); synch.BeginMessageLoop(); SynchronizationContext.SetSynchronizationContext(oldContext); return ret; } private class ExclusiveSynchronizationContext : SynchronizationContext { private bool _done; private readonly AutoResetEvent _workItemsWaiting = new AutoResetEvent(false); public Exception InnerException { get; set; } readonly Queue> items = new Queue>(); public override void Send(SendOrPostCallback d, object state) { throw new NotSupportedException("We cannot send to our same thread"); } public override void Post(SendOrPostCallback d, object state) { lock (items) { items.Enqueue(Tuple.Create(d, state)); } _workItemsWaiting.Set(); } public void EndMessageLoop() { Post(_ => _done = true, null); } public void BeginMessageLoop() { while (!_done) { Tuple task = null; lock (items) { if (items.Count > 0) { task = items.Dequeue(); } } if (task != null) { task.Item1(task.Item2); if (InnerException != null) // the method threw an exception { throw new AggregateException("AsyncHelper.Run method threw an exception.", InnerException); } } else { _workItemsWaiting.WaitOne(); } } } public override SynchronizationContext CreateCopy() { return this; } } } }