using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Threading; using Object = UnityEngine.Object; namespace UnityAsync { public interface IAwaitInstructionAwaiter { bool Evaluate(); FrameScheduler Scheduler { get; } } /// /// Encapsulates an with additional information about how the instruction /// will be queued and executed. Continuations are intended to be awaited after or shortly after instantiation. /// /// The type of to encapsulate. [StructLayout(LayoutKind.Sequential, Pack = 1)] public struct AwaitInstructionAwaiter : IAwaitInstructionAwaiter, INotifyCompletion where T : IAwaitInstruction { static Exception exception; Object owner; CancellationToken cancellationToken; /// /// Evaluate the encapsulated . If the instruction is finished, the /// continuation delegate is invoked and the method returns true. If the owner is destroyed, the /// method will return true without invoking the continuation delegate. If it was cancelled, an exception is /// is thrown when the continuation delegate is invoked. Otherwise, returns false (i.e. the instruction is not /// finished). /// /// /// true if the is finished, its owner destroyed, /// or has been cancelled, otherwise false. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Evaluate() { try { // if cancelled, throw exception cancellationToken.ThrowIfCancellationRequested(); // if owner is destroyed, behaves like a UnityEngine.Coroutine, ie: // "With this object's death, the thread of prophecy is severed. Restore a saved game to restore the // weave of fate, or persist in the doomed world you have created." if(!owner) return true; // if not completed, return false to put it back into a queue for next frame if(!instruction.IsCompleted()) return false; } catch(Exception e) { // store exception in static field exception = e; // exception is rethrown in GetResult, at start of continuation continuation(); return true; } // if we get here, it completed without exceptions and we can call continuation and be done with it continuation(); return true; } public FrameScheduler Scheduler { get; private set; } T instruction; Action continuation; public AwaitInstructionAwaiter(in T inst) { instruction = inst; continuation = null; owner = AsyncManager.Instance; exception = null; Scheduler = FrameScheduler.Update; } public AwaitInstructionAwaiter(in T inst, FrameScheduler scheduler) { instruction = inst; continuation = null; owner = AsyncManager.Instance; exception = null; Scheduler = scheduler; } public AwaitInstructionAwaiter(in T inst, CancellationToken cancellationToken, FrameScheduler scheduler) { instruction = inst; continuation = null; owner = AsyncManager.Instance; exception = null; this.cancellationToken = cancellationToken; Scheduler = scheduler; } public AwaitInstructionAwaiter(in T inst, Object owner, FrameScheduler scheduler) { instruction = inst; continuation = null; this.owner = owner; exception = null; Scheduler = scheduler; } public bool IsCompleted => false; [MethodImpl(MethodImplOptions.AggressiveInlining)] public void OnCompleted(Action continuation) { this.continuation = continuation; AsyncManager.AddContinuation(this); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void GetResult() { if(exception != null) { var e = exception; exception = null; throw e; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public AwaitInstructionAwaiter ConfigureAwait(Object owner) { this.owner = owner; return this; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public AwaitInstructionAwaiter ConfigureAwait(CancellationToken cancellationToken) { this.cancellationToken = cancellationToken; return this; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public AwaitInstructionAwaiter ConfigureAwait(FrameScheduler scheduler) { Scheduler = scheduler; return this; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public AwaitInstructionAwaiter ConfigureAwait(Object owner, FrameScheduler scheduler) { this.owner = owner; Scheduler = scheduler; return this; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public AwaitInstructionAwaiter ConfigureAwait(CancellationToken cancellationToken, FrameScheduler scheduler) { this.cancellationToken = cancellationToken; Scheduler = scheduler; return this; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public AwaitInstructionAwaiter ConfigureAwait(Object owner, CancellationToken cancellationToken, FrameScheduler scheduler) { this.owner = owner; this.cancellationToken = cancellationToken; Scheduler = scheduler; return this; } } }