// Seems like a kind of weird way to do this piece. public void InjectState(RCBot bot) { foreach (Type type in _modules) { bot.PutModule(type); } }
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); }
public RCRunner(long workers) { _ctorThread = Thread.CurrentThread; _bots[0] = new RCBot(this, 0); _output[0] = new Queue <RCAsyncState> (); _parser = new RCLParser(RCSystem.Activator); Console.CancelKeyPress += HandleConsoleCancelKeyPress; for (int i = 0; i < workers; ++i) { Thread worker = new Thread(Work); worker.IsBackground = true; _workers.Write(worker); worker.Start(); } }
public static RCClosure FiberClosure(RCBot bot, long fiber, RCClosure closure, RCValue code) { // First create a clone of the parent closure for the current operator, // with the new fiber operator on it. This ensures that that when tail // calls are eliminated the next closure will have the correct child fiber // number, not the fiber number of the parent. // Update July 12, 2014. // This additional clone of the parent closure no longer seems necessary for tail // calls. // In addition it was found to cause certain fibers to give the wrong result on // completion. // TestTryWaitKill5 is the repro for this. /* * RCClosure parent = null; * if (closure.Parent != null) * { * parent = new RCClosure ( * //bot, * //fiber, * closure.Parent.Bot, * closure.Parent.Fiber, * closure.Parent.Locks, * closure.Parent.Parent, * closure.Parent.Code, * closure.Parent.Left, * closure.Parent.Result, * closure.Parent.Index); * } */ RCClosure clone = new RCClosure(bot.Id, fiber, closure.Locks, closure.Parent, closure.Code, closure.Left, closure.Result, closure.Index, closure.UserOp, closure.UserOpContext, noClimb: false, noResolve: false); RCClosure next = new RCClosure(clone, bot.Id, code, clone.Left, RCBlock.Empty, 0); return(next); }
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 Reset() { // Should I take the locks here? One or both? Both. lock (_queueLock) { lock (_botLock) { _parser = new RCLParser(RCSystem.Activator); _root = null; _result = null; _exception = null; _exceptionClosure = null; _exceptionCount = 0; _queue = new Queue <RCClosure> (); _pending = new Dictionary <long, Dictionary <long, RCClosure> > (); _bot = 1; _bots = new Dictionary <long, RCBot> (); _bots[0] = new RCBot(this, 0); _output[0] = new Queue <RCAsyncState> (); ++_reset; } } }
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 Invoke(RCRunner runner, RCClosure closure, string name, object left, object right) { OverloadValue overload; Type ltype = left.GetType(); Type rtype = right.GetType(); Type ctype = right.GetType(); try { if (_dispatch.TryGetValue(new OverloadKey(name, ctype, ltype, rtype), out overload)) { RCBot bot = runner.GetBot(closure.Bot); object state = bot.GetModule(overload.Module); overload.Implementation.Invoke(state, new object[] { runner, closure, left, right }); } else if (_dispatch.TryGetValue(new OverloadKey(name, typeof(object), ltype, rtype), out overload)) { RCBot bot = runner.GetBot(closure.Bot); object state = bot.GetModule(overload.Module); overload.Implementation.Invoke(state, new object[] { runner, closure, left, right }); } else if (_dispatch.TryGetValue(new OverloadKey(name, typeof(object), typeof(object), rtype), out overload)) { RCBot bot = runner.GetBot(closure.Bot); object state = bot.GetModule(overload.Module); overload.Implementation.Invoke(state, new object[] { runner, closure, left, right }); } else if (_dispatch.TryGetValue(new OverloadKey(name, typeof(object), ltype, typeof(object)), out overload)) { RCBot bot = runner.GetBot(closure.Bot); object state = bot.GetModule(overload.Module); overload.Implementation.Invoke(state, new object[] { runner, closure, left, right }); } else if (_dispatch.TryGetValue(new OverloadKey(name, typeof(object), typeof(object), typeof(object)), out overload)) { RCBot bot = runner.GetBot(closure.Bot); object state = bot.GetModule(overload.Module); overload.Implementation.Invoke(state, new object[] { runner, closure, left, right }); } else if (_dispatch.TryGetValue(new OverloadKey(name, typeof(object), ltype, typeof(RCOperator)), out overload)) { RCBot bot = runner.GetBot(closure.Bot); object state = bot.GetModule(overload.Module); overload.Implementation.Invoke(state, new object[] { runner, closure, left, right }); } else { throw RCException.Overload(closure, name, left, right); } } catch (TargetInvocationException tiex) { Exception ex = tiex.GetBaseException(); if (ex is RCException) { throw ex; } else { // You have to pass the tiex, not ex here so that the interior stack trace will // be // preserved when/if it is rethrown. throw new RCException(closure, tiex, RCErrors.Native, ThrowMessage(name, ex)); } } }
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)); } } }