From c005dd8360a4579ab53a2e6164c543585c910fce Mon Sep 17 00:00:00 2001
From: Martin Hierholzer <martin.hierholzer@desy.de>
Date: Wed, 28 Sep 2022 13:26:45 +0200
Subject: [PATCH] fix shutdown issues when waiting for devices (cf. #10029)

---
 include/DeviceManager.h |  2 ++
 src/Application.cc      |  6 ++++++
 src/DeviceManager.cc    | 22 ++++++++++++++++++++++
 3 files changed, 30 insertions(+)

diff --git a/include/DeviceManager.h b/include/DeviceManager.h
index 41dc8159..6579360c 100644
--- a/include/DeviceManager.h
+++ b/include/DeviceManager.h
@@ -130,6 +130,8 @@ namespace ChimeraTK {
      */
     [[nodiscard]] Device& getDevice() { return _device; }
 
+    void terminate() override;
+
    protected:
     /**
      * Use this function to read the exception version number. It is locking the variable mutex correctly for you.
diff --git a/src/Application.cc b/src/Application.cc
index 5a43ea59..972a8059 100644
--- a/src/Application.cc
+++ b/src/Application.cc
@@ -185,6 +185,12 @@ void Application::shutdown() {
     internalModule->deactivate();
   }
 
+  // shutdown all DeviceManagers, otherwise application modules might hang if still waiting for initial values from
+  // devices
+  for(auto& pair : _deviceManagerMap) {
+    pair.second->terminate();
+  }
+
   // next deactivate the modules, as they have running threads inside as well
   for(auto& module : getSubmoduleListRecursive()) {
     module->terminate();
diff --git a/src/DeviceManager.cc b/src/DeviceManager.cc
index 8846a098..0f494945 100644
--- a/src/DeviceManager.cc
+++ b/src/DeviceManager.cc
@@ -398,4 +398,26 @@ namespace ChimeraTK {
 
   /*********************************************************************************************************************/
 
+  void DeviceManager::terminate() {
+    if(moduleThread.joinable()) {
+      moduleThread.interrupt();
+      // try joining the thread
+      while(!moduleThread.try_join_for(boost::chrono::milliseconds(10))) {
+        // send boost interrupted exception through the _errorQueue
+        try {
+          throw boost::thread_interrupted();
+        }
+        catch(boost::thread_interrupted&) {
+          _errorQueue.push_exception(std::current_exception());
+        }
+
+        // it may not suffice to send the exception once, as the exception might get overwritten in the queue, thus we
+        // repeat this until the thread was joined.
+      }
+    }
+    assert(!moduleThread.joinable());
+  }
+
+  /*********************************************************************************************************************/
+
 } // namespace ChimeraTK
-- 
GitLab