From 131d85b07d3eb26a4331cc25f026bfd41787f6e3 Mon Sep 17 00:00:00 2001 From: nxtumUbun Date: Fri, 10 Jul 2026 15:03:22 -0700 Subject: [PATCH 1/3] working shutdown.... --- Source/Thunder/PluginServer.h | 18 +++++++++--------- Source/ThunderPlugin/Process.cpp | 22 +++++++++++++++------- 2 files changed, 24 insertions(+), 16 deletions(-) diff --git a/Source/Thunder/PluginServer.h b/Source/Thunder/PluginServer.h index b1c02d79c..533939096 100644 --- a/Source/Thunder/PluginServer.h +++ b/Source/Thunder/PluginServer.h @@ -1686,6 +1686,15 @@ namespace PluginHost { } _composit.ReleaseInterfaces(); + _handler = nullptr; + + _pluginHandling.Unlock(); + + if (currentIF != nullptr) { + _external.SetInterface(nullptr); + currentIF->Release(); + } + if (_connection != nullptr) { // Lets record the ID associated with this connection. // If the other end of this connection (indicated by the @@ -1697,15 +1706,6 @@ namespace PluginHost { _connection = nullptr; } - _handler = nullptr; - - _pluginHandling.Unlock(); - - if (currentIF != nullptr) { - _external.SetInterface(nullptr); - currentIF->Release(); - } - if (_library.IsLoaded() == true) { Core::ServiceAdministrator::Instance().ReleaseLibrary(std::move(_library)); } diff --git a/Source/ThunderPlugin/Process.cpp b/Source/ThunderPlugin/Process.cpp index a7ed3c9ac..529bcb147 100644 --- a/Source/ThunderPlugin/Process.cpp +++ b/Source/ThunderPlugin/Process.cpp @@ -81,9 +81,11 @@ POP_WARNING() else { TRACE_L1("All living objects are killed. Time for HaraKiri!!."); - // Seems there is no more live here, time to signal the - // WorkerPool to quit running and close down... - _parent.Stop(); + if (_parent.HostsPluginInterface() == false) { + // IPlugin interface is released by the host, which + // terminates this process after the final proxy release completes + _parent.Stop(); + } } } void Destructed() override { @@ -111,10 +113,11 @@ POP_WARNING() WorkerPoolImplementation& operator=(const WorkerPoolImplementation&) = delete; PUSH_WARNING(DISABLE_WARNING_THIS_IN_MEMBER_INITIALIZER_LIST) - WorkerPoolImplementation(const uint8_t threads, const uint32_t stackSize, const uint32_t queueSize, const string& callsign, const uint8_t additionalThreads = 1) + WorkerPoolImplementation(const uint8_t threads, const uint32_t stackSize, const uint32_t queueSize, const string& callsign, const uint32_t interfaceId, const uint8_t additionalThreads = 1) : WorkerPool((threads - additionalThreads), stackSize, queueSize, &_dispatcher, this, (threads > 2 ? (threads - 1) : threads), (threads > 2 ? (threads - 1) : threads), additionalThreads) , _dispatcher(callsign) , _sink(*this) + , _interfaceId(interfaceId) { Core::ServiceAdministrator::Instance().Callback(&_sink); @@ -147,6 +150,10 @@ POP_WARNING() { Core::WorkerPool::Stop(); } + bool HostsPluginInterface() const + { + return (_interfaceId == PluginHost::IPlugin::ID); + } void Idle() override { // If we handled all pending requests, it is safe to "unload" @@ -165,6 +172,7 @@ POP_WARNING() private: Dispatcher _dispatcher; Sink _sink; + const uint32_t _interfaceId; }; class ConsoleOptions : public Core::Options { @@ -503,10 +511,10 @@ class ProcessFlow { _lock.Unlock(); } - void Startup(const uint8_t threadCount, const Core::NodeId& remoteNode, const string& callsign) + void Startup(const uint8_t threadCount, const Core::NodeId& remoteNode, const string& callsign, const uint32_t interfaceId) { // Seems like we have enough information, open up the Process communcication Channel. - _engine = Core::ProxyType::Create(threadCount, Core::Thread::DefaultStackSize(), 16, callsign); + _engine = Core::ProxyType::Create(threadCount, Core::Thread::DefaultStackSize(), 16, callsign, interfaceId); // Whenever someone is looking for a WorkerPool, here it is, register it.. Core::IWorkerPool::Assign(&(*_engine)); @@ -709,7 +717,7 @@ int main(int argc, char** argv) Core::ProcessCurrent().User(string(options.User)); } - process.Startup(options.Threads, remoteNode, callsign); + process.Startup(options.Threads, remoteNode, callsign, options.InterfaceId); // Register an interface to handle incoming requests for interfaces. if ((base = Process::AcquireInterfaces(options)) != nullptr) { From ec9c8eab73449d96c3ce726d9ee8c614e2b181dd Mon Sep 17 00:00:00 2001 From: nxtumUbun Date: Mon, 20 Jul 2026 01:04:12 -0700 Subject: [PATCH 2/3] add documentation --- docs/plugin/execution-modes/outofprocess.md | 25 +++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/docs/plugin/execution-modes/outofprocess.md b/docs/plugin/execution-modes/outofprocess.md index dde4830e1..d7d345d44 100644 --- a/docs/plugin/execution-modes/outofprocess.md +++ b/docs/plugin/execution-modes/outofprocess.md @@ -7,6 +7,31 @@ When the plugin is activated, Thunder will automatically spawn a ThunderPlugin i !!! tip For larger, more complex out-of-process plugins, it is often useful to split a plugin into two separate libraries - see [here](../split-implementation) for more details. +## Whole plugin OOP + +It is also possible to run the full plugin out of process. This allows a plugin to be hosted by `ThunderPlugin` even if it was not split or specifically designed for the traditional OOP plugin model. In this mode,`PluginHost::IPlugin` itself is hosted by `ThunderPlugin`. + +This is mainly useful as a development and debugging option. For example, it can be used to isolate leaks crashes, or shutdown behavior in plugins that were originally designed to run in process. + +To enable this mode, add a top-level `root` section to the plugin configuration: + +```json +{ + "locator": "libThunderSamplePlugin.so", + "classname": "SamplePlugin", + "startmode": "Activated", + "root": { + "mode": "Local" + }, + "configuration": { + } +} +``` + +Currently, the plugin must expose a JSON-RPC interface to use this mode. This restriction may be removed in a future Thunder version. + +Other root modes may also work, but `Local` is the mode covered by this documentation. + **Advantages** * Reliability - if a plugin crashes, it will only bring down the ThunderPlugin instance and therefore not affect any other plugin. It can then be restarted as necessary From cca887cc6d78f1939ec5a7996cd55399148f5285 Mon Sep 17 00:00:00 2001 From: nxtumUbun Date: Mon, 20 Jul 2026 04:34:44 -0700 Subject: [PATCH 3/3] adapt documentation --- docs/plugin/execution-modes/outofprocess.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/docs/plugin/execution-modes/outofprocess.md b/docs/plugin/execution-modes/outofprocess.md index d7d345d44..9b756c741 100644 --- a/docs/plugin/execution-modes/outofprocess.md +++ b/docs/plugin/execution-modes/outofprocess.md @@ -9,9 +9,9 @@ When the plugin is activated, Thunder will automatically spawn a ThunderPlugin i ## Whole plugin OOP -It is also possible to run the full plugin out of process. This allows a plugin to be hosted by `ThunderPlugin` even if it was not split or specifically designed for the traditional OOP plugin model. In this mode,`PluginHost::IPlugin` itself is hosted by `ThunderPlugin`. +It is also possible to run the full plugin out of process. This allows a plugin to be hosted by `ThunderPlugin` even if it was not split or specifically designed for the traditional OOP plugin model. In this mode, `PluginHost::IPlugin` itself is hosted by `ThunderPlugin`. -This is mainly useful as a development and debugging option. For example, it can be used to isolate leaks crashes, or shutdown behavior in plugins that were originally designed to run in process. +This is mainly useful as a development and debugging option. For example, it can be used to isolate leaks,crashes, or shutdown behavior in plugins that were originally designed to run in process. To enable this mode, add a top-level `root` section to the plugin configuration: @@ -28,8 +28,6 @@ To enable this mode, add a top-level `root` section to the plugin configuration: } ``` -Currently, the plugin must expose a JSON-RPC interface to use this mode. This restriction may be removed in a future Thunder version. - Other root modes may also work, but `Local` is the mode covered by this documentation. **Advantages**