using System; using System.Collections.Generic; using System.Linq; using System.Runtime.ExceptionServices; using System.Text; using System.Threading; using System.Threading.Tasks; namespace Amazon.Runtime.Internal.Util { /// /// This is a utility class to be used in last resort for code paths that are synchronous but need to call an asynchronous method. /// This should never be used for methods that are called at high volume as this utility class has a performance cost. For example this /// class was added for the refreshing credentials like Cognito which would need to use this about once an hour. /// /// This code is taken from: http://stackoverflow.com/questions/5095183/how-would-i-run-an-async-taskt-method-synchronously /// which is licensed under Creative Commons Attribution-ShareAlike 3.0 (http://creativecommons.org/licenses/by-sa/3.0/) /// public static class AsyncHelpers { /// /// Execute's an async Task<T> which has a void return value synchronously /// /// Task<T> method to execute public static void RunSync(Func task) { var oldContext = SynchronizationContext.Current; try { 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(); } finally { SynchronizationContext.SetSynchronizationContext(oldContext); } } /// /// Execute's 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; try { 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(); return ret; } finally { SynchronizationContext.SetSynchronizationContext(oldContext); } } private class ExclusiveSynchronizationContext : SynchronizationContext { private bool done; public Exception InnerException { get; set; } readonly AutoResetEvent workItemsWaiting = new AutoResetEvent(false); 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 exeption ExceptionDispatchInfo.Capture(InnerException).Throw(); } else { workItemsWaiting.WaitOne(); } } } public override SynchronizationContext CreateCopy() { return this; } } } }