summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEugene Morozov <jmv@emorozov.net>2021-11-08 09:59:14 +0300
committerGitHub <noreply@github.com>2021-11-08 08:59:14 +0200
commitbba75187931af84dd21c91bcf1b3bd422c9aed72 (patch)
tree59c256b4929623e62f90038b35687b8239bbabf8
parent4257ceb1e5b438d9e7ea4d2ac0c74609c2771749 (diff)
downloadredis-py-bba75187931af84dd21c91bcf1b3bd422c9aed72.tar.gz
Fix garbage collection deadlock (#1578)
-rw-r--r--dev_requirements.txt1
-rwxr-xr-xredis/connection.py9
-rw-r--r--tests/test_pubsub.py12
3 files changed, 19 insertions, 3 deletions
diff --git a/dev_requirements.txt b/dev_requirements.txt
index aa9d8f9..0ca7727 100644
--- a/dev_requirements.txt
+++ b/dev_requirements.txt
@@ -1,5 +1,6 @@
flake8>=3.9.2
pytest==6.2.5
+pytest-timeout==2.0.1
tox==3.24.4
tox-docker==3.1.0
invoke==1.6.0
diff --git a/redis/connection.py b/redis/connection.py
index f5d6a38..cb9acb4 100755
--- a/redis/connection.py
+++ b/redis/connection.py
@@ -10,6 +10,7 @@ import os
import socket
import threading
import warnings
+import weakref
from redis.exceptions import (
AuthenticationError,
@@ -562,7 +563,7 @@ class Connection:
pass
def register_connect_callback(self, callback):
- self._connect_callbacks.append(callback)
+ self._connect_callbacks.append(weakref.WeakMethod(callback))
def clear_connect_callbacks(self):
self._connect_callbacks = []
@@ -588,8 +589,10 @@ class Connection:
# run any user callbacks. right now the only internal callback
# is for pubsub channel/pattern resubscription
- for callback in self._connect_callbacks:
- callback(self)
+ for ref in self._connect_callbacks:
+ callback = ref()
+ if callback:
+ callback(self)
def _connect(self):
"Create a TCP socket connection"
diff --git a/tests/test_pubsub.py b/tests/test_pubsub.py
index 6a4f0aa..4be6c7a 100644
--- a/tests/test_pubsub.py
+++ b/tests/test_pubsub.py
@@ -570,3 +570,15 @@ class TestPubSubWorkerThread:
assert event.wait(timeout=1.0)
pubsub_thread.join(timeout=1.0)
assert not pubsub_thread.is_alive()
+
+
+class TestPubSubDeadlock:
+ @pytest.mark.timeout(30, method='thread')
+ def test_pubsub_deadlock(self, master_host):
+ pool = redis.ConnectionPool(host=master_host)
+ r = redis.Redis(connection_pool=pool)
+
+ for i in range(60):
+ p = r.pubsub()
+ p.subscribe("my-channel-1", "my-channel-2")
+ pool.reset()