Commit f27e6f0d authored by Honza Král's avatar Honza Král
Browse files

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
parent 14abb7c5
Loading
Loading
Loading
Loading
+35 −18
Original line number Diff line number Diff line
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)

        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))
        
        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.
        """

        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:
            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:
                    del self.receivers[idx]
                        del self.receivers[last_idx-idx]
        finally:
            self.lock.release()


def receiver(signal, **kwargs):