private AdvanceAction AdvanceCoroutine(CoroutineState executingCoroutine) { var coroutine = executingCoroutine.Coroutine; while (true) { // Execute the coroutine's next frame bool isCompleted; // We need to lock to ensure cancellation from source does not interfere with frame lock (coroutine.SyncRoot) { // Cancellation can come from outside, as well as completion if (coroutine.IsComplete) { return(AdvanceAction.Complete); } try { isCompleted = !executingCoroutine.Enumerator.MoveNext(); } catch (Exception ex) { coroutine.SignalException(ex); return(AdvanceAction.Complete); } if (isCompleted) { coroutine.SignalComplete(false, null); return(AdvanceAction.Complete); } } IWaitObject newWait = executingCoroutine.Enumerator.Current; executingCoroutine.WaitForObject = newWait; // Special case null means wait to next frame if (newWait is WaitForSeconds waitForSeconds) { // The wait object is set to null, we bypass it and use trigger instead executingCoroutine.WaitForObject = null; timerTrigger.AddTrigger(waitForSeconds.WaitTime, executingCoroutine); return(AdvanceAction.MoveToWaitForTrigger); } if (newWait == null) { return(AdvanceAction.Keep); } else if (newWait is ReturnValue retVal) { coroutine.SignalComplete(true, retVal.Result); return(AdvanceAction.Complete); } if (newWait is Coroutine newWaitCoroutine) { // If we yield an unstarted coroutine, we add it to this scheduler! if (newWaitCoroutine.Status == CoroutineStatus.WaitingForStart) { StartSubCoroutine(newWaitCoroutine, coroutine); switch (newWaitCoroutine.Status) { case CoroutineStatus.CompletedWithException: coroutine.SignalException(newWaitCoroutine.Exception); return(AdvanceAction.Complete); case CoroutineStatus.Cancelled: coroutine.SignalException(new OperationCanceledException("Internal coroutine was cancelled")); return(AdvanceAction.Complete); } } } if (newWait.IsComplete) { // If the wait object is complete, we continue immediatelly (yield does not split frames) continue; } // Check if we get notified for completion, otherwise polling is used if (newWait is IWaitObjectWithNotifyCompletion withCompletion) { withCompletion.RegisterCompleteSignal( () => { trigerredCoroutines.Enqueue(executingCoroutine); }); return(AdvanceAction.MoveToWaitForTrigger); } return(AdvanceAction.Keep); } }
private bool AdvanceCoroutine(long coroutineID, Coroutine coroutine, IEnumerator <IWaitObject> iterator) { while (true) { // Execute the coroutine's next frame bool isCompleted; // We need to lock to ensure cancellation from source does not interfere with frame lock (coroutine.SyncRoot) { // Cancellation can come from outside, as well as completion if (coroutine.IsComplete) { return(true); } try { isCompleted = !iterator.MoveNext(); } catch (Exception ex) { coroutine.SignalException(ex); return(true); } if (isCompleted) { coroutine.SignalComplete(false, null); return(true); } } IWaitObject newWait = iterator.Current; // Special case null means wait to next frame if (newWait is WaitForSeconds waitForSeconds) { timerTrigger.AddTrigger(waitForSeconds.WaitTime, new ContinueCoroutineEvent(coroutineID)); return(false); } // Special case null means wait to next frame if (newWait == null) { eventQueue.EnqueueNextFrame(new ContinueCoroutineEvent(coroutineID)); return(false); } else if (newWait is ReturnValue retVal) { coroutine.SignalComplete(true, retVal.Result); return(true); } if (newWait is Coroutine newWaitCoroutine) { // If we yield an unstarted coroutine, we add it to this scheduler! if (newWaitCoroutine.Status == CoroutineStatus.WaitingForStart) { coroutine.SignalStarted(this, executionState, coroutine); StartAndMakeFirstIteration(newWaitCoroutine); switch (newWaitCoroutine.Status) { case CoroutineStatus.CompletedWithException: coroutine.SignalException(newWaitCoroutine.Exception); return(true); case CoroutineStatus.Cancelled: coroutine.SignalException(new OperationCanceledException("Internal coroutine was cancelled")); return(true); } } } if (newWait.IsComplete) { // If the wait object is complete, we continue immediatelly (yield does not split frames) continue; } // Check if we get notified for completion, otherwise polling is used if (newWait is IWaitObjectWithNotifyCompletion withCompletion) { withCompletion.RegisterCompleteSignal( () => { eventQueue.Enqueue(new ContinueCoroutineEvent(coroutineID)); }); } return(false); } }