def _hpumpChild(self, mtype, spname, msg): #print "** mswitch._hpumpChild: mtype(%s) spname(%s)" % (mtype, spname) if mtype=="_ready": #print "mswitch._hpumpChild: sending `_ready` on local Bus" Bus.publish(self, "_ready") return if mtype=="_sub": try: msgtype=msg.pop(0) except: raise RuntimeError("missing `msgType` from `_sub` message") self._addSub(self.MAIN_PNAME, msgtype) #print "*** mswitch_hpumpChild: child(%s) subscribing to msgtype(%s)" % (self._pname, msgtype) return ## All other "system" messages are ignored if mtype.startswith("_"): return ## Finally, publish whatever message we receive ## from the Main process: we should have subscribed ## to these anyhow (unless of course there is a bug ;-) #print "mswitch._hpumpChild: mtype(%s) msg(%s)" % (mtype, msg) Bus.publish(self, mtype, *tuple(msg))
def _handleMsg(self, mtype, msg): """Performs message dispatching """ ## If we are a Child instance and we receive ## a message, that means we have already subscribed ## to be a recipient of this message type. Just ## pass it on the local Message Bus then. if self._child: Bus.publish(self, mtype, msg) return True #Bus.publish(self, "log", "_handleMsg(%s)" % mtype) subs=self._map.get(mtype, []) if not subs: self._log("no subscribers for type(%s)" % mtype) return True for proc_name in subs: q=self._qmap.get(proc_name, None) if q is None: self._log("error", "missing 'queue' object for proc(%s)" % proc_name) continue q.put(msg) #self._log("queued, type(%s) for proc(%s)" % (mtype, proc_name)) return True
def _hpump(self): """ Pulls message(s) (if any) from the message queue Must be called periodically - it serves as "event loop" for the whole process (normally) A Child process: - "_sub" : locally subscribe the Main process to msgType This will send all local messages of msgType up to the Main process where it can be further distributed (multicasted) - "_ready" : just pass along to Message Bus The Main process: - "_sub" : - subscribe locally from Message Bus - send down to all Children in "split-horizon" - "_started" : just pass along on Message Bus """ #print "mswitch._hpump: (%s) msg: %s" % (self._pname, "begin") Bus.publish(self, "%beat?") msg = self._getMsg() while (msg is not None) and (not self._term): self._processMsg(msg) msg=self._getMsgNoWait() Bus.publish(self, "%beat?")
def _qbeat(self): if self._beat: if self._child: Bus.publish(self, "%beat", self.pname) else: Bus.publish(self, "beat", self.pname) self._beat = False ## atomic assignment
def run(self): while not self._exitFlag: try: Bus.publish(None, "mswitch_pump") except Exception,e: print "*** (MAIN) Comm Exception!" Bus.publish(None, "log", "*** (MAIN) Comm exception: %s" % e) break
def _hstarted(self, pname): """ Generates `ready` message when all the Child processes report as being started """ if self.is_child: return self._started.extend([pname]) if len(self._started) == len(self._procs): Bus.publish(self, "_ready") self._ready=True
def _hbeat(self, *p): """ Heart-beat from Main process """ self._beat_count=0 if self._termSent: return if self._term: self._termSent=True Bus.publish(self, "%log", "warning", "SIGTERM received, proc(%s)" % self.pname) Bus.publish(self, "_sigterm", self.pname)
def _sendToSubscribers(self, mtype, spname, msgTail): msg=[mtype, spname] msg.extend(msgTail) subs=self._subs.get(mtype, []) #print "mswitch._sendToSubscribers: mtype(%s) subs: %s " % (mtype, subs) for pname in subs: if pname==spname:# or pname==self.MAIN_PNAME: continue if pname==self.MAIN_PNAME: Bus.publish(self, mtype, *tuple(msgTail)) else: #print "mswitch._sendToSubscribers: mtype(%s) pname(%s) msg(%s)" % (mtype, pname, msg) q=self._getQueue(pname) q.put(msg)
def _hstart(self): """ Handler for "start" message marking the debut of all registered processes """ for proc_name in self._procs: procDetails=self._procs[proc_name] try: proc=procDetails["proc"] except: raise RuntimeError("procDetails require a `proc` entry") try: Bus.publish(self, "%log", "< starting process(%s)" % procDetails["name"]) proc.start() Bus.publish(self, "%log", "> started process(%s)" % procDetails["name"]) except Exception,e: raise RuntimeError("Exception whilst starting process (%s)" % e)
def _hxbridge(self, mtype): """ Configures the Switch for bridging the specified message type This configuration will enable this instance to listen on the local Message Bus for the specified message-type and "bridge" any much message toward the "subscribers". """ def makeThunk(mtype): def _thunk(*p): return self._hmbus(mtype, *p) return _thunk ## We need a `thunk` to hold the message-type ## as when the message is delivered it won't ## carry this essential information Bus.subscribe(mtype, makeThunk(mtype))
def _hpumpMain(self, mtype, spname, msg): #print "@@ mswitch._hpumpMain: mtype(%s) spname(%s)" % (mtype, spname) if mtype=="_started": Bus.publish(self, "started", spname) return if mtype=="_sub": try: msgtype=msg.pop(0) except: raise RuntimeError("missing `msgType` from `_sub` message") self._addSub(spname, msgtype) #print "*** mswitch._hpumpMain: subscribing to msgtype: ", msgtype ## repeat source message self._sendSplitHorizon(mtype, spname, [msgtype]) return self._sendToSubscribers(mtype, spname, msg)
def doRun(self): print "TestProcRx.doRun (%s) pid(%s)" % (self.name, os.getpid()) Bus.publish(self, "log", "doRun: starting (%s) pid(%s)" % (self.name, os.getpid())) while not self.is_SigTerm() and not self.is_bark() and not self.is_shutdown(): try: Bus.publish(self, "mswitch_pump") except Exception,e: Bus.publish(self, "log", "*** (%s) Comm Exception: %s" % (self.name, e)) Bus.publish(self, "shutdown") print "getMsg, exception: ", e break sleep(0.01)
def run(self): """ Called by the multiprocessing module once the process is ready to run i.e. after the "fork" We intercept this call in order to make the final preparation before handing the control to the user process """ ## For now, there isn't that much to do ## except to reset the process level Message Bus ## to a known start state Bus.reset() Bus.subscribe("_ready", self._hready) ## Announce to the Agents we are starting and, incidentally, ## that "we" are a "Child" process Bus.publish(self, "proc_starting", (self.name, self.pqueue)) Bus.subscribe("beat", self._hbeat) Bus.subscribe("shutdown", self._hshutdown) return self.doRun()
def _hlbeat(self, *p): """ Process-local Heart-Beat """ if self._barked: return self._beat_count=self._beat_count+1 if self._beat_count >= self.MISSING_BEATS: Bus.publish(self, "%log", "warning", "watchdog expired, proc(%s)" % self.pname) Bus.publish(self, "%bark", self.pname) self._barked=True Bus.publish(self, "alive", self.pname)
def __init__(self, name): Process.__init__(self) self.pqueue=Queue() self.name=name self.term=False self.bark=False self._shutdown=False Bus.subscribe("%bark", self._hbark) Bus.subscribe("_sigterm", self._hsigterm) ## Publish the proc's details over the local message bus ## The ProcessManager will need those details in order to ## launch the process later on. Bus.publish(self, "proc", {"proc":self, "name":name, "queue":self.pqueue})
Bus.publish(self, "_sigterm", self.pname) def _hproc_starting(self, (pname, _)): self._beat_count=0 self.is_child=True self.pname=pname signal.signal(signal.SIGTERM, self._sterm) ### Only subscribe to the signals ### when we know we are a Child process Bus.subscribe("beat", _cwd._hbeat) Bus.subscribe("%beat", _cwd._hlbeat) _cwd=Child_WatchDogAgent() Bus.subscribe("proc_starting", _cwd._hproc_starting) class Main_WatchDogAgent(object): BEATS_THRESHOLD = 3 def __init__(self): self.pname="__main__" self._subToAlive=False self._ready=False self.alives=[] self.is_child=False
def hready(self): print "TestProcRx (%s) ready!" % self.name self.ready=True Bus.subscribe("tick", self._htick)
def doRun(self): print "TestProc.doRun (%s)" % self.name Bus.publish(self, "log", "starting (%s) pid(%s)" % (self.name, os.getpid())) try: while not self.is_SigTerm() and not self.is_bark() and not self.is_shutdown(): Bus.publish(self, "mswitch_pump") #print "tick (%s)" % self.name Bus.publish(self, "tick", self.name, os.getpid()) sleep(0.150+0.150*random.random()) print ">>> Exiting (%s) <<<" % self.name except Exception,e: Bus.publish(self, "log", "*** (%s) Comm Exception: %s" % (self.name, e)) print "TestProc: Exiting (%s)" % self.name Bus.publish(self, "log", "Exiting (%s)" % e) Bus.publish(self, "shutdown")
sys.path.insert(0, ppkg) class _Printer(object): def __call__(self, msg): print "!Bus: ", msg from phidgetsdbus.mbus import Bus from phidgetsdbus.system.process import ProcessClass from phidgetsdbus.agents import * Bus.logger=_Printer() #Bus.debug=True Bus.publish(None, "logpath", "tmswitch", "~/tmswitch.log") class TestProc(ProcessClass): def __init__(self, name): ProcessClass.__init__(self, name) def hready(self): print "TestProc (%s) ready!" % self.name def doRun(self): print "TestProc.doRun (%s)" % self.name Bus.publish(self, "log", "starting (%s) pid(%s)" % (self.name, os.getpid())) try: while not self.is_SigTerm() and not self.is_bark() and not self.is_shutdown(): Bus.publish(self, "mswitch_pump")
def _getQueue(self, pname): try: details=self._procs[pname] except: raise RuntimeError("missing proc from proc list") try: q=details["queue"] except: raise RuntimeError("missing `queue` parameter for proc(%s)" % pname) return q ## =============================================================================== ## =============================================================================== _mswitch=MessageSwitch() Bus.subscribe("*", _mswitch._promiscuousHandler) Bus.subscribe("_sigterm", _mswitch._hsigterm) Bus.subscribe("_sub", _mswitch._hsub) Bus.subscribe("proc", _mswitch._hproc) Bus.subscribe("_ready", _mswitch._hready) Bus.subscribe("mswitch_pump", _mswitch._hpump) Bus.subscribe("mswitch_params", _mswitch._hparams) Bus.subscribe("proc_starting", _mswitch._hproc_starting)
def _qpname(self): """ Answer for the question "pname?" """ Bus.publish(self, "pname", self._name)
proc.update(procDetails) self._procs[name]=proc def _hstart(self): """ Handler for "start" message marking the debut of all registered processes """ for proc_name in self._procs: procDetails=self._procs[proc_name] try: proc=procDetails["proc"] except: raise RuntimeError("procDetails require a `proc` entry") try: Bus.publish(self, "%log", "< starting process(%s)" % procDetails["name"]) proc.start() Bus.publish(self, "%log", "> started process(%s)" % procDetails["name"]) except Exception,e: raise RuntimeError("Exception whilst starting process (%s)" % e) ## ====================================================================================== ## ====================================================================================== _pm=ProcessManager() Bus.subscribe("start", _pm._hstart) Bus.subscribe("proc", _pm._hproc) Bus.subscribe("pname?", _pm._qpname) Bus.subscribe("proc_starting", _pm._hproc_starting)
def _log(self, *p): print p Bus.publish(self, "log", *p)
def _tick(self): self._beat = True ## atomic assignment def _qbeat(self): if self._beat: if self._child: Bus.publish(self, "%beat", self.pname) else: Bus.publish(self, "beat", self.pname) self._beat = False ## atomic assignment _heart = HeartAgent() Bus.subscribe("%beat?", _heart._qbeat) Bus.subscribe("proc_starting", _heart._hproc_starting) ## ===================================================== if __name__ == "__main__": from time import sleep Bus.debug = True class Cb(object): def beat(self, state): print "state: ", state _cb = Cb()
def _qmqueue(self): """ 'mqueue' question handler - Message Bus """ Bus.publish(self, "mqueue", self._mq)
self._log("sub: `proc_name` missing for mtype(%s)" % mtype) return if mtype is None: self._log("sub: `mtype` missing for proc_name(%s)" % pname) return subs=self._map.get(mtype, []) subs.append(pname) self._map[mtype]=subs ## ================================================================ _centralInputQueue=Queue() _centralInputQueue.cancel_join_thread() _mswitch=MessageSwitch(_centralInputQueue) Bus.subscribe("_sub", _mswitch._hsub) Bus.subscribe("proc", _mswitch._hproc) Bus.subscribe("start", _mswitch._hstart) Bus.subscribe("mqueue?", _mswitch._qmqueue) Bus.subscribe("mswitch_pump", _mswitch._hpump) Bus.subscribe("mswitch_params", _mswitch._hparams) Bus.subscribe("proc_starting", _mswitch._hproc_starting) Bus.subscribe("xsub", _mswitch._hxsub) Bus.subscribe("xbridge", _mswitch._hxbridge)
def _hbeat(self, _): """ Local "beat" message-type """ if self.is_child: return if self._termSent or self._shutdown_initiated: return if self._term: Bus.publish(self, "%log", "warning", "SIGTERM received, Main Proc") Bus.publish(self, "shutdown") self._shutdown_initiated=True if not self._subToAlive: if self._ready: self._subToAlive=True Bus.subscribe("alive", self._halive) ### Make sure we have a response from each of the ### Child processes within the Time Interval self._beat_count=self._beat_count+1 if self._beat_count == self.BEATS_THRESHOLD: self._beat_count=0 if len(self.alives) != len(self._procs): Bus.publish(self, "%log", "warning", "child process(es) missing") Bus.publish(self, "%bark", self.pname) Bus.publish(self, "shutdown") self._shutdown_initiated self.alives=[]