diff --git a/src/plugin/bridges/common.h b/src/plugin/bridges/common.h index 7f217ac4..a252cc4e 100644 --- a/src/plugin/bridges/common.h +++ b/src/plugin/bridges/common.h @@ -37,8 +37,8 @@ class PluginBridge { public: /** * Sets up everything needed to start the host process. Classes deriving - * from this should call `log_init_message()` themselves after their - * initialization list. + * from this should call `log_init_message()` and + * `connect_sockets_guarded()` themselves after their initialization list. * * @param plugin_type The type of the plugin we're handling. * @param plugin_path The path to the plugin. For VST2 plugins this is the @@ -197,6 +197,47 @@ class PluginBridge { } } + /** + * Connect the sockets, while starting another thread that will terminate + * the plugin (through `std::terminate`/SIGABRT) when the host process fails + * to start. This is the only way to stop listening on our sockets without + * moving everything over to asynchronous listeners (which may actually be a + * good idea just for this use case). Otherwise the plugin would be stuck + * loading indefinitely when Wine is not configured correctly. + * + * TODO: Asynchronously connect our sockets so we can interrupt it, maybe + */ + void connect_sockets_guarded() { +#ifndef WITH_WINEDBG + // If the Wine process fails to start, then nothing will connect to the + // sockets and we'll be hanging here indefinitely. To prevent this, + // we'll periodically poll whether the Wine process is still running, + // and throw when it is not. The alternative would be to rewrite this to + // using `async_accept`, Boost.Asio timers, and another IO context, but + // I feel like this a much simpler solution. + host_guard_handler = std::jthread([&](std::stop_token st) { + using namespace std::literals::chrono_literals; + + while (!st.stop_requested()) { + if (!plugin_host->running()) { + generic_logger.log( + "The Wine host process has exited unexpectedly. Check " + "the " + "output above for more information."); + std::terminate(); + } + + std::this_thread::sleep_for(20ms); + } + }); +#endif + + sockets.connect(); +#ifndef WITH_WINEDBG + host_guard_handler.request_stop(); +#endif + } + /** * The type of the plugin we're dealing with. Passed to the host process and * printed in the initialisation message. @@ -224,7 +265,10 @@ class PluginBridge { /** * The sockets used for communication with the Wine process. * - * @see PluginBridge::log_init_message + * @remark `sockets.connect()` should not be called directly. + * `connect_sockets_guarded()` should be used instead. + * + * @see PluginBridge::connect_sockets_guarded */ TSockets sockets; @@ -263,4 +307,14 @@ class PluginBridge { * STDOUT and STDERR messages. */ std::jthread wine_io_handler; + + private: + /** + * A thread used during the initialisation process to terminate listening on + * the sockets if the Wine process cannot start for whatever reason. This + * has to be defined here instead of in the constructor we can't simply + * detach the thread as it has to check whether the VST host is still + * running. + */ + std::jthread host_guard_handler; }; diff --git a/src/plugin/bridges/vst2.cpp b/src/plugin/bridges/vst2.cpp index c52213bd..27011d29 100644 --- a/src/plugin/bridges/vst2.cpp +++ b/src/plugin/bridges/vst2.cpp @@ -61,36 +61,9 @@ Vst2PluginBridge::Vst2PluginBridge(audioMasterCallback host_callback) create_logger_prefix(sockets.base_dir)))) { log_init_message(); - // TODO: Also move his to `PluginHost` -#ifndef WITH_WINEDBG - // If the Wine process fails to start, then nothing will connect to the - // sockets and we'll be hanging here indefinitely. To prevent this, we'll - // periodically poll whether the Wine process is still running, and throw - // when it is not. The alternative would be to rewrite this to using - // `async_accept`, Boost.Asio timers, and another IO context, but I feel - // like this a much simpler solution. - host_guard_handler = std::jthread([&](std::stop_token st) { - using namespace std::literals::chrono_literals; - - while (!st.stop_requested()) { - if (!plugin_host->running()) { - logger.log( - "The Wine host process has exited unexpectedly. Check the " - "output above for more information."); - std::terminate(); - } - - std::this_thread::sleep_for(20ms); - } - }); -#endif - // This will block until all sockets have been connected to by the Wine VST // host - sockets.connect(); -#ifndef WITH_WINEDBG - host_guard_handler.request_stop(); -#endif + connect_sockets_guarded(); // Set up all pointers for our `AEffect` struct. We will fill this with data // from the VST plugin loaded in Wine at the end of this constructor. diff --git a/src/plugin/bridges/vst2.h b/src/plugin/bridges/vst2.h index 19d6e3de..0b7cb3c9 100644 --- a/src/plugin/bridges/vst2.h +++ b/src/plugin/bridges/vst2.h @@ -148,15 +148,6 @@ class Vst2PluginBridge : PluginBridge> { */ Vst2Logger logger; - /** - * A thread used during the initialisation process to terminate listening on - * the sockets if the Wine process cannot start for whatever reason. This - * has to be defined here instead of in the constructor we can't simply - * detach the thread as it has to check whether the VST host is still - * running. - */ - std::jthread host_guard_handler; - /** * A scratch buffer for sending and receiving data during `process`, * `processReplacing` and `processDoubleReplacing` calls. diff --git a/src/plugin/bridges/vst3.cpp b/src/plugin/bridges/vst3.cpp index 095f0cd0..5b00d0cd 100644 --- a/src/plugin/bridges/vst3.cpp +++ b/src/plugin/bridges/vst3.cpp @@ -34,5 +34,7 @@ Vst3PluginBridge::Vst3PluginBridge() create_logger_prefix(sockets.base_dir)))) { log_init_message(); - // TODO: Call the host guard handler + // This will block until all sockets have been connected to by the Wine VST + // host + connect_sockets_guarded(); }