def kill_agent_process_ids(self): pids = process_configuration.extract_all_pids(self.agent_metadata_map) for pid in pids: try: parent = psutil.Process(pid) for child in parent.children( recursive=True ): # or parent.children() for recursive=False self.logger.info(f"Killing {child.pid} (child of {pid})") try: child.kill() except psutil._exceptions.NoSuchProcess: self.logger.info("Already dead.") self.logger.info(f"Killing {pid}") try: parent.kill() except psutil._exceptions.NoSuchProcess: self.logger.info("Already dead.") except psutil.NoSuchProcess: self.logger.info("Can't fetch parent process, already dead.") except psutil.AccessDenied as ex: self.logger.error( f"Access denied when trying to kill a bot pid! {ex}") except Exception as ex: self.logger.error( f"Unexpected exception when trying to kill a bot pid! {ex}" )
def shut_down(self, time_limit=5, kill_all_pids=False, quiet=False): if not quiet: self.logger.info("Shutting Down") self.quit_event.set() end_time = datetime.now() + timedelta(seconds=time_limit) # Don't kill RLBot.exe. It needs to keep running because if we're in a GUI # that will persist after this shut down, the interface dll in charge of starting # matches is already locked in to its shared memory files, and if we start a new # RLBot.exe, those files will go stale. https://github.com/skyborgff/RLBot/issues/9 # Wait for all processes to terminate before terminating main process terminated = False while not terminated: terminated = True for callback in self.bot_quit_callbacks: if not callback.is_set(): terminated = False time.sleep(0.1) if datetime.now() > end_time: self.logger.info("Taking too long to quit, trying harder...") break self.kill_bot_processes() self.kill_agent_process_ids(set(self.script_processes.keys())) if kill_all_pids: # The original meaning of the kill_all_pids flag only applied to bots, not scripts, # so we are doing that separately. self.kill_agent_process_ids(process_configuration.extract_all_pids(self.agent_metadata_map)) self.kill_matchcomms_server() # Drain the agent_metadata_queue to make sure nothing rears its head later. while True: # will exit on queue.Empty try: metadata = self.agent_metadata_queue.get(timeout=0.1) self.logger.warn(f"Drained out metadata for {metadata.name} during shutdown!") except queue.Empty: break # The quit event can only be set once. Let's reset to our initial state self.quit_event = mp.Event() self.helper_process_manager = HelperProcessManager(self.quit_event) if not quiet: self.logger.info("Shut down complete!")
def kill_agent_process_ids(self): pids = process_configuration.extract_all_pids(self.agent_metadata_map) for pid in pids: try: parent = psutil.Process(pid) for child in parent.children(recursive=True): # or parent.children() for recursive=False self.logger.info(f"Killing {child.pid} (child of {pid})") try: child.kill() except psutil._exceptions.NoSuchProcess: self.logger.info("Already dead.") self.logger.info(f"Killing {pid}") try: parent.kill() except psutil._exceptions.NoSuchProcess: self.logger.info("Already dead.") except psutil.NoSuchProcess: self.logger.info("Can't fetch parent process, already dead.")