def testNonZeroRemaining(self): et = ExpirationTimer(timedelta(milliseconds=10)) ct = currentTime() assert timedelta(days=0) < et.view(ct).remaining() assert timedelta(milliseconds=11) > et.view(ct).remaining() sleep(et.view().remainingSeconds()) assert timedelta(days=0) == et.view().remaining()
def run(self): # Main loop for convention management. Wraps the lower-level # transport with a stop at the next needed convention # registration period to re-register. transport_continue = True try: while not getattr(self, 'shutdown_completed', False) and \ not isinstance(transport_continue, Thespian__Run_Terminated): ct = currentTime() delay = min( self._cstate.convention_inattention_delay(ct), ExpirationTimer(None).view(ct) if self._hysteresisSender.delay.expired() else self._hysteresisSender.delay) # n.b. delay does not account for soon-to-expire # pingValids, but since delay will not be longer than # a CONVENTION_REREGISTRATION_PERIOD, the worst case # is a doubling of a pingValid period (which should be fine). transport_continue = self.transport.run( self.handleIncoming, delay.remaining()) # Check Convention status based on the elapsed time self._performIO(self._cstate.check_convention()) self._hysteresisSender.checkSends() self._remove_expired_sources() except Exception as ex: import traceback thesplog('ActorAdmin uncaught exception: %s', traceback.format_exc(), level=logging.ERROR, exc_info=True) thesplog('Admin time to die', level=logging.DEBUG)
def _realizeWakeups(self): "Find any expired wakeups and queue them to the send processing queue" ct = currentTime() for target_addr, expired, payload in self._pop_expired_wakeups(ct): with self._private_lock: self._pendingSends.append( PendingSend(target_addr, WakeupMessage(expired.view(ct).duration, payload), target_addr))
def _remove_expired_sources(self): rmvlist = [] ct = currentTime() for each in self._sources: if not self._sources[each].source_valid and \ self._sources[each].load_expires.view(ct).expired(): rmvlist.append(each) for each in rmvlist: self._cancel_pending_actors(self._sources[each].pending_actors) del self._sources[each]
def _realizeWakeups(self): "Find any expired wakeups and queue them to the send processing queue" with self._wakeup_lock: ct = currentTime() starting_len = len(self._activeWakeups) while self._pendingWakeups and self._pendingWakeups[0][0].view( ct).expired(): timer, payload = self._pendingWakeups.pop(0) self._activeWakeups.append( ReceiveEnvelope(self.myAddress, WakeupMessage(timer.duration, payload))) return starting_len != len(self._activeWakeups)
def _common_formatStatus(tofd, response, childActorTag, showAddress=str): tofd.write(' |%s Actors [%d]:\n' % (childActorTag, len(response.childActors))) for A in response.childActors: tofd.write(' @ %s\n' % (showAddress(A))) if response.governer: tofd.write(' |Rate Governer: %s\n' % (str(response.governer))) tofd.write(' |Pending Messages [%d]:\n' % len(response.pendingMessages)) for F, T, M in response.pendingMessages: tofd.write(' %s --> %s: %s\n' % (showAddress(F), showAddress(T), M)) tofd.write(' |Received Messages [%d]:\n' % len(response.receivedMessages)) for F, T, M in response.receivedMessages: tofd.write(' %s <-- %s: %s\n' % (showAddress(T), showAddress(F), M)) # pendingWakeups is a dict with datetime keys and a list of # ReceiveEnvelope(WakeupMessage) values in older Thespian versions # (3.5.2 and earlier), changed to a list of (address, # ExpirationTimer) in newer versions. The following maintains # compatibility for both. tofd.write(' |Pending Wakeups [%d]:\n' % len(response.pendingWakeups)) from thespian.system.timing import currentTime from datetime import datetime now = datetime.now() ct = currentTime() pw = [(('' if V.sender == getattr(response, 'actorAddress', None) else '--> %s : ' % V.sender), '%s (in %s @ %s)' % (V.message.delayPeriod, str(A - now), str(A))) for A in response.pendingWakeups for V in response.pendingWakeups[A]] \ if isinstance(response.pendingWakeups, dict) else \ [(('' if A == getattr(response, 'actorAddress', None) else '--> %s : ' % A), '%s (in %s @ %s)' % (W.view(ct).duration, W.view(ct).remaining(), str(now + W.view(ct).remaining()))) for (A,W) in response.pendingWakeups] for each in pw: tofd.write(' %s%s\n' % each) tofd.write(' |Pending Address Resolution [%d]:\n' % (len(response._pendingAddrCnts))) for A in response._pendingAddrCnts: tofd.write(' %s: %s\n' % (A, response._pendingAddrCnts[A])) if response.miscKeyVals: miscKeys = list(response.miscKeyVals.keys()) miscKeys.sort() # Adjust all output for the longest value... up to 40 characters max maxValLen = min(40, max(map(len, map(str, response.miscKeyVals.values())))) for K in miscKeys: tofd.write(' |> %%%ds - %%s\n' % maxValLen % (str(response.miscKeyVals[K]), K))
def check_convention(self): ct = currentTime() rmsgs = [] if self._has_been_activated: rmsgs = foldl(lambda x, y: x + y, [self._check_preregistered_ping(ct, member) for member in self._conventionMembers.values()], self._convention_leader_checks(ct) if self.isConventionLeader() or not self.conventionLeaderAddr else self._convention_member_checks(ct)) if self._conventionRegistration.view(ct).expired(): self._conventionRegistration = ExpirationTimer(CONVENTION_REREGISTRATION_PERIOD) return rmsgs
def delay(self, current_time=None): ct = current_time or currentTime() qt = self._quitTime.view(ct) if getattr(self, '_awaitingTXSlot', False): if qt.expired(): return timedelta(seconds=0) return max(timedelta(milliseconds=10), (qt.remaining()) / 2) return max( timedelta(seconds=0), min( qt.remaining(), getattr(self, '_retryTime', self._quitTime).view(ct).remaining(), getattr(self, '_pauseUntil', self._quitTime).view(ct).remaining()))
def forward_pending_to_remote_system(self, childClass, envelope, sourceHash, acceptsCaps): alreadyTried = getattr(envelope.message, 'alreadyTried', []) ct = currentTime() if self.myAddress not in alreadyTried: # Don't send request back to this actor system: it cannot # handle it alreadyTried.append(self.myAddress) remoteCandidates = [ K for K in self._conventionMembers.values() if not K.registryValid.view(ct).expired() and K.remoteAddress != envelope.sender # source Admin and K.remoteAddress not in alreadyTried and acceptsCaps(K.remoteCapabilities) ] if not remoteCandidates: if self.isConventionLeader() or not self.conventionLeaderAddr: raise NoCompatibleSystemForActor( childClass, 'No known ActorSystems can handle a %s for %s', childClass, envelope.message.forActor) # Let the Convention Leader try to find an appropriate ActorSystem bestC = self.conventionLeaderAddr else: # distribute equally amongst candidates C = [(K.remoteAddress, len(K.hasRemoteActors)) for K in remoteCandidates] bestC = foldl( lambda best, possible: best if best[1] <= possible[1] else possible, C)[0] thesplog('Requesting creation of %s%s on remote admin %s', envelope.message.actorClassName, ' (%s)' % sourceHash if sourceHash else '', bestC) if bestC in alreadyTried: return [] # Have to give up, no-one can handle this # Don't send request to this remote again, it has already # been tried. This would also be indicated by that system # performing the add of self.myAddress as below, but if # there is disagreement between the local and remote # addresses, this addition will prevent continual # bounceback. alreadyTried.append(bestC) envelope.message.alreadyTried = alreadyTried return [TransmitIntent(bestC, envelope.message)]
def _runWithExpiry(self, incomingHandler): if incomingHandler == TransmitOnly or \ isinstance(incomingHandler, TransmitOnly): # transmits are not queued/multistage in this transport, no waiting return 0 self._aborting_run = None while self._aborting_run is None: ct = currentTime() if self.run_time.view(ct).expired(): break if self._checkChildren: self._checkChildren = False rcvdEnv = ReceiveEnvelope(self.myAddress, ChildMayHaveDied()) elif self._shutdownSignalled: self._shutdownSignalled = False rcvdEnv = ReceiveEnvelope(self.myAddress, ActorExitRequest()) elif self._rcvd: rcvdEnv = self._rcvd.pop() else: next_action_timeout = self.check_pending_actions(ct) try: sresp, _ign1, _ign2 = select.select( [self.socket.fileno()], [], [], min(self.run_time, next_action_timeout).view(ct).remainingSeconds()) except (OSError, select.error) as se: errnum = getattr(se, 'errno', se.args[0]) if err_select_retry(errnum): thesplog('UDP select retry on %s', se, level=logging.DEBUG) continue thesplog('Error during UDP select: %s', se, level=logging.CRITICAL) return Thespian__Run_Errored(se) except ValueError: # self.run_time can expire between the while test # and the use in the select statement. continue if [] == sresp: if [] == _ign1 and [] == _ign2: # Timeout, give up return Thespian__Run_Expired() thesplog('Waiting for read event, but got %s %s', _ign1, _ign2, level=logging.WARNING) continue rawmsg, sender = self.socket.recvfrom(65535) if rawmsg == b'BuMP': if self._checkChildren or self._shutdownSignalled: continue return Thespian__UpdateWork() else: sendAddr = ActorAddress( UDPv4ActorAddress(*sender, external=True)) try: msg = serializer.loads(rawmsg) except Exception: continue rcvdEnv = ReceiveEnvelope(sendAddr, msg) if incomingHandler is None: return rcvdEnv r = Thespian__Run_HandlerResult(incomingHandler(rcvdEnv)) if not r: # handler returned false-ish, indicating run() should exit return r if self._aborting_run is not None: return self._aborting_run return Thespian__Run_Expired()