Merge lp:~allenap/maas/events-register-unregister-during-fire into lp:~maas-committers/maas/trunk

Proposed by Gavin Panella
Status: Merged
Approved by: Gavin Panella
Approved revision: no longer in the source branch.
Merged at revision: 5435
Proposed branch: lp:~allenap/maas/events-register-unregister-during-fire
Merge into: lp:~maas-committers/maas/trunk
Diff against target: 58 lines (+20/-4)
2 files modified
src/provisioningserver/utils/events.py (+6/-4)
src/provisioningserver/utils/tests/test_events.py (+14/-0)
To merge this branch: bzr merge lp:~allenap/maas/events-register-unregister-during-fire
Reviewer Review Type Date Requested Status
Blake Rouse (community) Approve
Review via email: mp+307524@code.launchpad.net

Commit message

Event.registerHandler and Event.unregisterHandler can now be called by handlers.

Previously calling these methods from a handler would cause a "size changed during iteration" exception in Event.fire.

To post a comment you must log in.
Revision history for this message
Blake Rouse (blake-rouse) wrote :

Looks good.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/provisioningserver/utils/events.py'
2--- src/provisioningserver/utils/events.py 2015-12-01 18:12:59 +0000
3+++ src/provisioningserver/utils/events.py 2016-10-04 08:33:48 +0000
4@@ -1,4 +1,4 @@
5-# Copyright 2015 Canonical Ltd. This software is licensed under the
6+# Copyright 2015-2016 Canonical Ltd. This software is licensed under the
7 # GNU Affero General Public License version 3 (see the file LICENSE).
8
9 """Event-related utilities."""
10@@ -22,12 +22,14 @@
11
12 def unregisterHandler(self, handler):
13 """Unregister handler from event."""
14- if handler in self.handlers:
15- self.handlers.remove(handler)
16+ self.handlers.discard(handler)
17
18 def fire(self, *args, **kwargs):
19 """Fire the event."""
20- for handler in self.handlers:
21+ # Shallow-copy the set to avoid "size changed during iteration"
22+ # errors. This is a fast operation: faster than switching to an
23+ # immutable set or using thread-safe locks (and won't deadlock).
24+ for handler in self.handlers.copy():
25 handler(*args, **kwargs)
26
27
28
29=== modified file 'src/provisioningserver/utils/tests/test_events.py'
30--- src/provisioningserver/utils/tests/test_events.py 2016-06-22 17:03:02 +0000
31+++ src/provisioningserver/utils/tests/test_events.py 2016-10-04 08:33:48 +0000
32@@ -28,12 +28,26 @@
33 event.registerHandler(sentinel.handler)
34 self.assertItemsEqual([sentinel.handler], event.handlers)
35
36+ def test_registerHandler_during_fire(self):
37+ event = Event()
38+ event.registerHandler(event.registerHandler)
39+ event.fire(sentinel.otherHandler)
40+ self.assertItemsEqual(
41+ [event.registerHandler, sentinel.otherHandler],
42+ event.handlers)
43+
44 def test_unregisterHandler(self):
45 event = Event()
46 event.registerHandler(sentinel.handler)
47 event.unregisterHandler(sentinel.handler)
48 self.assertItemsEqual([], event.handlers)
49
50+ def test_unregisterHandler_during_fire(self):
51+ event = Event()
52+ event.registerHandler(event.unregisterHandler)
53+ event.fire(event.unregisterHandler)
54+ self.assertItemsEqual([], event.handlers)
55+
56 def test_fire_calls_all_handlers(self):
57 event = Event()
58 handler_one = MagicMock()