def run(self, _id: int, verbose: bool = False, timeout: int = 60000, n_games: int = 100): # hostname = f"{socket.gethostname()}_{time.ctime().replace(' ', '-')}" hostname = f"{socket.gethostname()}_{_id}_{time.time()}" address = f"tcp://{self.args.attach}:{self.args.frontend_port}" context = zmq.Context() error_sock = context.socket(zmq.REQ) error_sock.connect(address) while True: try: # alive_event: # 게임이 인스턴스 재시작 없이 재시작 할 때마다 set # 게임이 정상적으로 재시작하고 있다는 의미 alive_event = mp.Event() # req_kill_event: # 게임 프로세스 내부에서 외부에 재시작을 요청할 때 set # n_games 만큼 게임을 플레이 한 뒤에 set해서 # 게임 프로세스 내부에 문제로 인해 프로세스 종료가 안되더라도, # 외부에서 강제로 종료할 수 있도록 event 전달 req_kill_event = mp.Event() # exc_queue = mp.Queue() def play_game(hostname, address, n_games, alive_event, req_kill_event, exc_queue): # n_games: # 게임 인스턴스를 한번 실행해서 연속으로 몇 번이나 게임을 실행할 것인가? # - 너무 작으면, 게임 인스턴스를 자주 재시작해야하기 때문에 속도가 느려짐 # - 너무 크면, 예외 상황이 발생할 가능성이 높음 # sync_event: # host (게임 서버)와 join (클라이언트) 사이의 동기를 맞추기 위해 사용 # host가 learner에게 전달받은 대로 게임 세팅 설정한 뒤에 set을 해서, # join이 다음 단계를 진행할 수 있도록 함 sync_event = asyncio.Event() portconfig = Portconfig() context = zmq.Context() sock = context.socket(zmq.REQ) sock.RCVTIMEO = timeout # zmq 시간제한 설정ㄴ sock.connect(address) # task_dict & players: # 게임 세팅 관련변수, # host와 join이 동일한 reference를 가지고 있어야 함 task_dict = dict(step_interval=self.args.step_interval) players = [None, None] asyncio.get_event_loop().run_until_complete( asyncio.gather( Actor._host_game(hostname, sock, task_dict, players, sync_event, alive_event, req_kill_event, exc_queue, n_games=n_games, realtime=False, portconfig=portconfig), Actor._join_game(task_dict, players, sync_event, alive_event, req_kill_event, exc_queue, n_games=n_games, realtime=False, portconfig=portconfig))) if self.args.game_timeout < 0: # 테스트 용: # play_game 기능 테스트할 때, 최대 게임시간을 음수로 설정하면, # play_game 함수 직접 실행 play_game(hostname, address, n_games, alive_event, req_kill_event, exc_queue) else: # 일반적인 상황에서는 play_game을 자식 프로세스로 실행 # 자식 프로세스 내부에서 종료를 요청(req_kill_event)하거나, # 현재 프로세스에서 제한시간마다 게임이 새로 시작하는지 검사해서, # 게임이 새로 시작하지 않으면(alive_event), 자식프로세스 재시작 game_play_proc = mp.Process( target=play_game, args=(hostname, address, n_games, alive_event, req_kill_event, exc_queue), daemon=False, ) game_play_proc.start() running = True checkpoint = time.monotonic() while running: # 게임 프로세스(자식 프로세스)가 정상적으로 작동 중인지 확인 if req_kill_event.is_set(): # 게임 프로세스에서 종료 요청 running = False if time.monotonic( ) - checkpoint > self.args.game_timeout: if alive_event.is_set(): # 제한 시간 이전에 게임 프로세스 내부에서 게임 재시작 확인 checkpoint = time.monotonic() alive_event.clear() else: running = False while exc_queue.qsize() > 0: # 자식 프로세스에서 발행한 에러 메시지를 Learner에 전달 self.log_exception(hostname, error_sock, exc_queue.get()) time.sleep(1) # 게임 프로세스 종료 시도 - sig.TERM if game_play_proc.is_alive(): for _ in range(3): game_play_proc.terminate() time.sleep(0.5) if not game_play_proc.is_alive(): break # 게임 프로세스 종료 시도 - sig.KILL if game_play_proc.is_alive(): game_play_proc.kill() game_play_proc.close() # 게임 프로세스 종료 시도 - psutil if game_play_proc.is_alive(): kill_children_processes(game_play_proc.pid, including_parent=True) except Exception as exc: import traceback traceback.print_exc() self.log_exception(hostname, error_sock, traceback.format_exc()) if self.args.halt_at_exception: # 테스트용: 예외가 발생했을 때 멈춰 있도록 하기 위한 용도 embed() try: kill_children_processes(including_parent=False) except OSError: # 테스트용: 예외가 발생했을 때 멈춰 있도록 하기 위한 용도 # 매우 드물게 OSError가 발생할 수 있는데, 원인은 불확실 함 traceback.print_exc() embed() pass
cprint(f'[FUNC. UPDATE] {new_task}', 'yellow') self.tasks[i] = new_task if __name__ == '__main__': mp.freeze_support() # for Windows support from .envs import Actor, Environment args = parse_arguments() # TODO: numpy seed # TODO: torch seed if args.attach is None: # os.system("kill -9 $(ps ax | grep SC2_x64 | fgrep -v grep | awk '{ print $1 }')") Trainer(args).run() else: if args.n_actors <= 1: Actor(args).run(0) else: actors = [Actor(args) for _ in range(args.n_actors)] ps = [ mp.Process(target=actor.run, args=(i, ), daemon=False) for i, actor in enumerate(actors) ] [p.start() for p in ps] [p.join() for p in ps] kill_children_processes(including_parent=False)
def run(self, _id: int, verbose: bool=False, timeout: int=60000, n_games: int=100): # hostname = f"{socket.gethostname()}_{time.ctime().replace(' ', '-')}" hostname = f"{socket.gethostname()}_{_id}_{time.time()}" address = f"tcp://{self.args.attach}:{self.args.frontend_port}" context = zmq.Context() error_sock = context.socket(zmq.REQ) error_sock.connect(address) while True: try: # alive_event: # 게임이 인스턴스 재시작 없이 재시작 할 때마다 set # 게임이 정상적으로 재시작하고 있다는 의미 alive_event = mp.Event() # req_kill_event: # 게임 프로세스 내부에서 외부에 재시작을 요청할 때 set # n_games 만큼 게임을 플레이 한 뒤에 set해서 # 게임 프로세스 내부에 문제로 인해 프로세스 종료가 안되더라도, # 외부에서 강제로 종료할 수 있도록 event 전달 req_kill_event = mp.Event() # exc_queue = mp.Queue() ##play_game이 정의되어 있던 자리 if self.args.game_timeout < 0: # 테스트 용: # play_game 기능 테스트할 때, 최대 게임시간을 음수로 설정하면, # play_game 함수 직접 실행 ## args 인자에 timeout과 self 추가 play_game(hostname, address, n_games, alive_event, req_kill_event, exc_queue, timeout, self) else: # 일반적인 상황에서는 play_game을 자식 프로세스로 실행 # 자식 프로세스 내부에서 종료를 요청(req_kill_event)하거나, # 현재 프로세스에서 제한시간마다 게임이 새로 시작하는지 검사해서, # 게임이 새로 시작하지 않으면(alive_event), 자식프로세스 재시작 ## args 인자에 timeout과 self 추가 game_play_proc = mp.Process( target=play_game, args=(hostname, address, n_games, alive_event, req_kill_event, exc_queue, timeout, self), daemon=False, ) game_play_proc.start() running = True checkpoint = time.monotonic() while running: # 게임 프로세스(자식 프로세스)가 정상적으로 작동 중인지 확인 if req_kill_event.is_set(): # 게임 프로세스에서 종료 요청 running = False if time.monotonic() - checkpoint > self.args.game_timeout: if alive_event.is_set(): # 제한 시간 이전에 게임 프로세스 내부에서 게임 재시작 확인 checkpoint = time.monotonic() alive_event.clear() else: running = False while exc_queue.qsize() > 0: # 자식 프로세스에서 발행한 에러 메시지를 Learner에 전달 self.log_exception(hostname, error_sock, exc_queue.get()) time.sleep(1) # 게임 프로세스 종료 시도 - sig.TERM if game_play_proc.is_alive(): for _ in range(3): game_play_proc.terminate() time.sleep(0.5) if not game_play_proc.is_alive(): break # 게임 프로세스 종료 시도 - sig.KILL if game_play_proc.is_alive(): game_play_proc.kill() game_play_proc.close() # 게임 프로세스 종료 시도 - psutil if game_play_proc.is_alive(): kill_children_processes(game_play_proc.pid, including_parent=True) except Exception as exc: import traceback traceback.print_exc() self.log_exception(hostname, error_sock, traceback.format_exc()) if self.args.halt_at_exception: # 테스트용: 예외가 발생했을 때 멈춰 있도록 하기 위한 용도 embed() try: kill_children_processes(including_parent=False) except OSError: # 테스트용: 예외가 발생했을 때 멈춰 있도록 하기 위한 용도 # 매우 드물게 OSError가 발생할 수 있는데, 원인은 불확실 함 traceback.print_exc() embed() pass