Browse Source

[1427] The socket in cache

Michal 'vorner' Vaner 13 years ago
parent
commit
6a4afc2165

+ 38 - 2
src/lib/python/isc/bind10/socket_cache.py

@@ -17,6 +17,8 @@
 Here's the cache for sockets from socket creator.
 """
 
+import os
+
 class SocketError(Exception):
     """
     Exception raised when the socket creator is unable to create requested
@@ -33,6 +35,40 @@ class ShareError(Exception):
     """
     pass
 
+class Socket:
+    """
+    This represents one socket cached by the cache program. This should never
+    be used directly by a user, it is used internally by the Cache. Therefore
+    many member variables are used directly instead of by a accessor method.
+
+    Be warned that this object implements the __del__ method. It closes the
+    socket held inside in it. But this poses various problems with garbage
+    collector. In short, do not make reference cycles with this and generally
+    leave this class alone to live peacefully.
+    """
+    def __init__(self, protocol, address, port, fileno):
+        """
+        Creates the socket.
+
+        The protocol, address and port are preserved for the information.
+        """
+        self.protocol = protocol
+        self.address = address
+        self.port = port
+        self.fileno = fileno
+        # Mapping from token -> application
+        self.active_tokens = {}
+        # The tokens which were not yet picked up
+        self.waiting_tokens = set()
+        # Share modes and names by the tokens (token -> (mode, name))
+        self.shares = {}
+
+    def __del__(self):
+        """
+        Closes the file descriptor.
+        """
+        os.close(self.fileno)
+
 class Cache:
     """
     This is the cache for sockets from socket creator. The purpose of cache
@@ -67,8 +103,8 @@ class Cache:
         # This format is the same as above, but for the tokens that were
         # already picked up by the application and not yet released.
         self._active_tokens = {}
-        # This is a dict from applications to list of sockets, for the
-        # sockets already picked up by an application
+        # This is a dict from applications to set of tokens used by the
+        # application, for the sockets already picked up by an application
         self._active_apps = {}
         # The sockets live here to be indexed by address and subsequently
         # by port

+ 64 - 1
src/lib/python/isc/bind10/tests/socket_cache_test.py

@@ -16,8 +16,70 @@
 import unittest
 import isc.log
 import isc.bind10.socket_cache
+from isc.net.addr import IPAddr
+import os
 
-class SocketCacheTest(unittest.TestCase):
+class Test(unittest.TestCase):
+    """
+    Base for the tests here. It replaces the os.close method.
+    """
+    def setUp(self):
+        self._closes = []
+        isc.bind10.socket_cache.os.close = self.__close
+
+    def tearDown(self):
+        # This is not very clean solution. But when the test stops
+        # to exist, the method must not be used to destroy the
+        # object any more. And we can't restore the os.close here
+        # as we never work with real sockets here.
+        isc.bind10.socket_cache.os.close = lambda fd: None
+
+    def __close(self, fd):
+        """
+        Just log a close was called.
+        """
+        self._closes.append(fd)
+
+class SocketTest(Test):
+    """
+    Test for the Socket class.
+    """
+    def setUp(self):
+        """
+        Creates the socket to be tested.
+
+        It has 'Test' as the protocol , which means the
+        fileno file descriptor will not be closed on deletion.
+
+        It also creates other useful test variables.
+        """
+        Test.setUp(self)
+        self.__address = IPAddr("192.0.2.1")
+        self.__socket = isc.bind10.socket_cache.Socket('Test', self.__address,
+                                                       1024, 42)
+
+    def test_init(self):
+        """
+        Checks the intrnals of the cache just after the creation.
+        """
+        self.assertEqual('Test', self.__socket.protocol)
+        self.assertEqual(self.__address, self.__socket.address)
+        self.assertEqual(1024, self.__socket.port)
+        self.assertEqual(42, self.__socket.fileno)
+        self.assertEqual({}, self.__socket.active_tokens)
+        self.assertEqual({}, self.__socket.shares)
+        self.assertEqual(set(), self.__socket.waiting_tokens)
+
+    def test_del(self):
+        """
+        Check it closes the socket when removed.
+        """
+        # This should make the refcount 0 and call the descructor
+        # right away
+        self.__socket = None
+        self.assertEqual([42], self._closes)
+
+class SocketCacheTest(Test):
     """
     Some tests for the isc.bind10.socket_cache.Cache.
 
@@ -28,6 +90,7 @@ class SocketCacheTest(unittest.TestCase):
         """
         Creates the cache for tests with us being the socket creator.
         """
+        Test.setUp(self)
         self.__cache = isc.bind10.socket_cache.Cache(self)
 
     def test_init(self):