#35801: Signals are dispatched to receivers associated with dead senders
-------------------------+----------------------------------------
     Reporter:  bobince  |                     Type:  Bug
       Status:  new      |                Component:  Core (Other)
      Version:  5.1      |                 Severity:  Normal
     Keywords:           |             Triage Stage:  Unreviewed
    Has patch:  0        |      Needs documentation:  0
  Needs tests:  0        |  Patch needs improvement:  0
Easy pickings:  0        |                    UI/UX:  0
-------------------------+----------------------------------------
 django.dispatch.Signal.receivers is keyed on the id() of the receiver and
 sender (modulo a thing to make bound methods consistent).

 If a sender connected to a signal is destroyed, and a new object is
 allocated with the same id() and then also connected as a sender to the
 signal, when the signal fires it will match the original sender and call
 the receiver that was connected for that sender (as well as the new one).

 Signal works around the problem of re-used ids for the receiver by having
 a weakref to the receiver (since it needs a reference anyway to be able to
 call it), but it doesn't for sender.

 In my case this resulted in post-migrate hooks for the wrong apps being
 occasionally called in migration tests that mutated INSTALLED_APPS causing
 AppConfig senders to be re-created, but it can be more simply provoked
 with:

 {{{
 from django.dispatch import Signal

 receivers_called = []
 def create_receiver(i):
     def receiver(**kwargs):
         receivers_called.append(i)
     return receiver

 n = 100
 receivers = [create_receiver(i) for i in range(n)]

 signal = Signal()
 for i in range(n):
     sender = object()
     signal.connect(receivers[i], sender=sender)
     receivers_called = []
     _ = signal.send(sender=sender)
     # only the receiver for the new sender object should be called
     assert receivers_called == [i], f'Expected [{i}], called
 {receivers_called}'
 }}}

 (how readily this explodes may depend on local memory allocation
 differences, but it dies pretty consistently for me on iteratio
 n 3.)

 Perhaps Signal should be keeping a weakref to each sender as well as
 receiver, and detecting when it has gone None? Does this need to
 participate in the _dead_receivers mechanism?
-- 
Ticket URL: <https://code.djangoproject.com/ticket/35801>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

-- 
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-updates/01070192386d14e9-1f493940-7c11-4a3a-a125-fffbd6152298-000000%40eu-central-1.amazonses.com.

Reply via email to