Changeset 60e401c in trunk


Ignore:
Timestamp:
2020-12-16T21:19:33Z (4 years ago)
Author:
Jean-Paul Calderone <exarkun@…>
Branches:
master
Children:
b2c9296
Parents:
3513e9b
Message:

Make ObserverList? synchronous, reentrant, and exception safe

with tests

Location:
src/allmydata
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • TabularUnified src/allmydata/test/test_observer.py

    r3513e9b r60e401c  
    102102        d.addCallback(_check2)
    103103        return d
     104
     105    def test_observer_list_reentrant(self):
     106        """
     107        ``ObserverList`` is reentrant.
     108        """
     109        observed = []
     110
     111        def observer_one():
     112            obs.unsubscribe(observer_one)
     113
     114        def observer_two():
     115            observed.append(None)
     116
     117        obs = observer.ObserverList()
     118        obs.subscribe(observer_one)
     119        obs.subscribe(observer_two)
     120        obs.notify()
     121
     122        self.assertEqual([None], observed)
     123
     124    def test_observer_list_observer_errors(self):
     125        """
     126        An error in an earlier observer does not prevent notification from being
     127        delivered to a later observer.
     128        """
     129        observed = []
     130
     131        def observer_one():
     132            raise Exception("Some problem here")
     133
     134        def observer_two():
     135            observed.append(None)
     136
     137        obs = observer.ObserverList()
     138        obs.subscribe(observer_one)
     139        obs.subscribe(observer_two)
     140        obs.notify()
     141
     142        self.assertEqual([None], observed)
     143        self.assertEqual(1, len(self.flushLoggedErrors(Exception)))
  • TabularUnified src/allmydata/util/observer.py

    r3513e9b r60e401c  
    1717from twisted.internet import defer
    1818from foolscap.api import eventually
     19from twisted.logger import (
     20    Logger,
     21)
    1922
    2023"""The idiom we use is for the observed object to offer a method named
     
    98101
    99102class ObserverList(object):
    100     """A simple class to distribute events to a number of subscribers."""
     103    """
     104    Immediately distribute events to a number of subscribers.
     105    """
     106    _logger = Logger()
    101107
    102108    def __init__(self):
     
    110116
    111117    def notify(self, *args, **kwargs):
    112         for o in self._watchers:
    113             eventually(o, *args, **kwargs)
     118        for o in self._watchers[:]:
     119            try:
     120                o(*args, **kwargs)
     121            except:
     122                self._logger.failure("While notifying {o!r}", o=o)
    114123
    115124class EventStreamObserver(object):
Note: See TracChangeset for help on using the changeset viewer.