using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; namespace System.Extensions; public static class TaskExtensions { /// /// Mark task as . /// /// Result type. /// Task. /// The started public static Task LongRunning(this Task task) { return Task.Factory.StartNew(async () => { return await task; }, TaskCreationOptions.LongRunning).Unwrap(); } /// /// Mark task as . /// /// Task. /// The started public static Task LongRunning(this Task task) { return Task.Factory.StartNew(async () => { await task; }, TaskCreationOptions.LongRunning).Unwrap(); } /// /// Execute action periodically and asynchronously. /// /// Action to be executed. /// Time to pass before starting to execute. /// Interval between procs. /// Cancellation token used to cancel the cycle. /// public static async Task RunPeriodicAsync(this Action onTick, TimeSpan dueTime, TimeSpan interval, CancellationToken token) { if (dueTime > TimeSpan.Zero) { await Task.Delay(dueTime, token).ConfigureAwait(false); } while (!token.IsCancellationRequested) { onTick?.Invoke(); if (interval > TimeSpan.Zero) { await Task.Delay(interval, token).ConfigureAwait(false); } } } /// /// Execute's an async Task method which has a void return value synchronously /// /// Task method to execute public static void RunSync(this 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(); } }, default!); synch.BeginMessageLoop(); SynchronizationContext.SetSynchronizationContext(oldContext); } /// /// Execute's an async Task method which has a T return type synchronously /// /// Return Type /// Task method to execute /// public static T RunSync(this Func> task) { var oldContext = SynchronizationContext.Current; var synch = new ExclusiveSynchronizationContext(); SynchronizationContext.SetSynchronizationContext(synch); var ret = default(T); synch.Post(async _ => { try { ret = await task(); } catch (Exception e) { synch.InnerException = e; throw; } finally { synch.EndMessageLoop(); } }, default!); 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> 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 (this.items) { this.items.Enqueue(Tuple.Create(d, state)); } this.workItemsWaiting.Set(); } public void EndMessageLoop() { this.Post(_ => this.done = true, default!); } public void BeginMessageLoop() { while (!this.done) { Tuple task = default!; lock (this.items) { if (this.items.Count > 0) { task = this.items.Dequeue(); } } if (task != null) { task.Item1(task.Item2); if (this.InnerException != null) // the method threw an exeption { throw new AggregateException("AsyncHelpers.Run method threw an exception.", this.InnerException); } } else { this.workItemsWaiting.WaitOne(); } } } public override SynchronizationContext CreateCopy() { return this; } } }