From 0f134ff8bc96365148f87f4f7aa8b8c653926dac Mon Sep 17 00:00:00 2001 From: Gabriel David Pragin Date: Fri, 2 Jan 2026 12:43:21 -0800 Subject: [PATCH] Fix: Suppress expected socket errors during intentional shutdown When stop() is called, the socket/pipe thread may be blocked in recv(). Closing the socket raises OSError (Bad file descriptor on Unix, handle errors on Windows) which is expected during intentional shutdown. This commit adds a _stopping flag to both UnixSocket and WindowsSocket classes to distinguish between: - Intentional shutdown (stop() called): Suppress error logging - Unexpected disconnection (MPV crash, etc): Continue logging errors This eliminates spurious error messages during normal application shutdown while preserving visibility of actual connection problems. Fixes race condition where socket.close() in stop() causes recv() to raise OSError in the socket thread's run() method. --- python_mpv_jsonipc.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/python_mpv_jsonipc.py b/python_mpv_jsonipc.py index 1d89d50..7a4bfce 100644 --- a/python_mpv_jsonipc.py +++ b/python_mpv_jsonipc.py @@ -52,6 +52,7 @@ def __init__(self, ipc_socket, callback=None, quit_callback=None): ipc_socket = "\\\\.\\pipe\\" + ipc_socket self.callback = callback self.quit_callback = quit_callback + self._stopping = False access = _winapi.GENERIC_READ | _winapi.GENERIC_WRITE limit = 5 # Connection may fail at first. Try 5 times. @@ -75,6 +76,7 @@ def __init__(self, ipc_socket, callback=None, quit_callback=None): def stop(self, join=True): """Terminate the thread.""" + self._stopping = True if self.socket is not None: try: self.socket.close() @@ -116,7 +118,9 @@ def run(self): if self.quit_callback: self.quit_callback() except Exception as ex: - log.error("Pipe connection died.", exc_info=1) + # Only log if not intentionally stopping + if not self._stopping: + log.error("Pipe connection died.", exc_info=1) if self.quit_callback: self.quit_callback() @@ -137,6 +141,7 @@ def __init__(self, ipc_socket, callback=None, quit_callback=None): self.ipc_socket = ipc_socket self.callback = callback self.quit_callback = quit_callback + self._stopping = False self.socket = socket.socket(socket.AF_UNIX) self.socket.connect(self.ipc_socket) @@ -147,6 +152,7 @@ def __init__(self, ipc_socket, callback=None, quit_callback=None): def stop(self, join=True): """Terminate the thread.""" + self._stopping = True if self.socket is not None: try: self.socket.shutdown(socket.SHUT_WR) @@ -184,7 +190,9 @@ def run(self): self.callback(json_data) data = b'' except Exception as ex: - log.error("Socket connection died.", exc_info=1) + # Only log if not intentionally stopping + if not self._stopping: + log.error("Socket connection died.", exc_info=1) if self.quit_callback: self.quit_callback()