public void Report(RCClosure closure, Exception ex) { RCBot bot = GetBot(closure.Bot); bot.ChangeFiberState(closure.Fiber, "reported"); RCSystem.Log.Record(closure, "fiber", closure.Fiber, "reported", ex); ++_exceptionCount; }
protected RCValue Run(RCClosure root, bool restoreStateOnError) { lock (_queueLock) { if (_root == null) { _root = root; RCBot rootBot = GetBot(_root.Bot); // keeping this inside the lock because it should happen before the call to // Enqueue. // But only during the very first call to run for this runner. // Log.Record (this, root, root.BotId, "bot", root.BotId, "start", root.Code); rootBot.ChangeFiberState(root.Fiber, "start"); RCSystem.Log.Record(root, "fiber", root.Fiber, "start", root.Code); } _queue.Enqueue(root); } // Trigger a worker (don't care which) to take it. _wait.Set(); // Wait for the work to be completed. _done.WaitOne(); // If an exception was thrown, rethrow it on this thread. if (_exception != null) { Exception exception = _exception; _exception = null; if (restoreStateOnError) { // Make the successfully computed values into the effective state of the // environment RCClosure top = _exceptionClosure; RCClosure underTop = null; while (top.Parent != null && top.Parent.Parent != null) { underTop = top; top = top.Parent; } if (underTop != null && top.Code.IsOperator) { top = underTop; } _state = RCBlock.Append(_state, top.Result); } _runnerUnhandled = true; throw exception; } // The final result is assigned by the worker in Finish (). RCValue result = _result; _result = null; return(result); }
protected long DoFiber(RCRunner runner, RCClosure closure, RCValue code) { long fiber = Interlocked.Increment(ref _fiber); RCBot bot = runner.GetBot(closure.Bot); RCClosure next = FiberClosure(bot, fiber, closure, code); bot.ChangeFiberState(fiber, "start"); RCSystem.Log.Record(closure, "fiber", fiber, "start", ""); // This creates a separate stream of execution (fiber) from the // one that called this method. // When it is done it will naturally try to return its result // back to the original calling fiber. But the Next method on the // fiber operator will see this happening and call FiberDone instead. runner.Continue(null, next); return(fiber); }
// Previous is the closure that will be removed from the pending set. // Next is the closure that will be added to the queue. // This is done in an atomic fashion so that all fibers will be // represented in _pending or _queue at all times. // previous will be null in cases where Continue is used to retry or fork streams of // execution. // next will be null in cases where the executing fiber is finished and all // that remains is to remove it from _pending. public void Continue(RCClosure previous, RCClosure next) { bool live = false; lock (_queueLock) { if (previous != null) { Dictionary <long, RCClosure> pending = null; if (_pending.TryGetValue(previous.Bot, out pending)) { RCClosure c = null; if (pending.TryGetValue(previous.Fiber, out c)) { pending.Remove(previous.Fiber); if (pending.Count == 0) { _pending.Remove(previous.Bot); } live = true; } } } else { live = true; } if (live) { if (next != null) { _queue.Enqueue(next); _wait.Set(); } } else { // This will internally take the _botLock. // This should be ok but given that it is just a log write // I would like to move this outside. RCBot bot = GetBot(previous.Bot); bot.ChangeFiberState(previous.Fiber, "dead"); RCSystem.Log.Record(previous, "fiber", previous.Fiber, "dead", ""); } } }
public long Bot(RCClosure closure, RCBlock right) { RCClosure next; long id; RCBot bot; lock (_botLock) { id = _bot++; bot = new RCBot(this, id); _output[id] = new Queue <RCAsyncState> (); _bots[id] = bot; next = Fiber.FiberClosure(bot, 0, closure, right); } bot.ChangeFiberState(0, "start"); RCSystem.Log.Record(next, "fiber", 0, "start", ""); Continue(null, next); return(id); }
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); } }
public void Start(RCValue program) { if (program == null) { throw new Exception("program may not be null"); } RCClosure root = null; lock (_queueLock) { if (_root != null) { throw new Exception("Runner has already started."); } RCBot rootBot = _bots[0]; root = new RCClosure(rootBot.Id, program); rootBot.ChangeFiberState(root.Fiber, "start"); RCSystem.Log.Record(root, "fiber", root.Fiber, "start", root.Code); _root = root; _queue.Enqueue(root); } _wait.Set(); }
public void Finish(RCClosure closure, Exception exception, long status) { RCValue result = null; RCClosure parent = closure; RCBot bot = GetBot(closure.Bot); while (parent != null && parent.Bot == closure.Bot && parent.Fiber == closure.Fiber) { if (parent.InCodeEval) { RCClosure next = parent.Code.Handle(this, parent, exception, status, out result); if (result != null && next == null) { string state = status == 1 ? "caught" : "killed"; bot.ChangeFiberState(closure.Fiber, state); RCSystem.Log.Record(closure, "fiber", closure.Fiber, state, exception); if (closure.Fiber == 0 && closure.Bot == 0) { Finish(closure, result); } else { bot.FiberDone(this, closure.Bot, closure.Fiber, result); } return; } else { Continue(null, next); } if (result != null) { bot.ChangeFiberState(closure.Fiber, "caught"); RCSystem.Log.Record(closure, "fiber", closure.Fiber, "caught", exception); ++_exceptionCount; return; } } parent = parent.Parent; } // This means it was not handled in the while loop. if (result == null) { string state = status == 1 ? "failed" : "killed"; bot.ChangeFiberState(closure.Fiber, state); SafeLogRecord(closure, "fiber", state, exception); ++_exceptionCount; if (closure.Fiber == 0 && closure.Bot == 0) { _exception = exception; _exceptionClosure = closure; _done.Set(); } else { // I think this is sort of mostly the correct think to do. // We need to record the fact that the fiber finished. // But stuffing an exception inside a Native to do so seems wrong. // Need more work on controlling the lifecycle of fibers. // Also I want to get rid of RCNative I think this is the only place // where I still need it. bot.FiberDone(this, closure.Bot, closure.Fiber, new RCNative(exception)); } } }