diff --git a/module/gamefree/network.py b/module/gamefree/network.py index c3fdcdd97..beb0afeb7 100644 --- a/module/gamefree/network.py +++ b/module/gamefree/network.py @@ -6,6 +6,40 @@ from module.gamefree.netpkg import * from module.logger import logger +class VmInterruptEvent: + def __init__(self): + self.true_event = threading.Event() + self.false_event = threading.Event() + self.false_event.set() + + def set_true(self): + self.true_event.set() + self.false_event.clear() + + def set_false(self): + self.true_event.clear() + self.false_event.set() + + def is_true(self): + return self.true_event.is_set() + + def is_false(self): + return self.false_event.is_set() + + def wait_until_true(self): + self.true_event.wait() + + def wait_until_false(self): + self.false_event.wait() + + def __enter__(self): + self.wait_until_true() + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + self.set_false() + + class AzurLaneNetworkClient: instance = None @@ -13,8 +47,10 @@ class AzurLaneNetworkClient: self.task_queue = Queue() self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.vm: Union[threading.Thread, None] = None - self.vm_continue = threading.Event() + self.vm_interrupt_event_queue = Queue() self.vm_exception: Union[Exception, None] = None + self.vm_exception_event = VmInterruptEvent() + self.vm_exit_request = threading.Event() def __del__(self): self.vm_stop() @@ -39,9 +75,8 @@ class AzurLaneNetworkClient: pkg.event.set() def vm_clear_task_queue(self): - self.vm_interrupt() - self.clear_task_queue() - self.vm_resume() + with self.vm_interrupt(): + self.clear_task_queue() def vm_start(self): if self.vm is not None: @@ -49,22 +84,25 @@ class AzurLaneNetworkClient: return self.vm = threading.Thread(target=self.vm_thread) self.vm.start() - self.vm_continue.set() - def vm_interrupt(self): - self.vm_continue.clear() + def vm_interrupt(self) -> VmInterruptEvent: + if self.vm_exit_request.is_set(): + raise RuntimeError("Client vm is trying to exit") + interrupt_event = VmInterruptEvent() + self.vm_interrupt_event_queue.put(interrupt_event) + return interrupt_event - def vm_resume(self): - self.vm_continue.set() + def vm_resume(self, interrupt_event): + interrupt_event.set_false() def vm_stop(self): - self.vm_interrupt() - self.clear_task_queue() - self.queue_package(AzurLaneNetworkEndPackage()) - self.vm_resume() + self.vm_exit_request.set() + with self.vm_interrupt(): + self.clear_task_queue() + self.queue_package(AzurLaneNetworkEndPackage()) def vm_wait_until_exception_handled(self): - self.vm_continue.wait() + self.vm_exception_event.wait_until_false() def vm_get_exception(self) -> Exception: return self.vm_exception @@ -75,12 +113,15 @@ class AzurLaneNetworkClient: def vm_set_exception(self, e): self.vm_exception = e - def vm_clear_exception(self): + def vm_exception_handled_and_resume(self): self.vm_exception = None + self.vm_exception_event.set_false() def vm_ensure_continue(self): - if not self.vm_continue.is_set(): - self.vm_continue.wait() + while not self.vm_interrupt_event_queue.empty(): + interrupt_event = self.vm_interrupt_event_queue.get() + interrupt_event.set_true() + interrupt_event.wait_until_false() def vm_thread(self): while 1: @@ -95,15 +136,24 @@ class AzurLaneNetworkClient: pkg.event.set() except Exception as e: self.vm_set_exception(e) - self.vm_interrupt() + self.vm_exception_event.set_true() pkg.event.set() self.vm_wait_until_exception_handled() else: logger.warning(f"Unknown net package class: {type(pkg).__name__}") def queue_package(self, pkg): + if self.vm_exit_request.is_set(): + raise RuntimeError("Client vm is trying to exit") self.task_queue.put(pkg) + def queue_packages(self, *pkgs): + if self.vm_exit_request.is_set(): + raise RuntimeError("Client vm is trying to exit") + with self.vm_interrupt(): + for i in pkgs: + self.task_queue.put(i) + @staticmethod def get_instance() -> "AzurLaneNetworkClient": if AzurLaneNetworkClient.instance is None: