From e6b1aeb5a6f51c6cf1b19d89839a9e2f7e6a2a11 Mon Sep 17 00:00:00 2001 From: Anna Bocharova Date: Sat, 26 Aug 2023 16:58:46 +0200 Subject: [PATCH] Introducing UI update lock. (#186) * Introducing UI update lock. * Using actual threading Lock. * Fix import order. * Using lock as a context manager (with). * Add logging for the lock. * Avoid calling UI update fn from the polling thread when it's already running. * Minor: comments. * Update octoprint_octorelay/__init__.py * test_input_polling__locked. * Asserting the released lock in test_update_ui. --- octoprint_octorelay/__init__.py | 5 +++++ tests/test_init.py | 10 ++++++++++ 2 files changed, 15 insertions(+) diff --git a/octoprint_octorelay/__init__.py b/octoprint_octorelay/__init__.py index f68f559b..109b3471 100755 --- a/octoprint_octorelay/__init__.py +++ b/octoprint_octorelay/__init__.py @@ -40,6 +40,7 @@ def __init__(self): self.polling_timer = None self.tasks = [] # of Task self.model = { index: {} for index in RELAY_INDEXES } + self.ui_update_lock = None def get_settings_version(self): return SETTINGS_VERSION @@ -263,6 +264,7 @@ def reducer(agg, task): ) def update_ui(self): + self.ui_update_lock = True # issue 186 self._logger.debug("Updating the UI") settings = self._settings.get([], merged=True) # expensive upcoming = self.get_upcoming_tasks(filter( @@ -290,6 +292,7 @@ def update_ui(self): "deadline": int(upcoming[index].deadline * 1000) # ms for JS } } + self.ui_update_lock = None # issue 186, once model is updated, the lock can be released self._logger.debug(f"The UI feed: {self.model}") self._plugin_manager.send_plugin_message(self._identifier, self.model) @@ -315,6 +318,8 @@ def get_update_information(self): # Polling thread def input_polling(self): # self._logger.debug("input_polling") # in case your log file is too small + if self.ui_update_lock: + return # issue 186, avoid the update during the another one for index in RELAY_INDEXES: active = self.model[index]["active"] model_state = self.model[index]["relay_state"] # bool since v3.1 diff --git a/tests/test_init.py b/tests/test_init.py index 4c0f503f..576a87d6 100644 --- a/tests/test_init.py +++ b/tests/test_init.py @@ -460,6 +460,15 @@ def test_input_polling(self): self.plugin_instance.update_ui.assert_called_with() self.plugin_instance._logger.debug.assert_called_with("relay: r3 has changed its pin state") + def test_input_polling__locked(self): + # Should return immediately when the UI update is locked + self.plugin_instance.ui_update_lock = True + self.plugin_instance.update_ui = Mock() + relayConstructorMock.reset_mock() + self.plugin_instance.input_polling() + self.plugin_instance.update_ui.assert_not_called() + relayConstructorMock.assert_not_called() + def test_update_ui(self): # Should send message via plugin manager containing actual settings and the relay state cases = [ @@ -501,6 +510,7 @@ def test_update_ui(self): self.plugin_instance._plugin_manager.send_plugin_message.assert_called_with( "MockedIdentifier", expected_model ) + self.assertIsNone(self.plugin_instance.ui_update_lock) @patch("os.system") def test_toggle_relay(self, system_mock):