mirror of
https://github.com/robbert-vdh/yabridge.git
synced 2026-05-10 04:30:12 +02:00
Implement the Wine host process watchdog
This will shut down a bridge's sockets when the connected native host process exits, to prevent dangling Wine processes.
This commit is contained in:
@@ -16,13 +16,17 @@
|
|||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
#include "../editor.h"
|
#include "../editor.h"
|
||||||
|
|
||||||
HostBridge::HostBridge(MainContext& main_context,
|
HostBridge::HostBridge(MainContext& main_context,
|
||||||
boost::filesystem::path plugin_path)
|
boost::filesystem::path plugin_path,
|
||||||
|
pid_t parent_pid)
|
||||||
: plugin_path(plugin_path),
|
: plugin_path(plugin_path),
|
||||||
main_context(main_context),
|
main_context(main_context),
|
||||||
generic_logger(Logger::create_wine_stderr()),
|
generic_logger(Logger::create_wine_stderr()),
|
||||||
|
parent_pid(parent_pid),
|
||||||
watchdog_guard(main_context.register_watchdog(*this)) {}
|
watchdog_guard(main_context.register_watchdog(*this)) {}
|
||||||
|
|
||||||
void HostBridge::handle_win32_events() {
|
void HostBridge::handle_win32_events() {
|
||||||
@@ -37,5 +41,17 @@ void HostBridge::handle_win32_events() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void HostBridge::shutdown_if_dangling() {
|
void HostBridge::shutdown_if_dangling() {
|
||||||
// TODO: Implement
|
// If the parent process has exited and this plugin bridge instance is
|
||||||
|
// outliving the process it's supposed to be connected to (because in some
|
||||||
|
// situations sockets won't get closed when this happens so we'd hang on
|
||||||
|
// `recv()`), then we'll close the sockets here so that the plugin bridge
|
||||||
|
// exits gracefully. This will be periodically called from `MainContext`'s
|
||||||
|
// watchdog thread.
|
||||||
|
if (!pid_running(parent_pid)) {
|
||||||
|
std::cerr << "WARNING: The native plugin host seems to have died."
|
||||||
|
<< std::endl;
|
||||||
|
std::cerr << " This bridge will shut down now." << std::endl;
|
||||||
|
|
||||||
|
close_sockets();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,7 +32,9 @@
|
|||||||
*/
|
*/
|
||||||
class HostBridge {
|
class HostBridge {
|
||||||
protected:
|
protected:
|
||||||
HostBridge(MainContext& main_context, boost::filesystem::path plugin_path);
|
HostBridge(MainContext& main_context,
|
||||||
|
boost::filesystem::path plugin_path,
|
||||||
|
pid_t parent_pid);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
virtual ~HostBridge(){};
|
virtual ~HostBridge(){};
|
||||||
@@ -127,6 +129,15 @@ class HostBridge {
|
|||||||
Logger generic_logger;
|
Logger generic_logger;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
/**
|
||||||
|
* The process ID of the native plugin host we are bridging for. This should
|
||||||
|
* be the parent, but it might not be because of Wine's startup script,
|
||||||
|
* `WINELOADER`s and Wine's `start.exe` behaviour. We'll periodically check
|
||||||
|
* if this process is still alive, and close the sockets if it is not to
|
||||||
|
* prevent dangling processes.
|
||||||
|
*/
|
||||||
|
const pid_t parent_pid;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A guard that, while in scope, will cause `shutdown_if_dangling()` to
|
* A guard that, while in scope, will cause `shutdown_if_dangling()` to
|
||||||
* periodically be called.
|
* periodically be called.
|
||||||
|
|||||||
@@ -217,13 +217,13 @@ void GroupBridge::accept_requests() {
|
|||||||
case PluginType::vst2:
|
case PluginType::vst2:
|
||||||
bridge = std::make_unique<Vst2Bridge>(
|
bridge = std::make_unique<Vst2Bridge>(
|
||||||
main_context, request.plugin_path,
|
main_context, request.plugin_path,
|
||||||
request.endpoint_base_dir);
|
request.endpoint_base_dir, request.parent_pid);
|
||||||
break;
|
break;
|
||||||
case PluginType::vst3:
|
case PluginType::vst3:
|
||||||
#ifdef WITH_VST3
|
#ifdef WITH_VST3
|
||||||
bridge = std::make_unique<Vst3Bridge>(
|
bridge = std::make_unique<Vst3Bridge>(
|
||||||
main_context, request.plugin_path,
|
main_context, request.plugin_path,
|
||||||
request.endpoint_base_dir);
|
request.endpoint_base_dir, request.parent_pid);
|
||||||
#else
|
#else
|
||||||
throw std::runtime_error(
|
throw std::runtime_error(
|
||||||
"This version of yabridge has not been compiled "
|
"This version of yabridge has not been compiled "
|
||||||
|
|||||||
@@ -68,8 +68,9 @@ Vst2Bridge& get_bridge_instance(const AEffect* plugin) {
|
|||||||
|
|
||||||
Vst2Bridge::Vst2Bridge(MainContext& main_context,
|
Vst2Bridge::Vst2Bridge(MainContext& main_context,
|
||||||
std::string plugin_dll_path,
|
std::string plugin_dll_path,
|
||||||
std::string endpoint_base_dir)
|
std::string endpoint_base_dir,
|
||||||
: HostBridge(main_context, plugin_dll_path),
|
pid_t parent_pid)
|
||||||
|
: HostBridge(main_context, plugin_dll_path, parent_pid),
|
||||||
logger(generic_logger),
|
logger(generic_logger),
|
||||||
plugin_handle(LoadLibrary(plugin_dll_path.c_str()), FreeLibrary),
|
plugin_handle(LoadLibrary(plugin_dll_path.c_str()), FreeLibrary),
|
||||||
sockets(main_context.context, endpoint_base_dir, false) {
|
sockets(main_context.context, endpoint_base_dir, false) {
|
||||||
|
|||||||
@@ -47,6 +47,9 @@ class Vst2Bridge : public HostBridge {
|
|||||||
* to load.
|
* to load.
|
||||||
* @param endpoint_base_dir The base directory used for the socket
|
* @param endpoint_base_dir The base directory used for the socket
|
||||||
* endpoints. See `Sockets` for more information.
|
* endpoints. See `Sockets` for more information.
|
||||||
|
* @param parent_pid The process ID of the native plugin host this bridge is
|
||||||
|
* supposed to communicate with. Used as part of our watchdog to prevent
|
||||||
|
* dangling Wine processes.
|
||||||
*
|
*
|
||||||
* @note The object has to be constructed from the same thread that calls
|
* @note The object has to be constructed from the same thread that calls
|
||||||
* `main_context.run()`.
|
* `main_context.run()`.
|
||||||
@@ -56,7 +59,8 @@ class Vst2Bridge : public HostBridge {
|
|||||||
*/
|
*/
|
||||||
Vst2Bridge(MainContext& main_context,
|
Vst2Bridge(MainContext& main_context,
|
||||||
std::string plugin_dll_path,
|
std::string plugin_dll_path,
|
||||||
std::string endpoint_base_dir);
|
std::string endpoint_base_dir,
|
||||||
|
pid_t parent_pid);
|
||||||
|
|
||||||
bool inhibits_event_loop() override;
|
bool inhibits_event_loop() override;
|
||||||
|
|
||||||
|
|||||||
@@ -89,8 +89,9 @@ InstanceInterfaces::InstanceInterfaces(
|
|||||||
|
|
||||||
Vst3Bridge::Vst3Bridge(MainContext& main_context,
|
Vst3Bridge::Vst3Bridge(MainContext& main_context,
|
||||||
std::string plugin_dll_path,
|
std::string plugin_dll_path,
|
||||||
std::string endpoint_base_dir)
|
std::string endpoint_base_dir,
|
||||||
: HostBridge(main_context, plugin_dll_path),
|
pid_t parent_pid)
|
||||||
|
: HostBridge(main_context, plugin_dll_path, parent_pid),
|
||||||
logger(generic_logger),
|
logger(generic_logger),
|
||||||
sockets(main_context.context, endpoint_base_dir, false) {
|
sockets(main_context.context, endpoint_base_dir, false) {
|
||||||
std::string error;
|
std::string error;
|
||||||
|
|||||||
@@ -207,6 +207,9 @@ class Vst3Bridge : public HostBridge {
|
|||||||
* load.
|
* load.
|
||||||
* @param endpoint_base_dir The base directory used for the socket
|
* @param endpoint_base_dir The base directory used for the socket
|
||||||
* endpoints. See `Sockets` for more information.
|
* endpoints. See `Sockets` for more information.
|
||||||
|
* @param parent_pid The process ID of the native plugin host this bridge is
|
||||||
|
* supposed to communicate with. Used as part of our watchdog to prevent
|
||||||
|
* dangling Wine processes.
|
||||||
*
|
*
|
||||||
* @note The object has to be constructed from the same thread that calls
|
* @note The object has to be constructed from the same thread that calls
|
||||||
* `main_context.run()`.
|
* `main_context.run()`.
|
||||||
@@ -216,7 +219,8 @@ class Vst3Bridge : public HostBridge {
|
|||||||
*/
|
*/
|
||||||
Vst3Bridge(MainContext& main_context,
|
Vst3Bridge(MainContext& main_context,
|
||||||
std::string plugin_dll_path,
|
std::string plugin_dll_path,
|
||||||
std::string endpoint_base_dir);
|
std::string endpoint_base_dir,
|
||||||
|
pid_t parent_pid);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* For VST3 plugins we'll have to check for every object in
|
* For VST3 plugins we'll have to check for every object in
|
||||||
|
|||||||
@@ -82,12 +82,14 @@ __cdecl
|
|||||||
switch (plugin_type) {
|
switch (plugin_type) {
|
||||||
case PluginType::vst2:
|
case PluginType::vst2:
|
||||||
bridge = std::make_unique<Vst2Bridge>(
|
bridge = std::make_unique<Vst2Bridge>(
|
||||||
main_context, plugin_location, socket_endpoint_path);
|
main_context, plugin_location, socket_endpoint_path,
|
||||||
|
parent_pid);
|
||||||
break;
|
break;
|
||||||
case PluginType::vst3:
|
case PluginType::vst3:
|
||||||
#ifdef WITH_VST3
|
#ifdef WITH_VST3
|
||||||
bridge = std::make_unique<Vst3Bridge>(
|
bridge = std::make_unique<Vst3Bridge>(
|
||||||
main_context, plugin_location, socket_endpoint_path);
|
main_context, plugin_location, socket_endpoint_path,
|
||||||
|
parent_pid);
|
||||||
#else
|
#else
|
||||||
std::cerr << "This version of yabridge has not been compiled "
|
std::cerr << "This version of yabridge has not been compiled "
|
||||||
"with VST3 support"
|
"with VST3 support"
|
||||||
|
|||||||
Reference in New Issue
Block a user