Skip to content

Commit

Permalink
Added Signal._cleanup_bookeeping() to prune stale bookkeeping on demand.
Browse files Browse the repository at this point in the history
  • Loading branch information
jek committed Jul 23, 2015
1 parent efe8d64 commit bee3b6e
Show file tree
Hide file tree
Showing 3 changed files with 32 additions and 0 deletions.
2 changes: 2 additions & 0 deletions CHANGES
Expand Up @@ -7,6 +7,8 @@ Version 1.4dev

- Additional bookkeeping cleanup for non-ANY connections at disconnect
time.
- Added Signal._cleanup_bookeeping() to prune stale bookkeeping on
demand

Version 1.3
-----------
Expand Down
26 changes: 26 additions & 0 deletions blinker/base.py
Expand Up @@ -348,6 +348,32 @@ def _cleanup_sender(self, sender_ref):
for receiver_id in self._by_sender.pop(sender_id, ()):
self._by_receiver[receiver_id].discard(sender_id)

def _cleanup_bookkeeping(self):
"""Prune unused sender/receiver bookeeping. Not threadsafe.
Connecting & disconnecting leave behind a small amount of bookeeping
for the receiver and sender values. Typical workloads using Blinker,
for example in most web apps, Flask, CLI scripts, etc., are not
adversely affected by this bookkeeping.
With a long-running Python process performing dynamic signal routing
with high volume- e.g. connecting to function closures, "senders" are
all unique object instances, and doing all of this over and over- you
may see memory usage will grow due to extraneous bookeeping. (An empty
set() for each stale sender/receiver pair.)
This method will prune that bookeeping away, with the caveat that such
pruning is not threadsafe. The risk is that cleanup of a fully
disconnected receiver/sender pair occurs while another thread is
connecting that same pair. If you are in the highly dynamic, unique
receiver/sender situation that has lead you to this method, that
failure mode is perhaps not a big deal for you.
"""
for mapping in (self._by_sender, self._by_receiver):
for _id, bucket in list(mapping.items()):
if not bucket:
mapping.pop(_id, None)

def _clear_state(self):
"""Throw away all signal state. Useful for unit tests."""
self._weak_senders.clear()
Expand Down
4 changes: 4 additions & 0 deletions tests/test_signals.py
Expand Up @@ -211,6 +211,10 @@ class Sender(object):
assert senders() == (1, 0)
assert receivers() == (2, 0)

sig._cleanup_bookkeeping()
assert senders() == (0, 0)
assert receivers() == (0, 0)


def test_meta_connect_failure():
def meta_received(sender, **kw):
Expand Down

0 comments on commit bee3b6e

Please sign in to comment.