From 798733e2f1bb15abdfb0f3dff86ccfae89100f0c Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 4 Jul 2022 22:26:12 +0100 Subject: [PATCH] Bring Runner class from DPF, use it for Engine background details Signed-off-by: falkTX --- source/backend/CarlaEngine.hpp | 4 +- source/backend/engine/CarlaEngine.cpp | 8 +- source/backend/engine/CarlaEngineInternal.cpp | 16 +- source/backend/engine/CarlaEngineInternal.hpp | 14 +- source/backend/engine/CarlaEngineNative.cpp | 4 +- source/backend/engine/CarlaEngineRunner.cpp | 170 +++++++++++++ ...EngineThread.hpp => CarlaEngineRunner.hpp} | 22 +- source/backend/engine/CarlaEngineThread.cpp | 159 ------------ source/backend/engine/Makefile | 2 +- source/utils/CarlaRunner.hpp | 227 ++++++++++++++++++ 10 files changed, 435 insertions(+), 191 deletions(-) create mode 100644 source/backend/engine/CarlaEngineRunner.cpp rename source/backend/engine/{CarlaEngineThread.hpp => CarlaEngineRunner.hpp} (70%) delete mode 100644 source/backend/engine/CarlaEngineThread.cpp create mode 100644 source/utils/CarlaRunner.hpp diff --git a/source/backend/CarlaEngine.hpp b/source/backend/CarlaEngine.hpp index 998bd4ff7..b9f16a479 100644 --- a/source/backend/CarlaEngine.hpp +++ b/source/backend/CarlaEngine.hpp @@ -1311,13 +1311,13 @@ protected: */ friend class CarlaEngineEventPort; friend class CarlaEngineOsc; - friend class CarlaEngineThread; + friend class CarlaEngineRunner; friend class CarlaPluginInstance; friend class EngineInternalGraph; friend class PendingRtEventsRunner; friend class ScopedActionLock; friend class ScopedEngineEnvironmentLocker; - friend class ScopedThreadStopper; + friend class ScopedRunnerStopper; friend class PatchbayGraph; friend struct ExternalGraph; friend struct RackGraph; diff --git a/source/backend/engine/CarlaEngine.cpp b/source/backend/engine/CarlaEngine.cpp index 155c12e7b..47f79b8f7 100644 --- a/source/backend/engine/CarlaEngine.cpp +++ b/source/backend/engine/CarlaEngine.cpp @@ -854,7 +854,7 @@ bool CarlaEngine::addPlugin(const BinaryType btype, { CARLA_SAFE_ASSERT(! pData->loadingProject); - const ScopedThreadStopper sts(this); + const ScopedRunnerStopper srs(this); if (pData->options.processMode == ENGINE_PROCESS_MODE_PATCHBAY) pData->graph.replacePlugin(oldPlugin, plugin); @@ -925,7 +925,7 @@ bool CarlaEngine::removePlugin(const uint id) CARLA_SAFE_ASSERT_RETURN_ERR(plugin.get() != nullptr, "Could not find plugin to remove"); CARLA_SAFE_ASSERT_RETURN_ERR(plugin->getId() == id, "Invalid engine internal data"); - const ScopedThreadStopper sts(this); + const ScopedRunnerStopper srs(this); #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH if (pData->options.processMode == ENGINE_PROCESS_MODE_PATCHBAY) @@ -967,7 +967,7 @@ bool CarlaEngine::removeAllPlugins() if (pData->curPluginCount == 0) return true; - const ScopedThreadStopper sts(this); + const ScopedRunnerStopper srs(this); const uint curPluginCount = pData->curPluginCount; @@ -1112,7 +1112,7 @@ bool CarlaEngine::switchPlugins(const uint idA, const uint idB) noexcept CARLA_SAFE_ASSERT_RETURN_ERR(pluginA->getId() == idA, "Invalid engine internal data"); CARLA_SAFE_ASSERT_RETURN_ERR(pluginB->getId() == idB, "Invalid engine internal data"); - const ScopedThreadStopper sts(this); + const ScopedRunnerStopper srs(this); if (pData->options.processMode == ENGINE_PROCESS_MODE_PATCHBAY) pData->graph.switchPlugins(pluginA, pluginB); diff --git a/source/backend/engine/CarlaEngineInternal.cpp b/source/backend/engine/CarlaEngineInternal.cpp index bda4fcfb4..7cba6aa18 100644 --- a/source/backend/engine/CarlaEngineInternal.cpp +++ b/source/backend/engine/CarlaEngineInternal.cpp @@ -377,7 +377,7 @@ EngineEvent* CarlaEngine::getInternalEventBuffer(const bool isInput) const noexc // CarlaEngine::ProtectedData CarlaEngine::ProtectedData::ProtectedData(CarlaEngine* const engine) - : thread(engine), + : runner(engine), #if defined(HAVE_LIBLO) && !defined(BUILD_BRIDGE) osc(engine), #endif @@ -511,7 +511,7 @@ bool CarlaEngine::ProtectedData::init(const char* const clientName) #endif nextAction.clearAndReset(); - thread.startThread(); + runner.start(); return true; } @@ -526,7 +526,7 @@ void CarlaEngine::ProtectedData::close() aboutToClose = true; - thread.stopThread(500); + runner.stop(); nextAction.clearAndReset(); #if defined(HAVE_LIBLO) && !defined(BUILD_BRIDGE) @@ -840,19 +840,19 @@ ScopedActionLock::~ScopedActionLock() noexcept } // ----------------------------------------------------------------------- -// ScopedThreadStopper +// ScopedRunnerStopper -ScopedThreadStopper::ScopedThreadStopper(CarlaEngine* const e) noexcept +ScopedRunnerStopper::ScopedRunnerStopper(CarlaEngine* const e) noexcept : engine(e), pData(e->pData) { - pData->thread.stopThread(500); + pData->runner.stop(); } -ScopedThreadStopper::~ScopedThreadStopper() noexcept +ScopedRunnerStopper::~ScopedRunnerStopper() noexcept { if (engine->isRunning() && ! pData->aboutToClose) - pData->thread.startThread(); + pData->runner.start(); } // ----------------------------------------------------------------------- diff --git a/source/backend/engine/CarlaEngineInternal.hpp b/source/backend/engine/CarlaEngineInternal.hpp index 112ca9d4f..9e9d5dace 100644 --- a/source/backend/engine/CarlaEngineInternal.hpp +++ b/source/backend/engine/CarlaEngineInternal.hpp @@ -18,7 +18,7 @@ #ifndef CARLA_ENGINE_INTERNAL_HPP_INCLUDED #define CARLA_ENGINE_INTERNAL_HPP_INCLUDED -#include "CarlaEngineThread.hpp" +#include "CarlaEngineRunner.hpp" #include "CarlaEngineUtils.hpp" #include "CarlaPlugin.hpp" #include "LinkedList.hpp" @@ -244,7 +244,7 @@ struct EnginePluginData { // CarlaEngineProtectedData struct CarlaEngine::ProtectedData { - CarlaEngineThread thread; + CarlaEngineRunner runner; #if defined(HAVE_LIBLO) && !defined(BUILD_BRIDGE) CarlaEngineOsc osc; @@ -268,7 +268,7 @@ struct CarlaEngine::ProtectedData { uint32_t bufferSize; double sampleRate; - bool aboutToClose; // don't re-activate thread if true + bool aboutToClose; // don't re-activate runner if true int isIdling; // don't allow any operations while idling uint curPluginCount; // number of plugins loaded (0...max) uint maxPluginNumber; // number of plugins allowed (0, 16, 99 or 255) @@ -362,18 +362,18 @@ private: // ----------------------------------------------------------------------- -class ScopedThreadStopper +class ScopedRunnerStopper { public: - ScopedThreadStopper(CarlaEngine* engine) noexcept; - ~ScopedThreadStopper() noexcept; + ScopedRunnerStopper(CarlaEngine* engine) noexcept; + ~ScopedRunnerStopper() noexcept; private: CarlaEngine* const engine; CarlaEngine::ProtectedData* const pData; CARLA_PREVENT_HEAP_ALLOCATION - CARLA_DECLARE_NON_COPYABLE(ScopedThreadStopper) + CARLA_DECLARE_NON_COPYABLE(ScopedRunnerStopper) }; // ----------------------------------------------------------------------- diff --git a/source/backend/engine/CarlaEngineNative.cpp b/source/backend/engine/CarlaEngineNative.cpp index 3ac330afa..ff6c6bdc4 100644 --- a/source/backend/engine/CarlaEngineNative.cpp +++ b/source/backend/engine/CarlaEngineNative.cpp @@ -1520,8 +1520,8 @@ protected: } // stopped during removeAllPlugins() - if (! pData->thread.isThreadRunning()) - pData->thread.startThread(); + if (! pData->runner.isRunnerActive()) + pData->runner.start(); fOptionsForced = true; const String state(data); diff --git a/source/backend/engine/CarlaEngineRunner.cpp b/source/backend/engine/CarlaEngineRunner.cpp new file mode 100644 index 000000000..145cf548f --- /dev/null +++ b/source/backend/engine/CarlaEngineRunner.cpp @@ -0,0 +1,170 @@ +/* + * Carla Plugin Host + * Copyright (C) 2011-2022 Filipe Coelho + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * For a full copy of the GNU General Public License see the doc/GPL.txt file. + */ + +#include "CarlaEngineRunner.hpp" +#include "CarlaEngineInternal.hpp" +#include "CarlaPlugin.hpp" + +#include "water/misc/Time.h" + +CARLA_BACKEND_START_NAMESPACE + +// ----------------------------------------------------------------------- + +CarlaEngineRunner::CarlaEngineRunner(CarlaEngine* const engine) noexcept + : CarlaRunner("CarlaEngineRunner"), + kEngine(engine), + fIsAlwaysRunning(false), + fIsPlugin(false) +{ + CARLA_SAFE_ASSERT(engine != nullptr); + carla_debug("CarlaEngineRunner::CarlaEngineRunner(%p)", engine); +} + +CarlaEngineRunner::~CarlaEngineRunner() noexcept +{ + carla_debug("CarlaEngineRunner::~CarlaEngineRunner()"); +} + +void CarlaEngineRunner::start() +{ + carla_debug("CarlaEngineRunner::start()"); + if (isRunnerActive()) + stopRunner(); + + fIsPlugin = kEngine->getType() == kEngineTypePlugin; + fIsAlwaysRunning = kEngine->getType() == kEngineTypeBridge || fIsPlugin; + + startRunner(25); +} + +void CarlaEngineRunner::stop() +{ + carla_debug("CarlaEngineRunner::stop()"); + stopRunner(); +} + +// ----------------------------------------------------------------------- + +bool CarlaEngineRunner::run() noexcept +{ + CARLA_SAFE_ASSERT_RETURN(kEngine != nullptr, false); + + float value; + +#if defined(HAVE_LIBLO) && ! defined(BUILD_BRIDGE) + // int64_t lastPingTime = 0; + const CarlaEngineOsc& engineOsc(kEngine->pData->osc); +#endif + + // runner must do something... + CARLA_SAFE_ASSERT_RETURN(fIsAlwaysRunning || kEngine->isRunning(), false); + +#if defined(HAVE_LIBLO) && ! defined(BUILD_BRIDGE) + const bool oscRegistedForUDP = engineOsc.isControlRegisteredForUDP(); +#else + const bool oscRegistedForUDP = false; +#endif + +#if defined(HAVE_LIBLO) && !defined(BUILD_BRIDGE) + if (kIsPlugin) + engineOsc.idle(); +#endif + + for (uint i=0, count = kEngine->getCurrentPluginCount(); i < count; ++i) + { + const CarlaPluginPtr plugin = kEngine->getPluginUnchecked(i); + + CARLA_SAFE_ASSERT_CONTINUE(plugin.get() != nullptr && plugin->isEnabled()); + CARLA_SAFE_ASSERT_UINT2(i == plugin->getId(), i, plugin->getId()); + + const uint hints = plugin->getHints(); + const bool updateUI = (hints & PLUGIN_HAS_CUSTOM_UI) != 0 && (hints & PLUGIN_NEEDS_UI_MAIN_THREAD) == 0; + + // ----------------------------------------------------------- + // DSP Idle + + try { + plugin->idle(); + } CARLA_SAFE_EXCEPTION("idle()") + + // ----------------------------------------------------------- + // Post-poned events + + if (oscRegistedForUDP || updateUI) + { + // ------------------------------------------------------- + // Update parameter outputs + + for (uint32_t j=0, pcount=plugin->getParameterCount(); j < pcount; ++j) + { + if (! plugin->isParameterOutput(j)) + continue; + + value = plugin->getParameterValue(j); + +#if defined(HAVE_LIBLO) && ! defined(BUILD_BRIDGE) + // Update OSC engine client + if (oscRegistedForUDP) + engineOsc.sendParameterValue(i, j, value); +#endif + // Update UI + if (updateUI) + plugin->uiParameterChange(j, value); + } + + if (updateUI) + { + try { + plugin->uiIdle(); + } CARLA_SAFE_EXCEPTION("uiIdle()") + } + } + +#if defined(HAVE_LIBLO) && ! defined(BUILD_BRIDGE) + // ----------------------------------------------------------- + // Update OSC control client peaks + + if (oscRegistedForUDP) + engineOsc.sendPeaks(i, kEngine->getPeaks(i)); +#endif + } + +#if defined(HAVE_LIBLO) && !defined(BUILD_BRIDGE) + if (oscRegistedForUDP) + engineOsc.sendRuntimeInfo(); + + /* + if (engineOsc.isControlRegisteredForTCP()) + { + const int64_t timeNow = water::Time::currentTimeMillis(); + + if (timeNow - lastPingTime > 1000) + { + engineOsc.sendPing(); + lastPingTime = timeNow; + } + } + */ +#endif + + return true; +} + +// ----------------------------------------------------------------------- + +CARLA_BACKEND_END_NAMESPACE diff --git a/source/backend/engine/CarlaEngineThread.hpp b/source/backend/engine/CarlaEngineRunner.hpp similarity index 70% rename from source/backend/engine/CarlaEngineThread.hpp rename to source/backend/engine/CarlaEngineRunner.hpp index 3ef35270a..ebcd81828 100644 --- a/source/backend/engine/CarlaEngineThread.hpp +++ b/source/backend/engine/CarlaEngineRunner.hpp @@ -1,6 +1,6 @@ /* * Carla Plugin Host - * Copyright (C) 2011-2014 Filipe Coelho + * Copyright (C) 2011-2022 Filipe Coelho * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -19,28 +19,34 @@ #define CARLA_ENGINE_THREAD_HPP_INCLUDED #include "CarlaBackend.h" -#include "CarlaThread.hpp" +#include "CarlaRunner.hpp" #include "CarlaJuceUtils.hpp" CARLA_BACKEND_START_NAMESPACE // ----------------------------------------------------------------------- -// CarlaEngineThread +// CarlaEngineRunner -class CarlaEngineThread : public CarlaThread +class CarlaEngineRunner : public CarlaRunner { public: - CarlaEngineThread(CarlaEngine* engine) noexcept; - ~CarlaEngineThread() noexcept override; + CarlaEngineRunner(CarlaEngine* engine) noexcept; + ~CarlaEngineRunner() noexcept override; + + void start(); + void stop(); protected: - void run() noexcept override; + bool run() noexcept override; private: CarlaEngine* const kEngine; - CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CarlaEngineThread) + bool fIsAlwaysRunning; + bool fIsPlugin; + + CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CarlaEngineRunner) }; // ----------------------------------------------------------------------- diff --git a/source/backend/engine/CarlaEngineThread.cpp b/source/backend/engine/CarlaEngineThread.cpp deleted file mode 100644 index f39b79952..000000000 --- a/source/backend/engine/CarlaEngineThread.cpp +++ /dev/null @@ -1,159 +0,0 @@ -/* - * Carla Plugin Host - * Copyright (C) 2011-2020 Filipe Coelho - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * For a full copy of the GNU General Public License see the doc/GPL.txt file. - */ - -#include "CarlaEngineThread.hpp" -#include "CarlaEngineInternal.hpp" -#include "CarlaPlugin.hpp" - -#include "water/misc/Time.h" - -CARLA_BACKEND_START_NAMESPACE - -// ----------------------------------------------------------------------- - -CarlaEngineThread::CarlaEngineThread(CarlaEngine* const engine) noexcept - : CarlaThread("CarlaEngineThread"), - kEngine(engine) -{ - CARLA_SAFE_ASSERT(engine != nullptr); - carla_debug("CarlaEngineThread::CarlaEngineThread(%p)", engine); -} - -CarlaEngineThread::~CarlaEngineThread() noexcept -{ - carla_debug("CarlaEngineThread::~CarlaEngineThread()"); -} - -// ----------------------------------------------------------------------- - -void CarlaEngineThread::run() noexcept -{ - CARLA_SAFE_ASSERT_RETURN(kEngine != nullptr,); - carla_debug("CarlaEngineThread::run()"); - - const bool kIsPlugin = kEngine->getType() == kEngineTypePlugin; - const bool kIsAlwaysRunning = kEngine->getType() == kEngineTypeBridge || kIsPlugin; - - float value; - -#if defined(HAVE_LIBLO) && ! defined(BUILD_BRIDGE) - // int64_t lastPingTime = 0; - const CarlaEngineOsc& engineOsc(kEngine->pData->osc); -#endif - - // thread must do something... - CARLA_SAFE_ASSERT_RETURN(kIsAlwaysRunning || kEngine->isRunning(),); - - for (; (kIsAlwaysRunning || kEngine->isRunning()) && ! shouldThreadExit();) - { -#if defined(HAVE_LIBLO) && ! defined(BUILD_BRIDGE) - const bool oscRegistedForUDP = engineOsc.isControlRegisteredForUDP(); -#else - const bool oscRegistedForUDP = false; -#endif - -#if defined(HAVE_LIBLO) && !defined(BUILD_BRIDGE) - if (kIsPlugin) - engineOsc.idle(); -#endif - - for (uint i=0, count = kEngine->getCurrentPluginCount(); i < count; ++i) - { - const CarlaPluginPtr plugin = kEngine->getPluginUnchecked(i); - - CARLA_SAFE_ASSERT_CONTINUE(plugin.get() != nullptr && plugin->isEnabled()); - CARLA_SAFE_ASSERT_UINT2(i == plugin->getId(), i, plugin->getId()); - - const uint hints(plugin->getHints()); - const bool updateUI((hints & PLUGIN_HAS_CUSTOM_UI) != 0 && (hints & PLUGIN_NEEDS_UI_MAIN_THREAD) == 0); - - // ----------------------------------------------------------- - // DSP Idle - - try { - plugin->idle(); - } CARLA_SAFE_EXCEPTION("idle()") - - // ----------------------------------------------------------- - // Post-poned events - - if (oscRegistedForUDP || updateUI) - { - // ------------------------------------------------------- - // Update parameter outputs - - for (uint32_t j=0, pcount=plugin->getParameterCount(); j < pcount; ++j) - { - if (! plugin->isParameterOutput(j)) - continue; - - value = plugin->getParameterValue(j); - -#if defined(HAVE_LIBLO) && ! defined(BUILD_BRIDGE) - // Update OSC engine client - if (oscRegistedForUDP) - engineOsc.sendParameterValue(i, j, value); -#endif - // Update UI - if (updateUI) - plugin->uiParameterChange(j, value); - } - - if (updateUI) - { - try { - plugin->uiIdle(); - } CARLA_SAFE_EXCEPTION("uiIdle()") - } - } - -#if defined(HAVE_LIBLO) && ! defined(BUILD_BRIDGE) - // ----------------------------------------------------------- - // Update OSC control client peaks - - if (oscRegistedForUDP) - engineOsc.sendPeaks(i, kEngine->getPeaks(i)); -#endif - } - -#if defined(HAVE_LIBLO) && !defined(BUILD_BRIDGE) - if (oscRegistedForUDP) - engineOsc.sendRuntimeInfo(); - - /* - if (engineOsc.isControlRegisteredForTCP()) - { - const int64_t timeNow = water::Time::currentTimeMillis(); - - if (timeNow - lastPingTime > 1000) - { - engineOsc.sendPing(); - lastPingTime = timeNow; - } - } - */ -#endif - - carla_msleep(25); - } - - carla_debug("CarlaEngineThread closed"); -} - -// ----------------------------------------------------------------------- - -CARLA_BACKEND_END_NAMESPACE diff --git a/source/backend/engine/Makefile b/source/backend/engine/Makefile index 5256ee588..f750948c5 100644 --- a/source/backend/engine/Makefile +++ b/source/backend/engine/Makefile @@ -38,7 +38,7 @@ OBJS = \ $(OBJDIR)/CarlaEngineGraph.cpp.o \ $(OBJDIR)/CarlaEngineInternal.cpp.o \ $(OBJDIR)/CarlaEnginePorts.cpp.o \ - $(OBJDIR)/CarlaEngineThread.cpp.o + $(OBJDIR)/CarlaEngineRunner.cpp.o ifeq ($(HAVE_LIBLO),true) OBJS += \ diff --git a/source/utils/CarlaRunner.hpp b/source/utils/CarlaRunner.hpp new file mode 100644 index 000000000..19a0aa611 --- /dev/null +++ b/source/utils/CarlaRunner.hpp @@ -0,0 +1,227 @@ +/* + * Carla Runner + * Copyright (C) 2022 Filipe Coelho + * + * Permission to use, copy, modify, and/or distribute this software for any purpose with + * or without fee is hereby granted, provided that the above copyright notice and this + * permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef CARLA_RUNNER_HPP_INCLUDED +#define CARLA_RUNNER_HPP_INCLUDED + +#include "CarlaUtils.hpp" + +#ifndef CARLA_OS_WASM +# include "CarlaThread.hpp" +#else +# include "CarlaString.hpp" +# include +#endif + +// ------------------------------------------------------------------------------------------------------------------- +// CarlaRunner class + +/** + This is a handy class that handles "idle" time in either background or main thread, + whichever is more suitable to the target platform. + Typically background threads on desktop platforms, main thread on web. + + A single function is expected to be implemented by subclasses, + which directly allows it to stop the runner by returning false. + + You can use it for quick operations that do not need to be handled in the main thread if possible. + The target is to spread out execution over many runs, instead of spending a lot of time on a single task. + */ +class CarlaRunner +{ +protected: + /* + * Constructor. + */ + CarlaRunner(const char* const runnerName = nullptr) noexcept + #ifndef CARLA_OS_WASM + : fRunnerThread(runnerName), + #else + : fRunnerName(runnerName), + fShouldStop(false), + #endif + fTimeInterval(0) {} + + /* + * Destructor. + */ + virtual ~CarlaRunner() /*noexcept*/ + { + CARLA_SAFE_ASSERT(! isRunnerActive()); + + stopRunner(); + } + + /* + * Virtual function to be implemented by the subclass. + * Return true to keep running, false to stop execution. + */ + virtual bool run() = 0; + + /* + * Check if the runner should stop. + * To be called from inside the runner to know if a stop request has been made. + */ + bool shouldRunnerStop() const noexcept + { + #ifndef CARLA_OS_WASM + return fRunnerThread.shouldThreadExit(); + #else + return fShouldStop; + #endif + } + + // --------------------------------------------------------------------------------------------------------------- + +public: + /* + * Check if the runner is active. + */ + bool isRunnerActive() noexcept + { + #ifndef CARLA_OS_WASM + return fRunnerThread.isThreadRunning(); + #else + fShouldStop = false; + return true; + #endif + } + + /* + * Start the thread. + */ + bool startRunner(const uint timeIntervalMilliseconds = 0) noexcept + { + fTimeInterval = timeIntervalMilliseconds; + #ifndef CARLA_OS_WASM + return fRunnerThread.startThread(); + #else + fShouldStop = false; + emscripten_async_call(_entryPoint, this, timeIntervalMilliseconds); + return true; + #endif + } + + /* + * Stop the runner. + * This will signal the runner to stop if active, and wait until it finishes. + */ + bool stopRunner() noexcept + { + #ifndef CARLA_OS_WASM + return fRunnerThread.stopThread(); + #else + fShouldStop = true; + return true; + #endif + } + + /* + * Tell the runner to stop as soon as possible. + */ + void signalRunnerShouldStop() noexcept + { + #ifndef CARLA_OS_WASM + fRunnerThread.signalThreadShouldExit(); + #else + fShouldStop = true; + #endif + } + + // --------------------------------------------------------------------------------------------------------------- + + /* + * Returns the name of the runner. + * This is the name that gets set in the constructor. + */ + const CarlaString& getRunnerName() const noexcept + { + #ifndef CARLA_OS_WASM + return fRunnerThread.getThreadName(); + #else + return fRunnerName; + #endif + } + + // --------------------------------------------------------------------------------------------------------------- + +private: +#ifndef CARLA_OS_WASM + class RunnerThread : private Thread + { + Runner* const runner; + + RunnerThread(Runner* const r, const char* const tn, const uint t) + : Thread(rn), + runner(r), + timeInterval(t) {} + + void run() override + { + while (!shouldThreadExit()) + { + bool stillRunning = false; + + try { + stillRunning = run(); + } catch(...) {} + + if (stillRunning && !shouldThreadExit()) + { + if (timeInterval != 0) + d_msleep(timeInterval) + + pthread_yield(); + continue; + } + + break; + } + } + } fRunnerThread; +#else + const CarlaString fRunnerName; + volatile bool fShouldStop; + + void _runEntryPoint() noexcept + { + if (fShouldStop) + return; + + bool stillRunning = false; + + try { + stillRunning = run(); + } catch(...) {} + + if (stillRunning && !fShouldStop) + emscripten_async_call(_entryPoint, this, fTimeInterval); + } + + static void _entryPoint(void* const userData) noexcept + { + static_cast(userData)->_runEntryPoint(); + } +#endif + + uint fTimeInterval; + + CARLA_DECLARE_NON_COPYABLE(CarlaRunner) +}; + +// ------------------------------------------------------------------------------------------------------------------- + +#endif // CARLA_RUNNER_HPP_INCLUDED