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 stop( self, cmd = None ): """ Stop the child. If 'cmd' is given, this stop command will send to the app to stop itself. If this is not working, kill -15 and kill -9 will be used to kill the app. Returns an InProgress which finishes when the process stops. """ if self.stopping: return inprogress(self.signals['completed']) if not is_mainthread(): return MainThreadCallback(self.stop, cmd)() self.stopping = True cmd = cmd or self._stop_cmd if self.is_alive() and not self.__kill_timer: if cmd: log.info('sending exit command to app') if callable(cmd): cmd() else: self.write(cmd) cb = Callback( self.__kill, 15 ) self.__kill_timer = notifier.timer_add( 3000, cb ) else: cb = Callback( self.__kill, 15 ) self.__kill_timer = notifier.timer_add( 0, cb ) return inprogress(self.signals['completed'])
def unregister(self): """ Unregister the IOMonitor """ if not self.active: return if not is_mainthread(): return MainThreadCallback(self.unregister)() notifier.socket_remove(self._id, self._condition-1) super(IOMonitor, self).unregister()
def post(self, *args): """ Post event into the queue. """ event = self if args: event = copy.copy(self) event._set_args(args) if not is_mainthread(): return MainThreadCallback(manager.post, event)() else: return manager.post(event)
def _shutdown_check(*args): # Helper function to shutdown kaa on system exit # The problem is that pytgtk just exits python and # does not simply return from the main loop and kaa # can't call the shutdown handler. This is not a perfect # solution, e.g. with the generic mainloop you can do # stuff after kaa.main.run() which is not possible with gtk if is_running(): # If the kaa mainthread (i.e. thread the mainloop is running in) # is not the program's main thread, then is_mainthread() will be False # and we don't need to set running=False since shutdown() will raise a # SystemExit and things will exit normally. if is_mainthread(): _set_running(False) stop()
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 wait(self, timeout=None): """ Blocks until the InProgress is finished. The main loop is kept alive if waiting in the main thread, otherwise the thread is blocked until another thread finishes the InProgress. If the InProgress finishes due to an exception, that exception is raised. :param timeout: if not None, wait() blocks for at most timeout seconds (which may be fractional). If wait times out, a TimeoutException is raised. :return: the value the InProgress finished with """ # Connect a dummy handler to ourselves. This is a bit kludgy, but # solves a particular problem with InProgress(Any|All), which don't # actually finish unless something wants to know. Normally, without # wait, we are yielded to the coroutine wrapper which implicitly # connects to us. Here, with wait(), in a sense we want to know when # self finishes. dummy = lambda *args, **kwargs: None self.connect(dummy) if is_mainthread(): # We're waiting in the main thread, so we must keep the mainloop # alive by calling main.loop() until we're finished. main.loop(lambda: not self.finished, timeout) elif not main.is_running(): # Seems that no loop is running, try to loop try: main.loop(lambda: not self.finished, timeout) except RuntimeError: # oops, there is something running, wait self._finished_event.wait(timeout) else: # We're waiting in some other thread, so wait for some other # thread to wake us up. self._finished_event.wait(timeout) if not self.finished: self.disconnect(dummy) raise TimeoutException return self.result
def start(self, args = None): """ Starts the process. If args is not None, it can be either a list or string, as with the constructor, and is appended to the command line specified in the constructor. """ if not self.__dead: raise SystemError, "Process is already running." if self.stopping: raise SystemError, "Process isn't done stopping yet." cmd = self._cmd + self._normalize_cmd(args) self.__kill_timer = None self.__dead = False self.binary = cmd[0] self.child = popen2.Popen3( cmd, True, 100 ) flags = fcntl.fcntl(self.child.tochild.fileno(), fcntl.F_GETFL) fcntl.fcntl( self.child.tochild.fileno(), fcntl.F_SETFL, flags | os.O_NONBLOCK ) self._wmon = IOMonitor(self._handle_write) if self._write_buffer: self._wmon.register(self.child.tochild, IO_WRITE) log.info('running %s (pid=%s)' % ( self.binary, self.child.pid ) ) # IO_Handler for stdout self.stdout = IO_Handler( 'stdout', self.child.fromchild, self.signals["stdout"], self.signals['raw-stdout'], self._debugname ) # IO_Handler for stderr self.stderr = IO_Handler( 'stderr', self.child.childerr, self.signals["stderr"], self.signals['raw-stderr'], self._debugname ) # add child to watcher if not is_mainthread(): MainThreadCallback(proclist.append)(self, self.__child_died) else: proclist.append( self, self.__child_died ) self.in_progress = InProgress() return self.in_progress
def register(self, fd, condition = IO_READ): """ Register the IOMonitor to a specific file descriptor. The IOMonitor is registered with the notifier, which means that the notifier holds a reference to the IOMonitor until it is explicitly unregistered (or until the file descriptor is closed). :param fd: The file descriptor to monitor. :type fd: File descriptor or any file-like object :param condition: IO_READ or IO_WRITE """ if self.active: if fd != self._id or condition != self._condition: raise ValueError('Existing file descriptor already registered with this IOMonitor.') return if not is_mainthread(): return MainThreadCallback(self.register)(fd, condition) notifier.socket_add(fd, self, condition-1) self._condition = condition # Must be called _id to correspond with base class. self._id = fd