def test_wait_for_objects(): long_cmd = [sys.executable, "-c", "import time; time.sleep(40.0)"] short_cmd = [sys.executable, "-c", "pass"] p = e3.os.process.Run(long_cmd, bg=True) assert (wait_for_objects([int(p.internal._handle)], timeout=1) is None), "timeout was expected" p.kill() p0 = e3.os.process.Run(long_cmd, bg=True) p1 = e3.os.process.Run(short_cmd, bg=True) try: assert (wait_for_objects( [int(p0.internal._handle), int(p1.internal._handle)], timeout=2) == 1), "process 1 was expected" finally: p0.kill() p1.kill() p0 = e3.os.process.Run(long_cmd, bg=True) p1 = e3.os.process.Run(short_cmd, bg=True) try: assert (wait_for_objects( [int(p0.internal._handle), int(p1.internal._handle)], timeout=2, wait_for_all=True, ) is None), "timeout expected" finally: p0.kill() p1.kill() p0 = e3.os.process.Run(short_cmd, bg=True) p1 = e3.os.process.Run(short_cmd, bg=True) try: assert (wait_for_objects( [int(p0.internal._handle), int(p1.internal._handle)], timeout=0, wait_for_all=True, ) is not None), "no timeout expected" finally: p0.kill() p1.kill()
def wait_for_processes(process_list, timeout): """Wait for several processes spawned with Run. :param process_list: a list of Run objects :type process_list: list[Run] :param timeout: a timeout in seconds. If 0 block until a process ends. :type timeout: int :return: None in case of timeout or the index in process Run corresponding to the first process that end :rtype: None | int """ if len(process_list) == 0: return None start = time.time() remain = timeout if sys.platform == 'win32': # unix: no cover from e3.os.windows.process import process_exit_code, wait_for_objects handles = [int(p.internal._handle) for p in process_list] while True: try: idx = wait_for_objects(handles, remain, False) if idx is None: return if process_exit_code(handles[idx]) is None: # Process is still active so wait after updating timeout remain = timeout - time.time() + start if remain <= 0: # No remaining time return None else: # Process is exiting so finalize it by calling wait process_list[idx].wait() return idx except OSError: raise WaitError else: # windows: no cover import select # Each time a SIGCHLD signal is received write into pipe. Use # then select which support timeout arguments to wait. fd_r, fd_w = os.pipe() def handler(signum, frame): del signum, frame os.write(fd_w, b'a') signal.signal(signal.SIGCHLD, handler) try: while remain >= 0.0 or timeout == 0: # Do a first check in case a SIGCHLD was emited before the # initialisation of the handler. for index, p in enumerate(process_list): if p.poll() is not None: return index # Wait for a sigchld signal. Note that select might # be interrupted by signals thus the loop select_args = [[fd_r], [], []] if timeout != 0: select_args.append(remain) while True: try: l_r, _, _ = select.select(*select_args) if l_r: os.read(fd_r, 1) break except select.error: pass remain = timeout - time.time() + start logger.warning('no process ended after %f seconds', time.time() - start) # defensive code finally: # Be sure to remove signal handler and close pipe signal.signal(signal.SIGCHLD, 0) os.close(fd_r) os.close(fd_w)
def wait_for_processes(process_list, timeout): """Wait for several processes spawned with Run. :param process_list: a list of Run objects :type process_list: list[Run] :param timeout: a timeout in seconds. If 0 block until a process ends. :type timeout: int :return: None in case of timeout or the index in process Run corresponding to the first process that end :rtype: None | int """ if len(process_list) == 0: return None start = time.time() remain = timeout if sys.platform == 'win32': # unix: no cover from e3.os.windows.process import process_exit_code, wait_for_objects handles = [int(p.internal._handle) for p in process_list] while True: try: idx = wait_for_objects(handles, remain, False) if idx is None: return if process_exit_code(handles[idx]) is None: # Process is still active so wait after updating timeout remain = timeout - time.time() + start if remain <= 0: # No remaining time return None else: # Process is exiting so finalize it by calling wait process_list[idx].wait() return idx except OSError: raise WaitError else: import e3.os.unix.process # Choose between blocking or non-blocking call to wait blocking = True if timeout != 0: blocking = False while remain >= 0.0 or timeout == 0: # Retrieve first child process that ends. Note that that child # process might not be in our watch list pid = e3.os.unix.process.wait(blocking) if pid != 0: # We have a result result = next((index for index, p in enumerate(process_list) if p.pid == pid), None) if result is not None: # At the stage we need to set the process status and close # related handles. Indeed we will not be able to use the # wait method afterwards and retrieve it. process_list[result].wait() return result time.sleep(1.0) remain = timeout - time.time() + start return None