private void Finish(RoutineException error) { this.error = error; Stop(); if (parent != null && !parent.isStepping) { parent.Step(); } }
public void Reset() { Stop(); enumerator = null; parent = null; returnValue = null; error = null; context = null; exceptionHandler = null; }
private void Step() { Assert.IsTrue(!isStepping); isStepping = true; //Used to detect that routine was killed during step var steppingId = id; //Running IResumable if (enumerator is IResumable) { var resumer = (resumerPool.Count > 0) ? resumerPool.Pop() : new Resumer(); resumer.called = false; resumer.routine = this; resumer.steppingId = steppingId; var resumable = enumerator as IResumable; resumable.Run(resumer); //In case of suicide or immediate finish if (steppingId != id) { return; } } //Running enumerator else { int itCount; for (itCount = 0; itCount < Routine.maxIterations; ++itCount) { currentReturnValue = null; currentAllReturnValues.Clear(); //Bail out if any children are yielding: //Any array: stop if all children are yielding if (finishOnAny) { var childIsDone = true; for (int i = 0, l = children.Count; i < l; ++i) { var child = children[i]; if (child.error != null) { Finish(child.error); return; } childIsDone = child.IsDone; if (childIsDone) { currentReturnValue = child.returnValue; break; } } if (!childIsDone) { break; } } //Single or all-array: stop if any children are yielding else { var childIsYielding = false; for (int i = 0, l = children.Count; i < l; ++i) { var child = children[i]; if (child.error != null) { Finish(child.error); return; } childIsYielding = !child.IsDone; if (childIsYielding) { break; } } if (childIsYielding) { break; } //Collect return values from children if (arrayWasYielded) { for (int i = 0, l = children.Count; i < l; ++i) { currentAllReturnValues.Add(children[i].returnValue); } currentReturnValue = currentAllReturnValues; } else if (children.Count > 0) { Assert.IsTrue(children.Count == 1); currentReturnValue = children[0].returnValue; } } //Stop and clear children ClearChildren(); //Step this routine steppingStack.Push(this); bool done; RoutineException stepError = null; try { done = !enumerator.MoveNext(); } catch (System.Exception e) { stepError = CreateException(e.Message, e); exceptionHandler(stepError, context); done = true; } steppingStack.Pop(); //Check for suicide if (steppingId != id) { return; } //Prevent GetResult() from giving back something when it shouldn't currentReturnValue = null; currentAllReturnValues.Clear(); //Routine finished? if (done) { Finish(stepError); return; } arrayWasYielded = false; finishOnAny = false; //Check for yielded array var result = enumerator.Current; if (result is WrappedArray) { var wrappedArray = result as WrappedArray; var arr = wrappedArray.yieldables; finishOnAny = wrappedArray.isAny; wrappedArrayPool.Push(wrappedArray); if (arr == null) { Finish(CreateException("yieldables not set in WrappedArray")); return; } arrayWasYielded = true; //Copy array contents in case one of contained routines messes with it when stepped Assert.IsTrue(yieldedArray.Count == 0); foreach (var element in arr) { yieldedArray.Add(element); } for (int i = 0, l = yieldedArray.Count; i < l; ++i) { var yieldedValue = yieldedArray[i]; if (yieldedValue is IEnumerable) { yieldedValue = (yieldedValue as IEnumerable).GetEnumerator(); } else if (!(yieldedValue is IEnumerator)) { Finish(CreateException(string.Format("yielded value [{0}] is not an IEnumerator: {1}", i, yieldedValue))); return; } var child = CreateChild(yieldedValue as IEnumerator); //Check for parenticide if (steppingId != id) { return; } //Exit if any child completes in any-array mode if (finishOnAny && child.IsDone) { break; } } yieldedArray.Clear(); } //Single runable yielded: create child routine else if (result is IEnumerable) //Check for IEnumerable before IEnumerator, as the object could be both (Linq) and we want to treat it as the former if so { CreateChild((result as IEnumerable).GetEnumerator()); //Check for parenticide if (steppingId != id) { return; } } else if (result is IEnumerator) { CreateChild(result as IEnumerator); //Check for parenticide if (steppingId != id) { return; } } //Something not-runable was returned else { throw CreateException(string.Format("yielded value is not an IEnumerator or IEnumerable: {0}", result)); } } if (itCount == maxIterations) { throw CreateException("Infinite loop in routine!"); } } isStepping = false; }