From f7be18ba58931e157c8f433bcf4e10bcbec55d2d Mon Sep 17 00:00:00 2001 From: donmahallem Date: Wed, 16 Apr 2025 11:05:20 +0200 Subject: [PATCH 1/4] mostly fixes deadlock situation with KeyboardInterupt termination in some cases this blocked (probably race condition with _ready) it blocked the lock in send --- pyghthouse/connection/wsconnector.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/pyghthouse/connection/wsconnector.py b/pyghthouse/connection/wsconnector.py index d451f0a..0e6c0a8 100644 --- a/pyghthouse/connection/wsconnector.py +++ b/pyghthouse/connection/wsconnector.py @@ -39,11 +39,9 @@ def start(self): self.ws = WebSocketApp(self.address, on_message=None if self.on_msg is None else self._handle_msg, on_open=self._ready, on_error=self._fail) - self.lock.acquire() + self.lock.acquire() # wait for connection to be established kwargs = {"sslopt": {"cert_reqs": CERT_NONE}} if self.ignore_ssl_cert else None Thread(target=self.ws.run_forever, kwargs=kwargs).start() - self.lock.acquire() # wait for connection to be established - self.lock.release() def _fail(self, ws, err): self.lock.release() From 9f07bb837fb5987258db45e6e40771a7510a1917 Mon Sep 17 00:00:00 2001 From: donmahallem Date: Wed, 16 Apr 2025 11:22:58 +0200 Subject: [PATCH 2/4] another race condition based on initial delay to connect --- pyghthouse/connection/wsconnector.py | 19 ++++++++++++++++--- pyghthouse/ph.py | 6 ++++-- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/pyghthouse/connection/wsconnector.py b/pyghthouse/connection/wsconnector.py index 0e6c0a8..85477f9 100644 --- a/pyghthouse/connection/wsconnector.py +++ b/pyghthouse/connection/wsconnector.py @@ -1,9 +1,15 @@ +from enum import Enum from threading import Thread, Lock from websocket import WebSocketApp, setdefaulttimeout, ABNF from msgpack import packb, unpackb from ssl import CERT_NONE +class ConnectionState(Enum): + NONE=0 + CONNECTING=1 + CONNECTED=2 + FAILED=3 class WSConnector: class REID: @@ -26,7 +32,7 @@ def __init__(self, username: str, token: str, address: str, on_msg=None, ignore_ self.ws = None self.lock = Lock() self.reid = self.REID() - self.running = False + self.__connection_state = ConnectionState.NONE self.ignore_ssl_cert = ignore_ssl_cert setdefaulttimeout(60) @@ -40,10 +46,13 @@ def start(self): on_message=None if self.on_msg is None else self._handle_msg, on_open=self._ready, on_error=self._fail) self.lock.acquire() # wait for connection to be established + self.__connection_state = ConnectionState.CONNECTING kwargs = {"sslopt": {"cert_reqs": CERT_NONE}} if self.ignore_ssl_cert else None Thread(target=self.ws.run_forever, kwargs=kwargs).start() + def _fail(self, ws, err): + self.__connection_state = ConnectionState.FAILED self.lock.release() raise err @@ -51,15 +60,19 @@ def stop(self): if self.ws is not None: with self.lock: print("Closing the connection.") - self.running = False + self.__connection_state = ConnectionState.NONE self.ws.close() self.ws = None def _ready(self, ws): print(f"Connected to {self.address}.") - self.running = True + self.__connection_state = ConnectionState.CONNECTED self.lock.release() + @property + def connection_state(self): + return self.__connection_state + def _handle_msg(self, ws, msg): if isinstance(msg, bytes): msg = unpackb(msg) diff --git a/pyghthouse/ph.py b/pyghthouse/ph.py index 62f92ea..8b7e225 100644 --- a/pyghthouse/ph.py +++ b/pyghthouse/ph.py @@ -6,7 +6,7 @@ import numpy as np from pyghthouse.data.canvas import PyghthouseCanvas -from pyghthouse.connection.wsconnector import WSConnector +from pyghthouse.connection.wsconnector import WSConnector,ConnectionState class VerbosityLevel(Enum): @@ -208,10 +208,12 @@ def connect(self): self.connector.start() def start(self): - if not self.connector.running: + if not self.connector.connection_state==ConnectionState.CONNECTED: self.connect() self.stop() self.msg_handler.reset() + while not self.connector.connection_state==ConnectionState.CONNECTED: + sleep(100) self.ph_thread = self.PHThread(self) self.ph_thread.start() From 15ed248c393a4078c53ed12c8cbc36bb12f8198b Mon Sep 17 00:00:00 2001 From: donmahallem Date: Wed, 16 Apr 2025 14:17:07 +0200 Subject: [PATCH 3/4] add case where connection fails --- pyghthouse/ph.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/pyghthouse/ph.py b/pyghthouse/ph.py index 8b7e225..cca0136 100644 --- a/pyghthouse/ph.py +++ b/pyghthouse/ph.py @@ -212,7 +212,14 @@ def start(self): self.connect() self.stop() self.msg_handler.reset() - while not self.connector.connection_state==ConnectionState.CONNECTED: + while True: + state=self.connector.connection_state + if state==ConnectionState.CONNECTED: + # Connected + break + elif state==ConnectionState.FAILED: + # Connection failed + raise ConnectionError("Failed to connect") sleep(100) self.ph_thread = self.PHThread(self) self.ph_thread.start() From c7c12c3f6a8b3b2c9c1d9cfa3ffafdebe2c2d6bd Mon Sep 17 00:00:00 2001 From: donmahallem Date: Wed, 16 Apr 2025 14:21:50 +0200 Subject: [PATCH 4/4] name anonymous threads --- pyghthouse/connection/wsconnector.py | 4 ++-- pyghthouse/ph.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pyghthouse/connection/wsconnector.py b/pyghthouse/connection/wsconnector.py index 85477f9..e9ca550 100644 --- a/pyghthouse/connection/wsconnector.py +++ b/pyghthouse/connection/wsconnector.py @@ -48,8 +48,8 @@ def start(self): self.lock.acquire() # wait for connection to be established self.__connection_state = ConnectionState.CONNECTING kwargs = {"sslopt": {"cert_reqs": CERT_NONE}} if self.ignore_ssl_cert else None - Thread(target=self.ws.run_forever, kwargs=kwargs).start() - + Thread(target=self.ws.run_forever, name="Websocket Thread", kwargs=kwargs).start() + def _fail(self, ws, err): self.__connection_state = ConnectionState.FAILED diff --git a/pyghthouse/ph.py b/pyghthouse/ph.py index cca0136..06d755c 100644 --- a/pyghthouse/ph.py +++ b/pyghthouse/ph.py @@ -166,7 +166,7 @@ def print_warning(msg): class PHThread(Thread): def __init__(self, parent): - super().__init__() + super().__init__(name = "PHThread") self.parent = parent self._stop_event = Event()