Browse Source

[2855] Add BIND10Server.watch_fileno() method

Mukund Sivaraman 12 years ago
parent
commit
603b0180bc

+ 1 - 0
src/bin/memmgr/memmgr.py.in

@@ -43,6 +43,7 @@ class ConfigError(Exception):
 
 class Memmgr(BIND10Server):
     def __init__(self):
+        BIND10Server.__init__(self)
         # Running configurable parameters: on initial configuration this will
         # be a dict: str=>config_value.
         # This is defined as "protected" so tests can inspect it; others

+ 53 - 1
src/lib/python/isc/server_common/bind10_server.py.in

@@ -72,6 +72,11 @@ class BIND10Server:
     # Basically constant, but allow tests to override it.
     _select_fn = select.select
 
+    def __init__(self):
+        self._read_callbacks = {}
+        self._write_callbacks = {}
+        self._error_callbacks = {}
+
     @property
     def shutdown(self):
         return self.__shutdown
@@ -141,7 +146,13 @@ class BIND10Server:
         cc_fileno = self._mod_cc.get_socket().fileno()
         while not self.__shutdown:
             try:
-                (reads, _, _) = self._select_fn([cc_fileno], [], [])
+                read_fds = list(self._read_callbacks.keys())
+                read_fds.append(cc_fileno)
+                write_fds = list(self._write_callbacks.keys())
+                error_fds = list(self._error_callbacks.keys())
+
+                (reads, writes, errors) = \
+                    self._select_fn(read_fds, write_fds, error_fds)
             except select.error as ex:
                 # ignore intterruption by signal; regard other select errors
                 # fatal.
@@ -149,6 +160,22 @@ class BIND10Server:
                     continue
                 else:
                     raise
+
+            for fileno in reads:
+                if fileno in self._read_callbacks:
+                    for callback in self._read_callbacks[fileno]:
+                        callback()
+
+            for fileno in writes:
+                if fileno in self._write_callbacks:
+                    for callback in self._write_callbacks[fileno]:
+                        callback()
+
+            for fileno in errors:
+                if fileno in self._error_callbacks:
+                    for callback in self._error_callbacks[fileno]:
+                        callback()
+
             if cc_fileno in reads:
                 # this shouldn't raise an exception (if it does, we'll
                 # propagate it)
@@ -175,6 +202,31 @@ class BIND10Server:
         """The default implementation of the module specific initilization"""
         pass
 
+    def watch_fileno(self, fileno, rcallback=None, wcallback=None, \
+                         xcallback=None):
+        """Register the fileno for the internal select() call.
+
+        *callback's are callable objects which would be called when
+        read, write, error events occur on the specified fileno.
+        """
+        if rcallback is not None:
+            if fileno in self._read_callbacks:
+                self._read_callbacks[fileno].append(rcallback)
+            else:
+                self._read_callbacks[fileno] = [rcallback]
+
+        if wcallback is not None:
+            if fileno in self._write_callbacks:
+                self._write_callbacks[fileno].append(wcallback)
+            else:
+                self._write_callbacks[fileno] = [wcallback]
+
+        if xcallback is not None:
+            if fileno in self._error_callbacks:
+                self._error_callbacks[fileno].append(xcallback)
+            else:
+                self._error_callbacks[fileno] = [xcallback]
+
     def run(self, module_name):
         """Start the server and let it run until it's told to stop.
 

+ 32 - 0
src/lib/python/isc/server_common/tests/bind10_server_test.py

@@ -62,6 +62,7 @@ class MyCCSession(MockModuleCCSession, isc.config.ConfigData):
 
 class MockServer(BIND10Server):
     def __init__(self):
+        BIND10Server.__init__(self)
         self._select_fn = self.select_wrapper
 
     def _setup_ccsession(self):
@@ -90,6 +91,9 @@ class MockServer(BIND10Server):
 class TestBIND10Server(unittest.TestCase):
     def setUp(self):
         self.__server = MockServer()
+        self.__reads = 0
+        self.__writes = 0
+        self.__errors = 0
 
     def test_init(self):
         """Check initial conditions"""
@@ -246,6 +250,34 @@ class TestBIND10Server(unittest.TestCase):
         # others will notice it due to connection reset.
         self.assertFalse(self.__server.mod_ccsession.stopped)
 
+    def my_read_callback(self):
+        self.__reads += 1
+
+    def my_write_callback(self):
+        self.__writes += 1
+
+    def my_error_callback(self):
+        self.__errors += 1
+
+    def test_watch_fileno(self):
+        """Test watching for fileno."""
+        self.select_params = []
+        self.__server._select_fn = \
+            lambda r, w, e: self.select_wrapper(r, w, e,
+                                                ret=([10, 20, 42, TEST_FILENO], [], [30]))
+        self.__server._setup_ccsession()
+
+        self.__server.watch_fileno(10, rcallback=self.my_read_callback)
+        self.__server.watch_fileno(20, rcallback=self.my_read_callback, \
+                                       wcallback=self.my_write_callback)
+        self.__server.watch_fileno(30, xcallback=self.my_error_callback)
+
+        self.__server._run_internal()
+        self.assertEqual([([10, 20, TEST_FILENO], [20], [30])], self.select_params)
+        self.assertEqual(2, self.__reads)
+        self.assertEqual(0, self.__writes)
+        self.assertEqual(1, self.__errors)
+
 if __name__== "__main__":
     isc.log.init("bind10_server_test")
     isc.log.resetUnitTestRootLogger()