diff --git a/source/backend/CarlaEngine.hpp b/source/backend/CarlaEngine.hpp index 85e6983ea..cf50b152d 100644 --- a/source/backend/CarlaEngine.hpp +++ b/source/backend/CarlaEngine.hpp @@ -1002,11 +1002,17 @@ public: // ------------------------------------------------------------------- // Misc + /*! + * Check if the engine is about to close. + */ + bool isAboutToClose() const noexcept; + /*! * Tell the engine it's about to close. * This is used to prevent the engine thread(s) from reactivating. + * Returns true if there's no pending engine events. */ - void setAboutToClose() noexcept; + bool setAboutToClose() noexcept; // ------------------------------------------------------------------- // Options diff --git a/source/backend/CarlaHost.h b/source/backend/CarlaHost.h index 83f65486b..176bcc8ff 100644 --- a/source/backend/CarlaHost.h +++ b/source/backend/CarlaHost.h @@ -340,8 +340,9 @@ CARLA_EXPORT bool carla_is_engine_running(); /*! * Tell the engine it's about to close. * This is used to prevent the engine thread(s) from reactivating. + * Returns true if there's no pending engine events. */ -CARLA_EXPORT void carla_set_engine_about_to_close(); +CARLA_EXPORT bool carla_set_engine_about_to_close(); /*! * Set the engine callback function. diff --git a/source/backend/CarlaStandalone.cpp b/source/backend/CarlaStandalone.cpp index d89d4690a..7ae0f6723 100644 --- a/source/backend/CarlaStandalone.cpp +++ b/source/backend/CarlaStandalone.cpp @@ -421,12 +421,12 @@ bool carla_is_engine_running() return (gStandalone.engine != nullptr && gStandalone.engine->isRunning()); } -void carla_set_engine_about_to_close() +bool carla_set_engine_about_to_close() { - CARLA_SAFE_ASSERT_RETURN(gStandalone.engine != nullptr,); + CARLA_SAFE_ASSERT_RETURN(gStandalone.engine != nullptr, true); carla_debug("carla_set_engine_about_to_close()"); - gStandalone.engine->setAboutToClose(); + return gStandalone.engine->setAboutToClose(); } void carla_set_engine_callback(EngineCallbackFunc func, void* ptr) diff --git a/source/backend/engine/CarlaEngine.cpp b/source/backend/engine/CarlaEngine.cpp index a7caaf475..dca373a21 100644 --- a/source/backend/engine/CarlaEngine.cpp +++ b/source/backend/engine/CarlaEngine.cpp @@ -1340,11 +1340,21 @@ void CarlaEngine::setLastError(const char* const error) const noexcept pData->lastError = error; } -void CarlaEngine::setAboutToClose() noexcept +// ----------------------------------------------------------------------- +// Misc + +bool CarlaEngine::isAboutToClose() const noexcept +{ + return pData->aboutToClose; +} + +bool CarlaEngine::setAboutToClose() noexcept { carla_debug("CarlaEngine::setAboutToClose()"); pData->aboutToClose = true; + + return (pData->isIdling == 0); } // ----------------------------------------------------------------------- @@ -1856,6 +1866,9 @@ bool CarlaEngine::loadProjectInternal(juce::XmlDocument& xmlDoc) xmlElement = xmlDoc.getDocumentElement(false); CARLA_SAFE_ASSERT_RETURN_ERR(xmlElement != nullptr, "Failed to completely parse project file"); + if (pData->aboutToClose) + return true; + const bool isPlugin(getType() == kEngineTypePlugin); // engine settings @@ -1968,6 +1981,9 @@ bool CarlaEngine::loadProjectInternal(juce::XmlDocument& xmlDoc) break; } + if (pData->aboutToClose) + return true; + // handle plugins first for (XmlElement* elem = xmlElement->getFirstChildElement(); elem != nullptr; elem = elem->getNextElement()) { @@ -1980,6 +1996,9 @@ bool CarlaEngine::loadProjectInternal(juce::XmlDocument& xmlDoc) callback(ENGINE_CALLBACK_IDLE, 0, 0, 0, 0.0f, nullptr); + if (pData->aboutToClose) + return true; + CARLA_SAFE_ASSERT_CONTINUE(stateSave.type != nullptr); const void* extraStuff = nullptr; @@ -2004,6 +2023,9 @@ bool CarlaEngine::loadProjectInternal(juce::XmlDocument& xmlDoc) { callback(ENGINE_CALLBACK_IDLE, 0, 0, 0, 0.0f, nullptr); + if (pData->aboutToClose) + return true; + #ifndef BUILD_BRIDGE // deactivate bridge client-side ping check, since some plugins block during load if ((plugin->getHints() & PLUGIN_IS_BRIDGE) != 0 && ! isPreset) @@ -2034,6 +2056,9 @@ bool CarlaEngine::loadProjectInternal(juce::XmlDocument& xmlDoc) callback(ENGINE_CALLBACK_IDLE, 0, 0, 0, 0.0f, nullptr); + if (pData->aboutToClose) + return true; + // handle connections (internal) if (pData->options.processMode == ENGINE_PROCESS_MODE_PATCHBAY) { @@ -2076,6 +2101,9 @@ bool CarlaEngine::loadProjectInternal(juce::XmlDocument& xmlDoc) } callback(ENGINE_CALLBACK_IDLE, 0, 0, 0, 0.0f, nullptr); + + if (pData->aboutToClose) + return true; } // if we're running inside some session-manager (and using JACK), let them handle the external connections diff --git a/source/backend/plugin/CarlaPluginBridge.cpp b/source/backend/plugin/CarlaPluginBridge.cpp index 2ef45c07b..af488a99d 100644 --- a/source/backend/plugin/CarlaPluginBridge.cpp +++ b/source/backend/plugin/CarlaPluginBridge.cpp @@ -2567,6 +2567,8 @@ public: if (fInitiated) break; + if (pData->engine->isAboutToClose()) + break; carla_msleep(20); } diff --git a/source/carla_backend.py b/source/carla_backend.py index 9134a8541..c52abfb21 100644 --- a/source/carla_backend.py +++ b/source/carla_backend.py @@ -1852,7 +1852,7 @@ class CarlaHostNull(CarlaHostMeta): return False def set_engine_about_to_close(self): - return + return True def set_engine_callback(self, func): self.fEngineCallback = func @@ -2119,7 +2119,7 @@ class CarlaHostDLL(CarlaHostMeta): self.lib.carla_is_engine_running.restype = c_bool self.lib.carla_set_engine_about_to_close.argtypes = None - self.lib.carla_set_engine_about_to_close.restype = None + self.lib.carla_set_engine_about_to_close.restype = c_bool self.lib.carla_set_engine_callback.argtypes = [EngineCallbackFunc, c_void_p] self.lib.carla_set_engine_callback.restype = None @@ -2382,7 +2382,7 @@ class CarlaHostDLL(CarlaHostMeta): return bool(self.lib.carla_is_engine_running()) def set_engine_about_to_close(self): - self.lib.carla_set_engine_about_to_close() + return bool(self.lib.carla_set_engine_about_to_close()) def set_engine_callback(self, func): self._engineCallback = EngineCallbackFunc(func) diff --git a/source/carla_host.py b/source/carla_host.py index c095bad9e..a6903ef71 100644 --- a/source/carla_host.py +++ b/source/carla_host.py @@ -111,6 +111,10 @@ class HostWindow(QMainWindow): self.fIsProjectLoading = False self.fCurrentlyRemovingAllPlugins = False + # run a custom action after engine is properly closed + # 1 for close carla, 2 for load project + self.fCustomStopAction = 0 + # first attempt of auto-start engine doesn't show an error self.fFirstEngineInit = True @@ -623,22 +627,44 @@ class HostWindow(QMainWindow): @pyqtSlot() def slot_engineStop(self, forced = False): - if self.fPluginCount > 0: - if not forced: - ask = QMessageBox.question(self, self.tr("Warning"), self.tr("There are still some plugins loaded, you need to remove them to stop the engine.\n" - "Do you want to do this now?"), - QMessageBox.Yes | QMessageBox.No, QMessageBox.No) - if ask != QMessageBox.Yes: - return - - self.killTimers() - self.removeAllPlugins() - self.host.set_engine_about_to_close() - self.host.remove_all_plugins() + if self.fPluginCount == 0: + self.engineStopFinal() + return True + + if not forced: + ask = QMessageBox.question(self, self.tr("Warning"), self.tr("There are still some plugins loaded, you need to remove them to stop the engine.\n" + "Do you want to do this now?"), + QMessageBox.Yes | QMessageBox.No, QMessageBox.No) + if ask != QMessageBox.Yes: + return + + return self.slot_engineStopTryAgain() + + @pyqtSlot() + def slot_engineStopTryAgain(self): + if self.host.is_engine_running() and not self.host.set_engine_about_to_close(): + QTimer.singleShot(0, self.slot_engineStopTryAgain) + return False + + self.engineStopFinal() + return True + + def engineStopFinal(self): + self.killTimers() + self.removeAllPlugins() + self.host.remove_all_plugins() if self.host.is_engine_running() and not self.host.engine_close(): print(self.host.get_last_error()) + if self.fCustomStopAction == 1: + self.close() + elif self.fCustomStopAction == 2: + self.slot_engineStart() + self.loadProjectNow() + + self.fCustomStopAction = 0 + # -------------------------------------------------------------------------------------------------------- # Engine (host callbacks) @@ -1683,7 +1709,11 @@ class HostWindow(QMainWindow): self.fClientName = os.path.basename(valueStr) self.fProjectFilename = QFileInfo(valueStr+".carxp").absoluteFilePath() self.setProperWindowTitle() - self.slot_engineStop(True) + + self.fCustomStopAction = 2 + if not self.slot_engineStop(True): + return + self.slot_engineStart() self.loadProjectNow() @@ -1733,7 +1763,8 @@ class HostWindow(QMainWindow): @pyqtSlot() def slot_handleSIGTERM(self): print("Got SIGTERM -> Closing now") - self.close() + self.fCustomStopAction = 1 + self.slot_engineStop(True) # -------------------------------------------------------------------------------------------------------- # Internal stuff @@ -1817,6 +1848,11 @@ class HostWindow(QMainWindow): return pitem.getWidget() + # -------------------------------------------------------------------------------------------------------- + + def waitForPendingEvents(self): + pass + # -------------------------------------------------------------------------------------------------------- # show/hide event @@ -1927,7 +1963,10 @@ class HostWindow(QMainWindow): self.saveSettings() if self.host.is_engine_running() and not (self.host.isControl or self.host.isPlugin): - self.slot_engineStop(True) + if not self.slot_engineStop(True): + self.fCustomStopAction = 1 + event.accept() + return QMainWindow.closeEvent(self, event) diff --git a/source/native-plugins/resources/carla-plugin b/source/native-plugins/resources/carla-plugin index e58899d07..46f89b22e 100755 --- a/source/native-plugins/resources/carla-plugin +++ b/source/native-plugins/resources/carla-plugin @@ -64,7 +64,7 @@ class PluginHost(CarlaHostQtPlugin): return self.fExternalUI.isRunning() def set_engine_about_to_close(self): - return + return True # ------------------------------------------------------------------------------------------------------------ # Main Window