def is_running(pid): """Check whether a process with the given pid is running. :param pid: an integer (e.g the value of Run().pid) :type pid: int :rtype: bool """ if sys.platform == 'win32': # unix: no cover from e3.os.windows.native_api import Access, NT from e3.os.windows.process import process_exit_code handle = NT.OpenProcess(Access.PROCESS_QUERY_INFORMATION, False, pid) try: if not handle: return False return process_exit_code(handle) is None finally: NT.Close(handle) else: # windows: no cover try: # We send a null signal to check the validity of pid os.kill(pid, 0) except OSError as e: # If the process is not found, errno will be set to ESRCH return e.errno != errno.ESRCH return True
def is_running(pid): """Check whether a process with the given pid is running. :param pid: an integer (e.g the value of Run().pid) :type pid: int :rtype: bool """ if sys.platform == 'win32': # unix: no cover from e3.os.windows.native_api import Access, NT from e3.os.windows.process import process_exit_code handle = NT.OpenProcess(Access.PROCESS_QUERY_INFORMATION, False, pid) try: if handle == 0: return False return process_exit_code(handle) is None finally: NT.Close(handle) else: try: # We send a null signal to check the validity of pid os.kill(pid, 0) except OSError as e: # If the process is not found, errno will be set to ESRCH return e.errno != errno.ESRCH return True
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 test_invalid_handle(): with pytest.raises(WindowsError): process_exit_code(42)
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