class RWLockTestCase(unittest.TestCase): def setUp(self): self.lock = RWLock() def test_readers(self): with self.lock.read_lock(): self.assertEqual(self.lock.readers, 1) self.assertEqual(self.lock.writers, 0) self.assertFalse(self.lock.blocked_for_readers) self.assertTrue(self.lock.blocked_for_writers) # Multiple readers can hold the lock simultaneously with self.lock.read_lock(): self.assertEqual(self.lock.readers, 2) self.assertEqual(self.lock.writers, 0) self.assertFalse(self.lock.blocked_for_readers) self.assertTrue(self.lock.blocked_for_writers) def test_writers(self): with self.lock.write_lock(): self.assertEqual(self.lock.readers, 0) self.assertEqual(self.lock.writers, 1) self.assertTrue(self.lock.blocked_for_readers) self.assertTrue(self.lock.blocked_for_writers) def test_unlocked(self): self.assertEqual(self.lock.readers, 0) self.assertEqual(self.lock.writers, 0) self.assertFalse(self.lock.blocked_for_readers) self.assertFalse(self.lock.blocked_for_writers)
class MessageHandler(ABC): """ This is the base class for all message handlers. Subclassing this provides the most flexibility but it does require the most effort to implement correctly as well. """ def __init__(self, config: configparser.ConfigParser): """ Initialise the handler. The config is provided from the configuration file, which is guaranteed to have a [handler] section. Objects of this class *MUST* be thread-safe. :param config: Contents of the configuration file """ self.config = config # Provide a lock so that our state can be protected during a reload self.lock = RWLock() # Implement initialisation as a reload self.handle_reload() def reload(self, new_config: configparser.ConfigParser): """ This is called by the server on SIGHUP so the configuration can be reloaded, caches can be cleared etc. Subclasses shouldn't overwrite this method but the handle_reload() method, which will automatically be protected with a lock. :param new_config: The new configuration after the reload """ with self.lock.write_lock(): self.config = new_config self.handle_reload() # noinspection PyMethodMayBeStatic def handle_reload(self): """ This method can be overwritten by subclasses to handle configuration reloads. """ pass @abstractmethod def handle(self, received_message: RelayServerMessage, received_over_multicast: bool) -> Message or None: """