From 99bad1b0554f77138ca3273fbf5af2b1aa97d089 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Honza=20Kr=C3=A1l?= Date: Sun, 21 Nov 2010 14:58:18 +0000 Subject: [PATCH] [1.2.X] Make django signals more thread-safe. Thanks milosu for the patch! Backport of r14662 from trunk. git-svn-id: http://code.djangoproject.com/svn/django/branches/releases/1.2.X@14663 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- django/dispatch/dispatcher.py | 55 +++++++++++++++++++++++------------ 1 file changed, 37 insertions(+), 18 deletions(-) diff --git a/django/dispatch/dispatcher.py b/django/dispatch/dispatcher.py index 30245a79c5..bea9131398 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,11 +237,20 @@ 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() + +