From 3053abc89fbc09368b4d4a70d55f633df0516030 Mon Sep 17 00:00:00 2001 From: Markus Gerstel Date: Fri, 21 Apr 2023 16:16:48 +0100 Subject: [PATCH] Fix potential race condition during disconnection When the disconnect() function is called twice in parallel it is possible that one thread deletes the self._sock reference, while the other thread will attempt to call .close() on it, leading to an AttributeError. This situation can routinely be encountered by closing the connection in a PubSubWorkerThread error handler in a blocking thread (ie. with sleep_time==None), and then calling .close() on the PubSub object. The main thread will then run into the disconnect() function, and the listener thread is woken up by the closure and will race into the disconnect() function, too. This can be fixed easily by copying the object reference before doing the None-check, similar to what we do in the redis.client.close() function. --- redis/connection.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/redis/connection.py b/redis/connection.py index faea7683f7..95a96b4af6 100644 --- a/redis/connection.py +++ b/redis/connection.py @@ -778,20 +778,22 @@ def on_connect(self): def disconnect(self, *args): "Disconnects from the Redis server" self._parser.on_disconnect() - if self._sock is None: + + conn_sock = self._sock + self._sock = None + if conn_sock is None: return if os.getpid() == self.pid: try: - self._sock.shutdown(socket.SHUT_RDWR) + conn_sock.shutdown(socket.SHUT_RDWR) except OSError: pass try: - self._sock.close() + conn_sock.close() except OSError: pass - self._sock = None def _send_ping(self): """Send PING, expect PONG in return"""