From f27e6f0dd198309e0d965ca78bf1c5fd04a29f8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Honza=20Kr=C3=A1l?= Date: Sun, 21 Nov 2010 14:54:23 +0000 Subject: [PATCH] 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 --- django/dispatch/dispatcher.py | 53 +++++++++++++++++++++++------------ 1 file changed, 35 insertions(+), 18 deletions(-) diff --git a/django/dispatch/dispatcher.py b/django/dispatch/dispatcher.py index 3b6b72d044..9beb309ac1 100644 --- a/django/dispatch/dispatcher.py +++ b/django/dispatch/dispatcher.py @@ -1,4 +1,5 @@ import weakref +import threading from django.dispatch import saferef @@ -30,6 +31,7 @@ class Signal(object): if providing_args is None: providing_args = [] self.providing_args = set(providing_args) + self.lock = threading.Lock() def connect(self, receiver, sender=None, weak=True, dispatch_uid=None): """ @@ -97,11 +99,15 @@ class Signal(object): if weak: receiver = saferef.safeRef(receiver, onDelete=self._remove_receiver) - for r_key, _ in self.receivers: - if r_key == lookup_key: - break - else: - self.receivers.append((lookup_key, receiver)) + try: + self.lock.acquire() + for r_key, _ in self.receivers: + if r_key == lookup_key: + break + else: + self.receivers.append((lookup_key, receiver)) + finally: + self.lock.release() def disconnect(self, receiver=None, sender=None, weak=True, dispatch_uid=None): """ @@ -130,11 +136,15 @@ class Signal(object): else: lookup_key = (_make_id(receiver), _make_id(sender)) - for index in xrange(len(self.receivers)): - (r_key, _) = self.receivers[index] - if r_key == lookup_key: - del self.receivers[index] - break + try: + self.lock.acquire() + for index in xrange(len(self.receivers)): + (r_key, _) = self.receivers[index] + if r_key == lookup_key: + del self.receivers[index] + break + finally: + self.lock.release() def send(self, sender, **named): """ @@ -227,14 +237,21 @@ class Signal(object): Remove dead receivers from connections. """ - to_remove = [] - for key, connected_receiver in self.receivers: - if connected_receiver == receiver: - to_remove.append(key) - for key in to_remove: - for idx, (r_key, _) in enumerate(self.receivers): - if r_key == key: - del self.receivers[idx] + try: + self.lock.acquire() + to_remove = [] + for key, connected_receiver in self.receivers: + if connected_receiver == receiver: + to_remove.append(key) + for key in to_remove: + 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: + del self.receivers[last_idx-idx] + finally: + self.lock.release() def receiver(signal, **kwargs):