Skip to content

Instantly share code, notes, and snippets.

@SchreinerK
Created September 29, 2021 08:27
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save SchreinerK/e05633ef87fb768c6131f3714bae0f45 to your computer and use it in GitHub Desktop.
Save SchreinerK/e05633ef87fb768c6131f3714bae0f45 to your computer and use it in GitHub Desktop.
KsWare.Threading.AsyncHelper
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace KsWare.Threading {
public static class AsyncHelper {
public static Task<TResult> RunAsync<TResult>(Func<TResult> func) => Task.Run<TResult>(() => func());
public static Task<TResult> RunAsync<TResult, T1>(Func<T1, TResult> func, T1 p1) =>
Task.Run<TResult>(() => func(p1));
public static Task<TResult> RunAsync<TResult, T1, T2>(Func<T1, T2, TResult> func, T1 p1, T2 p2) =>
Task.Run<TResult>(() => func(p1, p2));
/// <summary>
/// Executes 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>
/// Executes 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;
private readonly AutoResetEvent _workItemsWaiting = new AutoResetEvent(false);
public Exception InnerException { get; set; }
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 exception
{
throw new AggregateException("AsyncHelper.Run method threw an exception.", InnerException);
}
}
else {
_workItemsWaiting.WaitOne();
}
}
}
public override SynchronizationContext CreateCopy() {
return this;
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment