Code

Ticket #16679: fast-signals1.diff

File fast-signals1.diff, 5.4 KB (added by akaariai, 3 years ago)

Without sender -> receivers cache

Line 
1diff --git a/django/db/models/base.py b/django/db/models/base.py
2index 71fd1f7..730e52f 100644
3--- a/django/db/models/base.py
4+++ b/django/db/models/base.py
5@@ -18,6 +18,7 @@ from django.db.models.query_utils import DeferredAttribute
6 from django.db.models.deletion import Collector
7 from django.db.models.options import Options
8 from django.db.models import signals
9+from django.db.models.signals import pre_init, post_init
10 from django.db.models.loading import register_models, get_model
11 from django.utils.translation import ugettext_lazy as _
12 from django.utils.functional import curry
13@@ -277,7 +278,7 @@ class Model(object):
14     _deferred = False
15 
16     def __init__(self, *args, **kwargs):
17-        signals.pre_init.send(sender=self.__class__, args=args, kwargs=kwargs)
18+        pre_init.send(sender=self.__class__, args=args, kwargs=kwargs)
19 
20         # Set up the storage for instance state
21         self._state = ModelState()
22@@ -367,7 +368,7 @@ class Model(object):
23             if kwargs:
24                 raise TypeError("'%s' is an invalid keyword argument for this function" % kwargs.keys()[0])
25         super(Model, self).__init__()
26-        signals.post_init.send(sender=self.__class__, instance=self)
27+        post_init.send(sender=self.__class__, instance=self)
28 
29     def __repr__(self):
30         try:
31diff --git a/django/dispatch/dispatcher.py b/django/dispatch/dispatcher.py
32index ed9da57..989d86d 100644
33--- a/django/dispatch/dispatcher.py
34+++ b/django/dispatch/dispatcher.py
35@@ -31,6 +31,7 @@ class Signal(object):
36         if providing_args is None:
37             providing_args = []
38         self.providing_args = set(providing_args)
39+        self.sender_no_receivers_cache = set()
40         self.lock = threading.Lock()
41 
42     def connect(self, receiver, sender=None, weak=True, dispatch_uid=None):
43@@ -107,6 +108,7 @@ class Signal(object):
44             else:
45                 self.receivers.append((lookup_key, receiver))
46         finally:
47+            self.sender_no_receivers_cache = set()
48             self.lock.release()
49 
50     def disconnect(self, receiver=None, sender=None, weak=True, dispatch_uid=None):
51@@ -144,6 +146,7 @@ class Signal(object):
52                     del self.receivers[index]
53                     break
54         finally:
55+            self.sender_no_receivers_cache = set()
56             self.lock.release()
57 
58     def send(self, sender, **named):
59@@ -165,10 +168,10 @@ class Signal(object):
60         Returns a list of tuple pairs [(receiver, response), ... ].
61         """
62         responses = []
63-        if not self.receivers:
64+        if sender in self.sender_no_receivers_cache:
65             return responses
66 
67-        for receiver in self._live_receivers(_make_id(sender)):
68+        for receiver in self._live_receivers(sender):
69             response = receiver(signal=self, sender=sender, **named)
70             responses.append((receiver, response))
71         return responses
72@@ -197,12 +200,12 @@ class Signal(object):
73         receiver.
74         """
75         responses = []
76-        if not self.receivers:
77+        if sender in self.sender_no_receivers_cache:
78             return responses
79 
80         # Call each receiver with whatever arguments it can accept.
81         # Return a list of tuple pairs [(receiver, response), ... ].
82-        for receiver in self._live_receivers(_make_id(sender)):
83+        for receiver in self._live_receivers(sender):
84             try:
85                 response = receiver(signal=self, sender=sender, **named)
86             except Exception, err:
87@@ -211,7 +214,7 @@ class Signal(object):
88                 responses.append((receiver, response))
89         return responses
90 
91-    def _live_receivers(self, senderkey):
92+    def _live_receivers(self, sender):
93         """
94         Filter sequence of receivers to get resolved, live receivers.
95 
96@@ -219,8 +222,17 @@ class Signal(object):
97         live receivers.
98         """
99         none_senderkey = _make_id(None)
100+        senderkey = _make_id(sender)
101         receivers = []
102-
103+        # Here is a trick which I believe is safe, but I haven't actually
104+        # proved it. We store a reference to current sender_no_receivers_cache
105+        # If there are modifications to self.receivers, then the
106+        # modifier will create a new object for self.sender_no_receivers.
107+        # Thus, if we decide to store something in local_sender_no_receivers
108+        # cache using stale self.receivers info, there is no harm, because
109+        # the reference itself is stale! Of course, this needs a more
110+        # convincing comment, or maybe a proof...
111+        local_sender_no_receivers = self.sender_no_receivers_cache
112         for (receiverkey, r_senderkey), receiver in self.receivers:
113             if r_senderkey == none_senderkey or r_senderkey == senderkey:
114                 if isinstance(receiver, WEAKREF_TYPES):
115@@ -230,13 +242,14 @@ class Signal(object):
116                         receivers.append(receiver)
117                 else:
118                     receivers.append(receiver)
119+        if not receivers:
120+            local_sender_no_receivers.add(sender)
121         return receivers
122 
123     def _remove_receiver(self, receiver):
124         """
125         Remove dead receivers from connections.
126         """
127-
128         self.lock.acquire()
129         try:
130             to_remove = []
131@@ -251,6 +264,7 @@ class Signal(object):
132                     if r_key == key:
133                         del self.receivers[last_idx-idx]
134         finally:
135+            self.sender_no_receivers_cache = set()
136             self.lock.release()
137 
138