def __execute(self, stdout): """Executes the command.""" # Configure the standard I/O file descriptors self.__configure_stdio(stdout) # Fork the process --> fork_lock = threading.Lock() with fork_lock: # Allocate all resources before fork() to guarantee that we will # be able to control the process execution. # Execution thread --> poll = psys.poll.Poll() try: self.__communication_thread = threading.Thread( target=self.__communication_thread_func, args=(fork_lock, poll)) self.__communication_thread.daemon = True self.__communication_thread.start() except: poll.close() raise # Execution thread <-- # Wait thread --> try: self.__termination_fd, termination_fd = os.pipe() except Exception as e: raise Error("Unable to create a pipe: {0}.", psys.e(e)) try: self.__wait_thread = threading.Thread( target=self.__wait_pid_thread, args=[fork_lock, termination_fd]) self.__wait_thread.daemon = True self.__wait_thread.start() except BaseException as error: try: eintr_retry(os.close)(termination_fd) except Exception as e: LOG.error("Unable to close a pipe: %s.", psys.e(e)) raise error # Wait thread <-- self.__pid = os.fork() if self.__pid: self.__state = _PROCESS_STATE_RUNNING else: self.__child()
def __communication_thread_func(self, fork_lock, poll): """A thread in which we communicate with the process.""" try: # Wait for fork() process completion with fork_lock: pass # Work only if we've successfully forked if self.__pid is not None: try: self.__communicate(poll) finally: self.__close() self.__state = _PROCESS_STATE_TERMINATED except Exception as e: LOG.exception("Execution thread crashed.") if self.__pid is not None: self.__error = e finally: poll.close()