def __check_and_die(self): if not self.ptr: # We've been destroyed during the middle of self.run(). # This method is being called into from C, and it's not # safe to go back to C (Windows in particular can abort # the process with "GetQueuedCompletionStatusEx: (6) The # handle is invalid.") So switch to the parent greenlet. getcurrent().parent.throw(LoopExit('Destroyed during run'))
def run(self): """ Entry-point to running the loop. This method is called automatically when the hub greenlet is scheduled; do not call it directly. :raises gevent.exceptions.LoopExit: If the loop finishes running. This means that there are no other scheduled greenlets, and no active watchers or servers. In some situations, this indicates a programming error. """ assert self is getcurrent(), 'Do not call Hub.run() directly' self.start_periodic_monitoring_thread() while 1: loop = self.loop loop.error_handler = self try: loop.run() finally: loop.error_handler = None # break the refcount cycle # This function must never return, as it will cause # switch() in the parent greenlet to return an unexpected # value. This can show up as unexpected failures e.g., # from Waiters raising AssertionError or MulitpleWaiter # raising invalid IndexError. # # It is still possible to kill this greenlet with throw. # However, in that case switching to it is no longer safe, # as switch will return immediately. # # Note that there's a problem with simply doing # ``self.parent.throw()`` and never actually exiting this # greenlet: The greenlet tends to stay alive. This is # because throwing the exception captures stack frames # (regardless of what we do with the argument) and those # get saved. In addition to this object having # ``gr_frame`` pointing to this method, which contains # ``self``, which points to the parent, and both of which point to # an internal thread state dict that points back to the current greenlet for the thread, # which is likely to be the parent: a cycle. # # We can't have ``join()`` tell us to finish, because we # need to be able to resume after this throw. The only way # to dispose of the greenlet is to use ``self.destroy()``. debug = [] if hasattr(loop, 'debug'): debug = loop.debug() loop = None self.parent.throw(LoopExit('This operation would block forever', self, debug)) # Execution could resume here if another blocking API call is made # in the same thread and the hub hasn't been destroyed, so clean # up anything left. debug = None
def run(self): """ Entry-point to running the loop. This method is called automatically when the hub greenlet is scheduled; do not call it directly. :raises gevent.exceptions.LoopExit: If the loop finishes running. This means that there are no other scheduled greenlets, and no active watchers or servers. In some situations, this indicates a programming error. """ assert self is getcurrent(), 'Do not call Hub.run() directly' self.start_periodic_monitoring_thread() while 1: loop = self.loop loop.error_handler = self try: loop.run() finally: loop.error_handler = None # break the refcount cycle debug = [] if hasattr(loop, 'debug'): debug = loop.debug() self.parent.throw(LoopExit('This operation would block forever', self, debug))