def _not_compatible(self, createActorEnvelope): # Called when the current Actor System is not compatible with # the Actor's actorSystemCapabilityCheck. Forward this # createActor request to another system that it's compatible # with. sourceHash = createActorEnvelope.message.sourceHash childRequirements = createActorEnvelope.message.targetActorReq childCName = createActorEnvelope.message.actorClassName childClass = actualActorClass( childCName, partial(loadModuleFromHashSource, sourceHash, self._sources) if sourceHash else None) acceptsCaps = lambda caps: checkActorCapabilities( childClass, caps, childRequirements) if createActorEnvelope.message.forActor is None: # Request from external; use sender address createActorEnvelope.message.forActor = createActorEnvelope.sender iolist = self._cstate.forward_pending_to_remote_system( childClass, createActorEnvelope, sourceHash, acceptsCaps) if iolist: for each in iolist: # Expected to be only one; if the transmit fails, # route it back here so that the next possible # remote can be tried. each.addCallback(onFailure=self._pending_send_failed) each.orig_create_envelope = createActorEnvelope return self._performIO(iolist) self._sendPendingActorResponse( createActorEnvelope, None, errorCode=PendingActorResponse.ERROR_No_Compatible_ActorSystem, errorStr="") # self._retryPendingChildOperations(childInstance, None) return True
def _startChildActor(self, childAddr, childClass, globalName, parentAddr, notifyAddr, childRequirements=None, sourceHash=None, sourceToLoad=None): """Create a new actor of type `childClass'. The `childAddr' is the local address of this child in the creator's address-space. The `parentAddr' is the parent of this actor in the heirarchy and will be another Actor or the local Admin. The `notifyAddr' is the Actor or Admin which should be notified on successful creation of this child Actor (normally this will be the parentAddr, but if the local Admin has been enlisted to create this Actor on behalf of another (possibly remote) Actor, the local Admin should be notified of the successful creation to complete it's administration and the Admin will forward the completion to the original requestor.). The optional `childRequirements' are a list of requirements dictated by the creating Actor. """ if parentAddr is None: raise ActorSystemFailure('parentAddr cannot be None!') if self.asLogger is None: raise ActorSystemFailure('logger ADDR cannot be None!') try: if not checkActorCapabilities( childClass, self.capabilities, childRequirements, partial(loadModuleFromHashSource, sourceHash, {sourceHash: sourceToLoad}) if sourceHash # and sourceToLoad else None): raise NoCompatibleSystemForActor( childClass, "no system has compatible capabilities") except (InvalidActorSourceHash, ImportError): # Allow these exceptions to propagate outward since they # have special, public meaning raise except Exception: # Most exceptions should be converted to # NoCompatibleSystemForActor so that calling code # recognizes this issue and defers the create request to # the Admin. raise NoCompatibleSystemForActor( childClass, "no system has compatible capabilities") # KWQ: when child starts it will have this parent address and it will initialize its transport and notify the parent, whereupon the parent will see the incoming message from the child with the id# indicated in the addressmanager localaddress and update the localaddress. All this should happen in the transport though, not here. endpointPrep = self.transport.prepEndpoint(childAddr, self.capabilities) multiprocessing.process._current_process._daemonic = False # Ensure fileNumsToClose is a list, not an iterator because it # is an argument passed to the child. fileNumsToClose = list(self.transport.childResetFileNumList()) mp = self.mpcontext if self.mpcontext else multiprocessing ccArg = '%s.%s'%(sys.modules[childClass.__module__].__name__, childClass.__name__) \ if hasattr(childClass, '__name__') else childClass child = mp.Process( target=startChild, args=(ccArg, globalName, endpointPrep, self.transport.__class__, sourceHash or self._sourceHash, sourceToLoad, parentAddr, self._adminAddr, notifyAddr, self.asLogger, childRequirements, self.capabilities, fileNumsToClose, self.mpcontext), name='Actor_%s__%s' % (getattr(childClass, '__name__', childClass), str(childAddr))) child.start() # Also note that while non-daemonic children cause the current # process to automatically join() those children on exit, # daemonic children are sent a terminate() operation (usually # indicated by a SIGTERM under unix or TERMINATE indicator # under windows. To avoid this, use another dirty trick and # remove all children from the _current_process._children list # so that they are not automatically stopped when this process # stops. detach_child(child) if not hasattr(self, '_child_procs'): self._child_procs = [] self._child_procs.append( ChildInfo(childAddr, childClass, child, endpointPrep.addrInst)) self.transport.connectEndpoint(endpointPrep)
def _startChildActor(self, childAddr, childClass, parentAddr, notifyAddr, childRequirements=None, sourceHash=None, sourceToLoad=None): """Create a new actor of type `childClass'. The `childAddr' is the local address of this child in the creator's address-space. The `parentAddr' is the parent of this actor in the heirarchy and will be another Actor or the local Admin. The `notifyAddr' is the Actor or Admin which should be notified on successful creation of this child Actor (normally this will be the parentAddr, but if the local Admin has been enlisted to create this Actor on behalf of another (possibly remote) Actor, the local Admin should be notified of the successful creation to complete it's administration and the Admin will forward the completion to the original requestor.). The optional `childRequirements' are a list of requirements dictated by the creating Actor. """ if parentAddr is None: raise ActorSystemFailure('parentAddr cannot be None!') if self.asLogger is None: raise ActorSystemFailure('logger ADDR cannot be None!') if not checkActorCapabilities(childClass, self.capabilities, childRequirements, partial(loadModuleFromHashSource, sourceHash, { sourceHash: sourceToLoad }) if sourceHash else None): raise NoCompatibleSystemForActor(childClass, "no system has compatible capabilities") # KWQ: when child starts it will have this parent address and it will initialize its transport and notify the parent, whereupon the parent will see the incoming message from the child with the id# indicated in the addressmanager localaddress and update the localaddress. All this should happen in the transport though, not here. endpointPrep = self.transport.prepEndpoint(childAddr) multiprocessing.process._current_process._daemonic = False fileNumsToClose = self.transport.childResetFileNumList() child = multiprocessing.Process(target=startChild, #KWQ: instantiates module specified by sourceHash to create actor args=(childClass, endpointPrep, self.transport.__class__, sourceHash or self._sourceHash, sourceToLoad, parentAddr, self._adminAddr, notifyAddr, self.asLogger, childRequirements, self.capabilities, fileNumsToClose), name='Actor_%s__%s'%(childClass, str(childAddr))) child.daemon = True #multiprocessing.process._current_process._daemonic = True child.start() # Also note that while non-daemonic children cause the current # process to automatically join() those children on exit, # daemonic children are sent a terminate() operation (usually # indicated by a SIGTERM under unix or TERMINATE indicator # under windows. To avoid this, use another dirty trick and # remove all children from the _current_process._children list # so that they are not automatically stopped when this process # stops. multiprocessing.process._current_process._children.remove(child) if not hasattr(self, '_child_procs'): self._child_procs = [] self._child_procs.append(child) self.transport.connectEndpoint(endpointPrep)
def h_PendingActor(self, envelope): sourceHash = envelope.message.sourceHash childRequirements = envelope.message.targetActorReq thesplog('Pending Actor request received for %s%s reqs %s from %s', envelope.message.actorClassName, ' (%s)'%sourceHash if sourceHash else '', childRequirements, envelope.sender) if sourceHash: if sourceHash not in self._sources: # If this request was forwarded by a remote Admin and the # sourceHash is not known locally, request it from the sending # remote Admin if self._cstate.sentByRemoteAdmin(envelope) and \ self._acceptsRemoteLoadedSourcesFrom(envelope): self._sources[sourceHash] = PendingSource(sourceHash, None) self._sources[sourceHash].pending_actors.append(envelope) self._hysteresisSender.sendWithHysteresis( TransmitIntent( envelope.sender, SourceHashTransferRequest(sourceHash, bool(self._sourceAuthority)))) # sent with hysteresis, so break out to local _run return False if sourceHash in self._sources and \ not self._sources[sourceHash].source_valid: # Still pending, add this create request to the waiting list self._sources[sourceHash].pending_actors.append(envelope) return True # If the requested ActorClass is compatible with this # ActorSystem, attempt to start it, otherwise forward the # request to any known compatible ActorSystem. childClass = envelope.message.actorClassName try: childClass = actualActorClass(envelope.message.actorClassName, partial(loadModuleFromHashSource, sourceHash, self._sources) if sourceHash else None) acceptsCaps = lambda caps: checkActorCapabilities(childClass, caps, childRequirements) if not acceptsCaps(self.capabilities): if envelope.message.forActor is None: # Request from external; use sender address envelope.message.forActor = envelope.sender iolist = self._cstate.forward_pending_to_remote_system( childClass, envelope, sourceHash, acceptsCaps) for each in iolist: # Expected to be only one; if the transmit fails, # route it back here so that the next possible # remote can be tried. each.addCallback(onFailure=self._pending_send_failed) self._performIO(iolist) return True except NoCompatibleSystemForActor as ex: thesplog(str(ex), level=logging.WARNING, primary=True) self._sendPendingActorResponse( envelope, None, errorCode=PendingActorResponse.ERROR_No_Compatible_ActorSystem) return True except InvalidActorSourceHash: self._sendPendingActorResponse( envelope, None, errorCode=PendingActorResponse.ERROR_Invalid_SourceHash) return True except InvalidActorSpecification as ex: thesplog('Error: InvalidActorSpecification: %s', str(ex), exc_info=True) self._sendPendingActorResponse( envelope, None, errorCode=PendingActorResponse.ERROR_Invalid_ActorClass, errorStr=str(ex)) return True except ImportError as ex: self._sendPendingActorResponse( envelope, None, errorCode=PendingActorResponse.ERROR_Import, errorStr=str(ex)) return True except AttributeError as ex: # Usually when the module has no attribute FooActor thesplog('Error: AttributeError: %s', str(ex), exc_info=True) self._sendPendingActorResponse( envelope, None, errorCode=PendingActorResponse.ERROR_Invalid_ActorClass, errorStr=str(ex)) return True except Exception as ex: import traceback thesplog('Exception "%s" handling PendingActor: %s', ex, traceback.format_exc(), level=logging.ERROR) self._sendPendingActorResponse( envelope, None, errorCode=PendingActorResponse.ERROR_Invalid_ActorClass, errorStr=str(ex)) return True return super(ConventioneerAdmin, self).h_PendingActor(envelope)
def h_PendingActor(self, envelope): sourceHash = envelope.message.sourceHash childRequirements = envelope.message.targetActorReq thesplog('Pending Actor request for %s%s reqs %s from %s', envelope.message.actorClassName, ' (%s)' % sourceHash if sourceHash else '', childRequirements, envelope.sender) # If this request was forwarded by a remote Admin and the # sourceHash is not known locally, request it from the sending # remote Admin if sourceHash and \ sourceHash not in self._sources and \ self._sentByRemoteAdmin(envelope) and \ self._acceptsRemoteLoadedSourcesFrom(envelope): requestedAlready = self._pendingSources.get(sourceHash, False) self._pendingSources.setdefault(sourceHash, []).append(envelope) if not requestedAlready: self._hysteresisSender.sendWithHysteresis( TransmitIntent(envelope.sender, SourceHashTransferRequest(sourceHash))) return False # sent with hysteresis, so break out to local _run return True # If the requested ActorClass is compatible with this # ActorSystem, attempt to start it, otherwise forward the # request to any known compatible ActorSystem. try: childClass = actualActorClass( envelope.message.actorClassName, partial(loadModuleFromHashSource, sourceHash, self._sources) if sourceHash else None) acceptsCaps = lambda caps: checkActorCapabilities( childClass, caps, childRequirements) if not acceptsCaps(self.capabilities): if envelope.message.forActor is None: # Request from external; use sender address envelope.message.forActor = envelope.sender remoteCandidates = [ K for K in self._conventionMembers if not self._conventionMembers[K].registryValid.expired() and self._conventionMembers[K].remoteAddress != envelope.sender # source Admin and self._conventionMembers[K].remoteAddress not in getattr(envelope.message, 'alreadyTried', []) and acceptsCaps(self._conventionMembers[K].remoteCapabilities) ] if not remoteCandidates: if self.isConventionLeader(): thesplog( 'No known ActorSystems can handle a %s for %s', childClass, envelope.message.forActor, level=logging.WARNING, primary=True) self._sendPendingActorResponse( envelope, None, errorCode=PendingActorResponse. ERROR_No_Compatible_ActorSystem) return True # Let the Convention Leader try to find an appropriate ActorSystem bestC = self._conventionAddress else: # distribute equally amongst candidates C = [(self._conventionMembers[K].remoteAddress, len(self._conventionMembers[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) envelope.message.alreadyTried.append(self.myAddress) self._send_intent(TransmitIntent(bestC, envelope.message)) return True except InvalidActorSourceHash: self._sendPendingActorResponse( envelope, None, errorCode=PendingActorResponse.ERROR_Invalid_SourceHash) return True except InvalidActorSpecification: self._sendPendingActorResponse( envelope, None, errorCode=PendingActorResponse.ERROR_Invalid_ActorClass) return True except ImportError as ex: self._sendPendingActorResponse( envelope, None, errorCode=PendingActorResponse.ERROR_Import, errorStr=str(ex)) return True except AttributeError as ex: # Usually when the module has no attribute FooActor self._sendPendingActorResponse( envelope, None, errorCode=PendingActorResponse.ERROR_Invalid_ActorClass, errorStr=str(ex)) return True except Exception as ex: import traceback thesplog('Exception "%s" handling PendingActor: %s', ex, traceback.format_exc()) self._sendPendingActorResponse( envelope, None, errorCode=PendingActorResponse.ERROR_Invalid_ActorClass, errorStr=str(ex)) return True return super(ConventioneerAdmin, self).h_PendingActor(envelope)
def h_PendingActor(self, envelope): sourceHash = envelope.message.sourceHash childRequirements = envelope.message.targetActorReq thesplog('Pending Actor request received for %s%s reqs %s from %s', envelope.message.actorClassName, ' (%s)' % sourceHash if sourceHash else '', childRequirements, envelope.sender) # If this request was forwarded by a remote Admin and the # sourceHash is not known locally, request it from the sending # remote Admin if sourceHash and \ sourceHash not in self._sources and \ self._cstate.sentByRemoteAdmin(envelope) and \ self._acceptsRemoteLoadedSourcesFrom(envelope): requestedAlready = self._pendingSources.get(sourceHash, False) self._pendingSources.setdefault(sourceHash, []).append(envelope) if not requestedAlready: self._hysteresisSender.sendWithHysteresis( TransmitIntent(envelope.sender, SourceHashTransferRequest(sourceHash))) return False # sent with hysteresis, so break out to local _run return True # If the requested ActorClass is compatible with this # ActorSystem, attempt to start it, otherwise forward the # request to any known compatible ActorSystem. childClass = envelope.message.actorClassName try: childClass = actualActorClass( envelope.message.actorClassName, partial(loadModuleFromHashSource, sourceHash, self._sources) if sourceHash else None) acceptsCaps = lambda caps: checkActorCapabilities( childClass, caps, childRequirements) if not acceptsCaps(self.capabilities): if envelope.message.forActor is None: # Request from external; use sender address envelope.message.forActor = envelope.sender iolist = self._cstate.forward_pending_to_remote_system( childClass, envelope, sourceHash, acceptsCaps) for each in iolist: # Expected to be only one; if the transmit fails, # route it back here so that the next possible # remote can be tried. each.addCallback(onFailure=self._pending_send_failed) self._performIO(iolist) return True except NoCompatibleSystemForActor as ex: thesplog(str(ex), level=logging.WARNING, primary=True) self._sendPendingActorResponse( envelope, None, errorCode=PendingActorResponse.ERROR_No_Compatible_ActorSystem) return True except InvalidActorSourceHash: self._sendPendingActorResponse( envelope, None, errorCode=PendingActorResponse.ERROR_Invalid_SourceHash) return True except InvalidActorSpecification: self._sendPendingActorResponse( envelope, None, errorCode=PendingActorResponse.ERROR_Invalid_ActorClass) return True except ImportError as ex: self._sendPendingActorResponse( envelope, None, errorCode=PendingActorResponse.ERROR_Import, errorStr=str(ex)) return True except AttributeError as ex: # Usually when the module has no attribute FooActor self._sendPendingActorResponse( envelope, None, errorCode=PendingActorResponse.ERROR_Invalid_ActorClass, errorStr=str(ex)) return True except Exception as ex: import traceback thesplog('Exception "%s" handling PendingActor: %s', ex, traceback.format_exc(), level=logging.ERROR) self._sendPendingActorResponse( envelope, None, errorCode=PendingActorResponse.ERROR_Invalid_ActorClass, errorStr=str(ex)) return True return super(ConventioneerAdmin, self).h_PendingActor(envelope)
def h_PendingActor(self, envelope): sourceHash = envelope.message.sourceHash childRequirements = envelope.message.targetActorReq # If this request was forwarded by a remote Admin and the # sourceHash is not known locally, request it from the sending # remote Admin if sourceHash and \ sourceHash not in self._sources and \ self._sentByRemoteAdmin(envelope) and \ self._acceptsRemoteLoadedSourcesFrom(envelope): requestedAlready = self._pendingSources.get(sourceHash, False) self._pendingSources.setdefault(sourceHash, []).append(envelope) if not requestedAlready: self._hysteresisSender.sendWithHysteresis( TransmitIntent(envelope.sender, SourceHashTransferRequest(sourceHash))) return False # sent with hysteresis, so break out to local _run return True # If the requested ActorClass is compatible with this # ActorSystem, attempt to start it, otherwise forward the # request to any known compatible ActorSystem. try: childClass = actualActorClass(envelope.message.actorClassName, partial(loadModuleFromHashSource, sourceHash, self._sources) if sourceHash else None) acceptsCaps = lambda caps: checkActorCapabilities(childClass, caps, childRequirements) if not acceptsCaps(self.capabilities): if envelope.message.forActor is None: # Request from external; use sender address envelope.message.forActor = envelope.sender remoteCandidates = [ K for K in self._conventionMembers if not self._conventionMembers[K].registryValid.expired() and self._conventionMembers[K].remoteAddress != envelope.sender # source Admin and acceptsCaps(self._conventionMembers[K].remoteCapabilities)] if not remoteCandidates: if self.isConventionLeader(): thesplog('No known ActorSystems can handle a %s for %s', childClass, envelope.message.forActor, level=logging.WARNING, primary=True) self._sendPendingActorResponse(envelope, None, errorCode = PendingActorResponse.ERROR_No_Compatible_ActorSystem) return True # Let the Convention Leader try to find an appropriate ActorSystem bestC = self._conventionAddress else: # distribute equally amongst candidates C = [(self._conventionMembers[K].remoteAddress, len(self._conventionMembers[K].hasRemoteActors)) for K in remoteCandidates] bestC = foldl(lambda best,possible: best if best[1] <= possible[1] else possible, C)[0] self._send_intent(TransmitIntent(bestC, envelope.message)) return True except InvalidActorSourceHash: self._sendPendingActorResponse(envelope, None, errorCode = PendingActorResponse.ERROR_Invalid_SourceHash) return True except InvalidActorSpecification: self._sendPendingActorResponse(envelope, None, errorCode = PendingActorResponse.ERROR_Invalid_ActorClass) return True except ImportError: self._sendPendingActorResponse(envelope, None, errorCode = PendingActorResponse.ERROR_Import) return True return super(ConventioneerAdmin, self).h_PendingActor(envelope)