def loop(condition, timeout=None): """ Executes the main loop until condition is met. This function may be called recursively, however two loops may not run in parallel threads. :param condition: a callable or object that is evaluated after each step of the loop. If it evaluates False, the loop terminates. (If condition is therefore ``True``, the loop will run forever.) :param timeout: number of seconds after which the loop will terminate (regardless of the condition). """ _loop_lock.acquire() if is_running() and not is_mainthread(): # race condition. Two threads started a mainloop and the other # one is executed right now. Raise a RuntimeError _loop_lock.release() raise RuntimeError('loop running in a different thread') initial_mainloop = False if not is_running(): # no mainloop is running, set this thread as mainloop and # set the internal running state. initial_mainloop = True set_as_mainthread() _set_running(True) # ok, that was the critical part _loop_lock.release() if not callable(condition): condition = lambda: condition abort = [] if timeout is not None: # timeout handling to stop the mainloop after the given timeout # even when the condition is still True. sec = timeout timeout = OneShotTimer(lambda: abort.append(True)) timeout.start(sec) try: while condition() and not abort: try: notifier.step() signals['step'].emit() except BaseException, e: if signals['exception'].emit(*sys.exc_info()) != False: # Either there are no global exception handlers, or none of # them explicitly returned False to abort mainloop # termination. So abort the main loop. type, value, tb = sys.exc_info() raise type, value, tb finally: # make sure we set mainloop status if timeout is not None: timeout.stop() if initial_mainloop: _set_running(False)
def handle(self): """ Callback from the real mainloop. """ try: try: notifier.step(sleep = False) except (KeyboardInterrupt, SystemExit): set_mainloop_running(False) notifier.shutdown() finally: self._lock.release()
def step(*args, **kwargs): """ Performs a single iteration of the main loop. This function should almost certainly never be called directly. Use it at your own peril. """ if not is_mainthread(): # If step is being called from a thread, wake up the mainthread # instead of allowing the thread into notifier.step. wakeup() # Sleep for epsilon to prevent busy loops. time.sleep(0.001) return notifier.step(*args, **kwargs) signals['step'].emit()
def run(self): """ Thread part running the blocking, simulating loop. """ set_mainloop_running(True) try: while True: self.sleeping = True notifier.step(simulate = True) self.sleeping = False if not main.is_running(): break self._call_mainloop(self.handle) self._lock.acquire() if not main.is_running(): break except (KeyboardInterrupt, SystemExit): pass except Exception, e: log.exception('loop')