Work around a regression in Wine 6.5

Killing a Wine process no longer terminates its threads, see the
changelog entry and NOTE for more information.
This commit is contained in:
Robbert van der Helm
2021-03-27 17:41:15 +01:00
parent 36d93d05ca
commit b036230067
4 changed files with 53 additions and 24 deletions
+8 -1
View File
@@ -8,7 +8,14 @@ Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased] ## [Unreleased]
### Yabridgectl ### Fixed
- Work around a regression in Wine 6.5 that would prevent yabridge from shutting
down. With Wine 6.5 terminating a Wine process no longer terminates its
threads, which would cause yabridge's plugin and host components to wait for
each other to shut down.
### yabridgectl
- Minor spelling fixes. - Minor spelling fixes.
+6 -5
View File
@@ -84,11 +84,12 @@ class PluginBridge {
io_context, io_context,
generic_logger, generic_logger,
info, info,
HostRequest{.plugin_type = plugin_type, HostRequest{
.plugin_path = .plugin_type = plugin_type,
info.windows_plugin_path.string(), .plugin_path =
.endpoint_base_dir = info.windows_plugin_path.string(),
sockets.base_dir.string()}))), .endpoint_base_dir = sockets.base_dir.string()},
sockets))),
has_realtime_priority(has_realtime_priority_promise.get_future()), has_realtime_priority(has_realtime_priority_promise.get_future()),
wine_io_handler([&]() { wine_io_handler([&]() {
// We no longer run this thread with realtime scheduling because // We no longer run this thread with realtime scheduling because
+19 -7
View File
@@ -56,8 +56,13 @@ bp::child launch_host(fs::path host_path, Args&&... args) {
bp::posix::use_vfork, std::forward<Args>(args)...); bp::posix::use_vfork, std::forward<Args>(args)...);
} }
HostProcess::HostProcess(boost::asio::io_context& io_context, Logger& logger) HostProcess::HostProcess(boost::asio::io_context& io_context,
: stdout_pipe(io_context), stderr_pipe(io_context), logger(logger) { Logger& logger,
Sockets& sockets)
: stdout_pipe(io_context),
stderr_pipe(io_context),
sockets(sockets),
logger(logger) {
// Print the Wine host's STDOUT and STDERR streams to the log file. This // Print the Wine host's STDOUT and STDERR streams to the log file. This
// should be done before trying to accept the sockets as otherwise we will // should be done before trying to accept the sockets as otherwise we will
// miss all output. // miss all output.
@@ -68,8 +73,9 @@ HostProcess::HostProcess(boost::asio::io_context& io_context, Logger& logger)
IndividualHost::IndividualHost(boost::asio::io_context& io_context, IndividualHost::IndividualHost(boost::asio::io_context& io_context,
Logger& logger, Logger& logger,
const PluginInfo& plugin_info, const PluginInfo& plugin_info,
const HostRequest& host_request) const HostRequest& host_request,
: HostProcess(io_context, logger), Sockets& sockets)
: HostProcess(io_context, logger, sockets),
plugin_info(plugin_info), plugin_info(plugin_info),
host_path(find_vst_host(plugin_info.native_library_path, host_path(find_vst_host(plugin_info.native_library_path,
plugin_info.plugin_arch, plugin_info.plugin_arch,
@@ -109,6 +115,13 @@ bool IndividualHost::running() {
} }
void IndividualHost::terminate() { void IndividualHost::terminate() {
// NOTE: This technically shouldn't be needed, but in Wine 6.5 sending
// SIGKILL to a Wine process no longer terminates the threads spawned
// by that process, so if we don't manually close the sockets there
// will still be threads listening on those sockets which in turn also
// prevents us from joining our `std::jthread`s on the plugin side.
sockets.close();
host.terminate(); host.terminate();
// NOTE: This leaves a zombie, because Boost.Process will actually not call // NOTE: This leaves a zombie, because Boost.Process will actually not call
// `wait()` after we have terminated the process. // `wait()` after we have terminated the process.
@@ -121,12 +134,11 @@ GroupHost::GroupHost(boost::asio::io_context& io_context,
const HostRequest& host_request, const HostRequest& host_request,
Sockets& sockets, Sockets& sockets,
std::string group_name) std::string group_name)
: HostProcess(io_context, logger), : HostProcess(io_context, logger, sockets),
plugin_info(plugin_info), plugin_info(plugin_info),
host_path(find_vst_host(plugin_info.native_library_path, host_path(find_vst_host(plugin_info.native_library_path,
plugin_info.plugin_arch, plugin_info.plugin_arch,
true)), true)) {
sockets(sockets) {
#ifdef WITH_WINEDBG #ifdef WITH_WINEDBG
if (plugin_info.windows_plugin_path.string().find(' ') != if (plugin_info.windows_plugin_path.string().find(' ') !=
std::string::npos) { std::string::npos) {
+20 -11
View File
@@ -68,8 +68,13 @@ class HostProcess {
* handled on. * handled on.
* @param logger The `Logger` instance the redirected STDIO streams will be * @param logger The `Logger` instance the redirected STDIO streams will be
* written to. * written to.
* @param sockets The socket endpoints that will be used for communication
* with the plugin. When the plugin shuts down, we'll close all of the
* sockets used by the plugin.
*/ */
HostProcess(boost::asio::io_context& io_context, Logger& logger); HostProcess(boost::asio::io_context& io_context,
Logger& logger,
Sockets& sockets);
/** /**
* The STDOUT stream of the Wine process we can forward to the logger. * The STDOUT stream of the Wine process we can forward to the logger.
@@ -80,6 +85,12 @@ class HostProcess {
*/ */
patched_async_pipe stderr_pipe; patched_async_pipe stderr_pipe;
/**
* The associated sockets for the plugin we're hosting. This is used to
* terminate the plugin.
*/
Sockets& sockets;
private: private:
/** /**
* The logger the Wine output will be written to. * The logger the Wine output will be written to.
@@ -108,6 +119,9 @@ class IndividualHost : public HostProcess {
* @param host_request The information about the plugin we should launch a * @param host_request The information about the plugin we should launch a
* host process for. The values in the struct will be used as command line * host process for. The values in the struct will be used as command line
* arguments. * arguments.
* @param sockets The socket endpoints that will be used for communication
* with the plugin. When the plugin shuts down, we'll close all of the
* sockets used by the plugin.
* *
* @throw std::runtime_error When `plugin_path` does not point to a valid * @throw std::runtime_error When `plugin_path` does not point to a valid
* 32-bit or 64-bit .dll file. * 32-bit or 64-bit .dll file.
@@ -115,7 +129,8 @@ class IndividualHost : public HostProcess {
IndividualHost(boost::asio::io_context& io_context, IndividualHost(boost::asio::io_context& io_context,
Logger& logger, Logger& logger,
const PluginInfo& plugin_info, const PluginInfo& plugin_info,
const HostRequest& host_request); const HostRequest& host_request,
Sockets& sockets);
boost::filesystem::path path() override; boost::filesystem::path path() override;
bool running() override; bool running() override;
@@ -153,15 +168,15 @@ class GroupHost : public HostProcess {
* @param host_request The information about the plugin we should launch a * @param host_request The information about the plugin we should launch a
* host process for. This object will be sent to the group host process. * host process for. This object will be sent to the group host process.
* @param sockets The socket endpoints that will be used for communication * @param sockets The socket endpoints that will be used for communication
* with the plugin. When the plugin shuts down, we'll terminate the * with the plugin. When the plugin shuts down, we'll close all of the
* dispatch socket contained in this object. * sockets used by the plugin.
* @param group_name The name of the plugin group. * @param group_name The name of the plugin group.
*/ */
GroupHost(boost::asio::io_context& io_context, GroupHost(boost::asio::io_context& io_context,
Logger& logger, Logger& logger,
const PluginInfo& plugin_info, const PluginInfo& plugin_info,
const HostRequest& host_request, const HostRequest& host_request,
Sockets& socket_endpoint, Sockets& sockets,
std::string group_name); std::string group_name);
boost::filesystem::path path() override; boost::filesystem::path path() override;
@@ -190,12 +205,6 @@ class GroupHost : public HostProcess {
*/ */
std::atomic_bool startup_failed; std::atomic_bool startup_failed;
/**
* The associated sockets for the plugin we're hosting. This is used to
* terminate the plugin.
*/
Sockets& sockets;
/** /**
* A thread that waits for the group host to have started and then ask it to * A thread that waits for the group host to have started and then ask it to
* host our plugin. This is used to defer the request since it may take a * host our plugin. This is used to defer the request since it may take a