Fixed #14533 -- Make django signals more thread-safe. Thanks milosu for the patch!

git-svn-id: http://code.djangoproject.com/svn/django/trunk@14662 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Honza Král 2010-11-21 14:54:23 +00:00
parent 14abb7c52c
commit f27e6f0dd1
1 changed files with 35 additions and 18 deletions

View File

@ -1,4 +1,5 @@
import weakref import weakref
import threading
from django.dispatch import saferef from django.dispatch import saferef
@ -30,6 +31,7 @@ class Signal(object):
if providing_args is None: if providing_args is None:
providing_args = [] providing_args = []
self.providing_args = set(providing_args) self.providing_args = set(providing_args)
self.lock = threading.Lock()
def connect(self, receiver, sender=None, weak=True, dispatch_uid=None): def connect(self, receiver, sender=None, weak=True, dispatch_uid=None):
""" """
@ -97,11 +99,15 @@ class Signal(object):
if weak: if weak:
receiver = saferef.safeRef(receiver, onDelete=self._remove_receiver) receiver = saferef.safeRef(receiver, onDelete=self._remove_receiver)
try:
self.lock.acquire()
for r_key, _ in self.receivers: for r_key, _ in self.receivers:
if r_key == lookup_key: if r_key == lookup_key:
break break
else: else:
self.receivers.append((lookup_key, receiver)) self.receivers.append((lookup_key, receiver))
finally:
self.lock.release()
def disconnect(self, receiver=None, sender=None, weak=True, dispatch_uid=None): def disconnect(self, receiver=None, sender=None, weak=True, dispatch_uid=None):
""" """
@ -130,11 +136,15 @@ class Signal(object):
else: else:
lookup_key = (_make_id(receiver), _make_id(sender)) lookup_key = (_make_id(receiver), _make_id(sender))
try:
self.lock.acquire()
for index in xrange(len(self.receivers)): for index in xrange(len(self.receivers)):
(r_key, _) = self.receivers[index] (r_key, _) = self.receivers[index]
if r_key == lookup_key: if r_key == lookup_key:
del self.receivers[index] del self.receivers[index]
break break
finally:
self.lock.release()
def send(self, sender, **named): def send(self, sender, **named):
""" """
@ -227,14 +237,21 @@ class Signal(object):
Remove dead receivers from connections. Remove dead receivers from connections.
""" """
try:
self.lock.acquire()
to_remove = [] to_remove = []
for key, connected_receiver in self.receivers: for key, connected_receiver in self.receivers:
if connected_receiver == receiver: if connected_receiver == receiver:
to_remove.append(key) to_remove.append(key)
for key in to_remove: for key in to_remove:
for idx, (r_key, _) in enumerate(self.receivers): last_idx = len(self.receivers) - 1
# enumerate in reverse order so that indexes are valid even
# after we delete some items
for idx, (r_key, _) in enumerate(reversed(self.receivers)):
if r_key == key: if r_key == key:
del self.receivers[idx] del self.receivers[last_idx-idx]
finally:
self.lock.release()
def receiver(signal, **kwargs): def receiver(signal, **kwargs):