From adf0f3c570f5bf6092fd12a74153a11aa80b64b3 Mon Sep 17 00:00:00 2001
From: David Hammer <dhammer@mailbox.org>
Date: Fri, 1 Apr 2022 18:21:39 +0200
Subject: [PATCH] Move expensive buffer changes to background to avoid timeouts

---
 src/calng/base_correction.py | 13 +++++++++----
 src/calng/utils.py           | 19 +++++++++++++++++++
 2 files changed, 28 insertions(+), 4 deletions(-)

diff --git a/src/calng/base_correction.py b/src/calng/base_correction.py
index b451c5ee..e498d34a 100644
--- a/src/calng/base_correction.py
+++ b/src/calng/base_correction.py
@@ -709,8 +709,7 @@ class BaseCorrection(PythonDevice):
         update = self._prereconfigure_update_hash
 
         if update.has("frameFilter"):
-            with self._buffer_lock:
-                self._update_frame_filter()
+            self._lock_and_update_in_background(self._update_frame_filter)
         elif any(
             update.has(shape_param)
             for shape_param in (
@@ -721,11 +720,17 @@ class BaseCorrection(PythonDevice):
                 "frameFilter",
             )
         ):
-            with self._buffer_lock:
-                self._update_buffers()
+            self._lock_and_update_in_background(self._update_buffers)
         # TODO: only call this if they are changed (is cheap, though)
         self._update_correction_flags()
 
+    def _lock_and_update_in_background(self, method):
+        # TODO: securely handle errors (postReconfigure may succeed and device state not)
+        def runner():
+            with self._buffer_lock, utils.StateContext(self, State.CHANGING):
+                method()
+        threading.Thread(target=runner, daemon=True).start()
+
     def loadMostRecentConstants(self):
         self.flush_constants()
         self.calcat_friend.flush_constants()
diff --git a/src/calng/utils.py b/src/calng/utils.py
index b14e36b8..c24502a8 100644
--- a/src/calng/utils.py
+++ b/src/calng/utils.py
@@ -279,6 +279,25 @@ class Stopwatch:
             return f"{self.name}: {self.elapsed():.3f} s"
 
 
+class StateContext:
+    """What if device state was a stack?"""
+
+    def __init__(self, device, new_state, revert_to=None):
+        self.device = device
+        if revert_to is None:
+            revert_to = device.get("state")
+        self.revert_to = revert_to
+        self.new_state = new_state
+
+    def __enter__(self):
+        if self.new_state is not self.revert_to:
+            self.device.updateState(self.new_state)
+
+    def __exit__(self, t, v, tb):
+        if self.new_state is not self.revert_to:
+            self.device.updateState(self.revert_to)
+
+
 class TrainRatioTracker:
     """Measure how many percent of recent train IDs (from contiguous set) were seen
 
-- 
GitLab