def close(self): """ When this method is called, any callers blocking waiting for socket events will be released, and caused to throw a ``AsyncSocketMgrNotOpen`` exception. This call is synchronous, and will not return until all internal cleanup has been performed. Still, some waiters might end up throwing their exception only after this method returns, which is not a problem. (Only called by external threads.) """ if self.dead: # Nothing to be done anymore. return try: if logging_enabled(): logwrite("adding deathwish") self.wait_for_request(None, "die", self.mortally_wound_internal_thread, []) if logging_enabled(): logwrite("deathwish acked") except AsyncSocketMgrNotOpen: # I guess someone else has already asked the manager # to stop accepting requests, which is fine. pass # This method may not return until cleanup is # complete, so do not. if logging_enabled(): logwrite("waiting for internal death to die") while not self.dead: time.sleep(0.1)
def wait_for_request(self, handle, req_type, req_func, req_args): """ Has the internal thread execute the function ``req_func`` with the arguments in list ``req_args``, and returns any result that ``req_func`` delivers back to this thread (by placing it into a known container). If the result indicates an error, ``wait_for_request`` throws the exception provided by ``req_func``. Note that we do not need to "send" requests to the internal thread. All we do need to do is to add them into the internal queue, and ensure that the internal thread notices. This operation may fail, and if it does, ``wait_for_request`` throws an exception. The ``handle`` argument specifies that the request concerns the socket with the particular handle. If the value is ``None``, then the request concerns an uncreated socket, or the manager itself. The ``req_type`` value is a descriptive string that is used mostly for more informative logging, but may in some special cases also otherwise affect the way a request gets processed. """ if logging_enabled(): logwrite("making a " + req_type + " request concerning " + str(handle)) blocker = EvSubst() result = [] req = ["req", handle, req_type, req_func, req_args, blocker, result] self.mutex.acquire() try: # This may fail with an exception, which is fine. self.queue_add(req) finally: self.mutex.release() # Wait for the request to complete. if logging_enabled(): logwrite("wait_for_request--waiting") blocker.wait() if logging_enabled(): logwrite("wait_for_request--released") # Return (or throw) the result. assert len(result) == 1 error_status, data = result[0] if error_status: raise error_status return data
def close_all_managed_sockets(self): """ Closes all the sockets being managed by this object. (Only called by the internal thread.) """ if logging_enabled(): logwrite("closing all managed sockets") for k, v in self.socket_map.iteritems(): v.close() self.socket_map.clear()
def process_request(self, req): """ The ``req`` parameter specifies a request entry. It must be of the form ["req", handle, req_type, func, args, result, event] where * ``func`` is the function that processes the request * ``args`` is an array of arguments to pass to ``func`` * ``result`` is empty array into which the result of the request will be stored in the form (status, retval), where * ``status`` is ``None``, or an exception in case of error completion * ``retval`` is a request specific result value * ``event`` is an ``Event`` object to signal when the request has been completed (Only called by the internal thread.) """ func = req[3] args = req[4] try: if logging_enabled(): logwrite("processing a " + req[2] + " request concerning " + str(req[1])) logwrite("calling function " + str(func)) func(self, req, *args) if logging_enabled(): logwrite("called ok") except Exception, exc: if logging_enabled(): logwrite("got exception") log_exception() self.complete_request(req, exc) if logging_enabled(): logwrite("completed with error")
def mortally_wound_internal_thread(self, dummy, req): """ This call causes the internal thread to wrap things up and die. The internal thread should take note of this, and start dying. It does not need to be dead yet by the time it signals the request. Indeed, it cannot itself signal anything after it already is dead. (Only called by the internal thread.) """ if not self.dying: # After this flag has been set, the internal thread # can no longer be assumed to handle or successfully # complete socket-related requests, as it won't # be processing socket events. self.dying = True if logging_enabled(): logwrite("completing wounding request") self.complete_request(req) if logging_enabled(): logwrite("completed wounding request")
def listenfunc(self, req, handle, kw): def cbfunc(orig, evtype, evstat, payload, cbparams): self, req = cbparams if self.__check_state(req): self.__mark_not_pending(req) assert evtype == "listen" if not evstat: self.complete_request(req) else: self.complete_request(req, evstat) if self.__check_state(req): socket = self.socket_map[handle] if logging_enabled(): logwrite("listenfunc--calling listen on " + str(socket)) socket.listen(cbfunc, (self, req), **kw) self.__mark_pending(req)
def create_internal_thread(self): """ This method creates and starts the internal thread that owns all the sockets, storing a reference to it in the internal ``thread`` property. """ # Create the Symbian-specific instance of this. self.itc = SymbianItc() # Now create and start the thread. # Do not forget to store the thread ID. if logging_enabled(): logwrite("starting internal thread") self.thread = None self.thread = start_thread(target=self.__loop, name="socket-owner-thread-%d" % hash(self), args=())
def __loop(self): """ The thread that owns all the sockets runs this loop that processes all asynchronous events, as well as decides how to handle any requests in the internal queue. To get this thread to do something for you (instead of blocking and waiting for events), simply queue a new asynchronous request for the thread to deal with. """ try: if logging_enabled(): logwrite("internal thread running") logwrite("logging enabled") # We use this value internally, too, so ensure it's set # before we do. while not self.thread: time.sleep(0.1) set_thread_priority(self.thread_pri) self.aoloop = AoLoop() self.aoloop.open() # Note that it is imperative that we _create_ active objects # within this thread, as otherwise they will get registered # with the active scheduler of some other thread, resulting # in stray signals when requests are made. We should also # note that some other thread may try to access the ``itc`` # object already before this thread gets to run, which is # why we created the object earlier, but are only now # registering it with the active scheduler. self.immediate = AoImmediate() self.immediate.open() self.itc.open() # We do not use this in this class, but we assume all # subclassers need an instance of this for initializing # RSocket instances. if logging_enabled(): logwrite("creating socket server handle") self.socket_serv = SymbianSocketServ() # Run a new active scheduler loop until someone # calls ``close``. self.immediate.complete(self.__req_init, None) if logging_enabled(): logwrite("starting ao loop") self.aoloop.start() if logging_enabled(): logwrite("ao loop exited") # This will cancel any remaining requests. self.__process_any_requests() # Now release those waiting for requests that we already # started processing. self.cancel_all_pending_requests() # This will ensure that no new socket-related events # will be generated, but some might have been generated # already, causing callbacks after all the cleanup # has already been done. We must make sure not to do # anything in such callbacks. self.close_all_managed_sockets() # Note that those objects that might have thread-specific # sessions must be cleaned up by this thread, rather # than left for GC to handle. We are doing the cleanup here. self.socket_serv.close() self.aoloop.close() self.immediate.close() self.itc.close() if logging_enabled(): logwrite("stopping logging for internal thread") thread_finish_logging() # This thread should die any moment after this. self.dead = True except: # Does nothing if logging has been stopped already. log_exception()