using System; using System.Collections.Generic; using System.Runtime.CompilerServices; using System.Text; using System.Threading; using System.Threading.Tasks; namespace Capnp.Util { /// /// A task-like object which enforces that all await operations from the same thread leave in the exact order they were issued. /// Note that an ordinary .NET Task does not fulfill this requirement if completed by a thread which is different from the /// awaiting thread. /// public class StrictlyOrderedAwaitTask: INotifyCompletion { class Cover { } class Seal { } static readonly Cover s_cover = new Cover(); static readonly Seal s_seal = new Seal(); readonly Task _awaitedTask; object? _state; /// /// Constructs an instance /// /// Task on which the order shall be enforced public StrictlyOrderedAwaitTask(Task awaitedTask) { _awaitedTask = awaitedTask; _state = s_cover; } /// /// await pattern implementation /// /// An object suitable for the await pattern public StrictlyOrderedAwaitTask GetAwaiter() { return this; } async void AwaitInternal() { try { await _awaitedTask; } catch { } finally { SpinWait.SpinUntil(() => { Action? continuations; do { continuations = (Action?)Interlocked.Exchange(ref _state, null); continuations?.Invoke(); } while (continuations != null); return Interlocked.CompareExchange(ref _state, s_seal, null) == null; }); } } /// /// Part of await pattern implementation. Do not use directly. /// public void OnCompleted(Action continuation) { bool first = false; SpinWait.SpinUntil(() => { object? cur, next; cur = Volatile.Read(ref _state); first = false; switch (cur) { case Cover cover: next = continuation; first = true; break; case null: next = continuation; break; case Action action: next = action + continuation; break; default: continuation(); return true; } return Interlocked.CompareExchange(ref _state, next, cur) == cur; }); if (first) { AwaitInternal(); } } /// /// Whether the underlying task did complete and it is safe to skip continuation registration. /// public bool IsCompleted => _awaitedTask.IsCompleted && _state == s_seal; /// /// Part of await pattern implementation. Do not use directly. /// public void GetResult() => _awaitedTask.GetAwaiter().GetResult(); /// /// Task on which the order shall be enforced. /// public Task WrappedTask => _awaitedTask; } /// /// A task-like object which enforces that all await operations from the same thread leave in the exact order they were issued. /// Note that an ordinary .NET Task does not fulfill this requirement if completed by a thread which is different from the /// awaiting thread. /// public class StrictlyOrderedAwaitTask : StrictlyOrderedAwaitTask { /// /// Constructs an instance /// /// Task on which the order shall be enforced public StrictlyOrderedAwaitTask(Task awaitedTask): base(awaitedTask) { } /// /// Task on which the order shall be enforced. /// public new Task WrappedTask => (Task)base.WrappedTask; /// /// await pattern implementation /// /// An object suitable for the await pattern public new StrictlyOrderedAwaitTask GetAwaiter() => this; /// /// Part of await pattern implementation. Do not use directly. /// public new T GetResult() => WrappedTask.GetAwaiter().GetResult(); /// /// Redirects to the wrapped Task's result. /// public T Result => WrappedTask.Result; } /// /// Extension methods to simplify the use of /// public static class StrictlyOrderedTaskExtensions { /// /// Converts the task to a task-like object which enforces that all await operations from the same thread leave in the exact order they were issued. /// /// The type of the result produced by the Task /// Task to wrap /// awaitable object public static StrictlyOrderedAwaitTask EnforceAwaitOrder(this Task task) { return new StrictlyOrderedAwaitTask(task); } /// /// Converts the task to a task-like object which enforces that all await operations from the same thread leave in the exact order they were issued. /// /// Task to wrap /// awaitable object public static StrictlyOrderedAwaitTask EnforceAwaitOrder(this Task task) { return new StrictlyOrderedAwaitTask(task); } } }