public void FiberDone(RCRunner runner, long bot, long fiber, RCValue result) { Queue <RCClosure> waiters; RCValue botResult = null; lock (_fiberLock) { if (_fiberWaiters.TryGetValue(fiber, out waiters)) { _fiberWaiters.Remove(fiber); } if (!_fiberResults.ContainsKey(fiber)) { _fiberResults.Add(fiber, result); } else { // I wanted to draw a hard line and ensure this method was // called only once for each fiber, by the fiber or bot operator. // But it has to also be called from within try when there is nothing // to do next. if (!_fiberResults[fiber].Equals(result)) { throw new Exception("Conflicting results for fiber " + fiber); } } if (_fiberResults.Count == _fibers.Count) { botResult = _fiberResults[0L]; } } if (waiters != null) { RCNative exValue = result as RCNative; if (exValue != null && exValue.Value is Exception) { foreach (RCClosure waiter in waiters) { runner.Finish(waiter, (Exception)exValue.Value, 1); } } else { foreach (RCClosure waiter in waiters) { runner.Yield(waiter, result); } } } if (botResult != null) { runner.BotDone(bot, botResult); } }
public static void DoYield(RCRunner runner, RCClosure closure, RCValue result) { if (result == null) { throw new ArgumentNullException("result"); } // Check to see if this fiber was killed before moving on RCBot bot = runner.GetBot(closure.Bot); if (bot.IsFiberDone(closure.Fiber)) { runner.Continue(closure, null); return; } // Do not permit any further changes to result or its children values. result.Lock(); RCClosure next = closure.Code.Next(runner, closure, closure, result); if (next == null) { result = closure.Code.Finish(runner, closure, result); bot.ChangeFiberState(closure.Fiber, "done"); RCSystem.Log.Record(closure, "fiber", closure.Fiber, "done", ""); if (closure.Fiber == 0 && closure.Bot == 0) { runner.Finish(closure, result); } // This will handle fibers that wake up from some suspended state // without realizing that they have been killed. else { bot.FiberDone(runner, closure.Bot, closure.Fiber, result); } // Remove closure from the pending queue. runner.Continue(closure, null); } else { runner.Continue(closure, next); } }
// Kicks off evaluation for a block. public static void DoEval(RCRunner runner, RCClosure closure, RCBlock block) { if (block.Count == 0) { DoYield(runner, closure, block); } else { RCBlock current = block.GetName(closure.Index); if (current.Evaluator.Invoke) { string op = ((RCString)current.Value)[0]; RCSystem.Activator.Invoke(runner, closure, op, closure.Result); } else if (current.Evaluator.Template) { try { RCString result = ExpandTemplate(new StringBuilder(), (RCTemplate)current, closure.Result, 0, ""); runner.Yield(closure, result); } catch (Exception ex) { RCException rcex = new RCException(closure, ex, RCErrors.Native, "An exception was thrown by the template."); runner.Finish(closure, rcex, (int)RCErrors.Native); } } else if (current.Evaluator.Pass) { DoYield(runner, closure, current.Value); } // This means that Value is an operator or a reference. else if (current.Value.ArgumentEval) { current.Value.Eval(runner, new RCClosure(closure, closure.Bot, current.Value, closure.Left, closure.Result, 0)); } else if (current.Evaluator.Return) { DoYield(runner, closure, current.Value); } else { // I need something different to happen when we are at the top level already. // Or maybe I need to inject a wrapper closure when I do Rep this way? if ((closure.Index < block.Count - 1) || (closure.Parent != null)) { DoYield(runner, closure, current.Value); } else { DoYield(runner, closure, current); } } } }
public void EvalFail(RCRunner runner, RCClosure closure, RCString right) { runner.Finish(closure, new RCException(closure, RCErrors.Custom, right[0]), (int)RCErrors.Custom); }