139 lines
3.2 KiB
C#
139 lines
3.2 KiB
C#
// Source: https://social.msdn.microsoft.com/Forums/en-US/163ef755-ff7b-4ea5-b226-bbe8ef5f4796/is-there-a-pattern-for-calling-an-async-method-synchronously?forum=async
|
|
|
|
using System;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using System.Collections.Generic;
|
|
|
|
namespace UnityGLTF
|
|
{
|
|
public static class AsyncHelpers
|
|
{
|
|
/// <summary>
|
|
/// Execute's an async Task<T> method which has a void return value synchronously
|
|
/// </summary>
|
|
/// <param name="task">Task<T> method to execute</param>
|
|
public static void RunSync(Func<Task> 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);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Execute's an async Task<T> method which has a T return type synchronously
|
|
/// </summary>
|
|
/// <typeparam name="T">Return Type</typeparam>
|
|
/// <param name="task">Task<T> method to execute</param>
|
|
/// <returns></returns>
|
|
public static T RunSync<T>(Func<Task<T>> 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;
|
|
public Exception InnerException { get; set; }
|
|
readonly AutoResetEvent workItemsWaiting = new AutoResetEvent(false);
|
|
|
|
readonly Queue<Tuple<SendOrPostCallback, object>> items =
|
|
new Queue<Tuple<SendOrPostCallback, object>>();
|
|
|
|
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<SendOrPostCallback, object> 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
|
|
{
|
|
throw new AggregateException("AsyncHelpers.Run method threw an exception.", InnerException);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
workItemsWaiting.WaitOne();
|
|
}
|
|
}
|
|
}
|
|
|
|
public override SynchronizationContext CreateCopy()
|
|
{
|
|
return this;
|
|
}
|
|
}
|
|
}
|
|
}
|