mirror of
https://github.com/robbert-vdh/yabridge.git
synced 2026-06-19 09:53:56 +02:00
Use std::jthread
This commit is contained in:
@@ -187,9 +187,9 @@ GroupHost::GroupHost(
|
|||||||
// process to start depending on Wine's current state. We'll defer
|
// process to start depending on Wine's current state. We'll defer
|
||||||
// this to a thread so we can finish the rest of the startup in the
|
// this to a thread so we can finish the rest of the startup in the
|
||||||
// meantime.
|
// meantime.
|
||||||
group_host_connect_handler = std::thread([&, group_socket_path,
|
group_host_connect_handler = std::jthread([&, group_socket_path,
|
||||||
plugin_path,
|
plugin_path,
|
||||||
socket_endpoint]() {
|
socket_endpoint]() {
|
||||||
using namespace std::literals::chrono_literals;
|
using namespace std::literals::chrono_literals;
|
||||||
|
|
||||||
// TODO: Replace this polling with inotify
|
// TODO: Replace this polling with inotify
|
||||||
@@ -221,14 +221,6 @@ GroupHost::GroupHost(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
GroupHost::~GroupHost() {
|
|
||||||
// This thread was briefly used to issue the host request if we had to start
|
|
||||||
// a new group host process
|
|
||||||
if (group_host_connect_handler.joinable()) {
|
|
||||||
group_host_connect_handler.join();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
PluginArchitecture GroupHost::architecture() {
|
PluginArchitecture GroupHost::architecture() {
|
||||||
return plugin_arch;
|
return plugin_arch;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -171,8 +171,6 @@ class GroupHost : public HostProcess {
|
|||||||
std::string group_name,
|
std::string group_name,
|
||||||
boost::asio::local::stream_protocol::socket& host_vst_dispatch);
|
boost::asio::local::stream_protocol::socket& host_vst_dispatch);
|
||||||
|
|
||||||
~GroupHost();
|
|
||||||
|
|
||||||
PluginArchitecture architecture() override;
|
PluginArchitecture architecture() override;
|
||||||
boost::filesystem::path path() override;
|
boost::filesystem::path path() override;
|
||||||
bool running() override;
|
bool running() override;
|
||||||
@@ -204,5 +202,5 @@ class GroupHost : public HostProcess {
|
|||||||
* TODO: Replace the polling with inotify to prevent delays and to reduce
|
* TODO: Replace the polling with inotify to prevent delays and to reduce
|
||||||
* wasting resources
|
* wasting resources
|
||||||
*/
|
*/
|
||||||
std::thread group_host_connect_handler;
|
std::jthread group_host_connect_handler;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -86,13 +86,10 @@ PluginBridge::PluginBridge(audioMasterCallback host_callback)
|
|||||||
// when it is not. The alternative would be to rewrite this to using
|
// 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
|
// `async_accept`, Boost.Asio timers, and another IO context, but I feel
|
||||||
// like this a much simpler solution.
|
// like this a much simpler solution.
|
||||||
std::thread([&]() {
|
std::jthread host_guard_handler([&](std::stop_token st) {
|
||||||
using namespace std::literals::chrono_literals;
|
using namespace std::literals::chrono_literals;
|
||||||
|
|
||||||
while (true) {
|
while (!st.stop_requested()) {
|
||||||
if (finished_accepting_sockets) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!vst_host->running()) {
|
if (!vst_host->running()) {
|
||||||
logger.log(
|
logger.log(
|
||||||
"The Wine host process has exited unexpectedly. Check the "
|
"The Wine host process has exited unexpectedly. Check the "
|
||||||
@@ -102,7 +99,7 @@ PluginBridge::PluginBridge(audioMasterCallback host_callback)
|
|||||||
|
|
||||||
std::this_thread::sleep_for(1s);
|
std::this_thread::sleep_for(1s);
|
||||||
}
|
}
|
||||||
}).detach();
|
});
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// It's very important that these sockets are connected to in the same
|
// It's very important that these sockets are connected to in the same
|
||||||
@@ -112,7 +109,11 @@ PluginBridge::PluginBridge(audioMasterCallback host_callback)
|
|||||||
socket_acceptor.accept(vst_host_callback);
|
socket_acceptor.accept(vst_host_callback);
|
||||||
socket_acceptor.accept(host_vst_parameters);
|
socket_acceptor.accept(host_vst_parameters);
|
||||||
socket_acceptor.accept(host_vst_process_replacing);
|
socket_acceptor.accept(host_vst_process_replacing);
|
||||||
finished_accepting_sockets = true;
|
|
||||||
|
#ifndef WITH_WINEDBG
|
||||||
|
host_guard_handler.request_stop();
|
||||||
|
host_guard_handler.detach();
|
||||||
|
#endif
|
||||||
|
|
||||||
// There's no need to keep the socket endpoint file around after accepting
|
// There's no need to keep the socket endpoint file around after accepting
|
||||||
// all the sockets, and RAII won't clean these files up for us
|
// all the sockets, and RAII won't clean these files up for us
|
||||||
@@ -131,7 +132,7 @@ PluginBridge::PluginBridge(audioMasterCallback host_callback)
|
|||||||
// For our communication we use simple threads and blocking operations
|
// For our communication we use simple threads and blocking operations
|
||||||
// instead of asynchronous IO since communication has to be handled in
|
// instead of asynchronous IO since communication has to be handled in
|
||||||
// lockstep anyway
|
// lockstep anyway
|
||||||
host_callback_handler = std::thread([&]() {
|
host_callback_handler = std::jthread([&]() {
|
||||||
while (true) {
|
while (true) {
|
||||||
try {
|
try {
|
||||||
// TODO: Think of a nicer way to structure this and the similar
|
// TODO: Think of a nicer way to structure this and the similar
|
||||||
@@ -456,11 +457,6 @@ intptr_t PluginBridge::dispatch(AEffect* /*plugin*/,
|
|||||||
// been caused by pipes and sockets being closed.
|
// been caused by pipes and sockets being closed.
|
||||||
io_context.stop();
|
io_context.stop();
|
||||||
|
|
||||||
// These threads should now be finished because we've forcefully
|
|
||||||
// terminated the Wine process, interupting their socket operations
|
|
||||||
host_callback_handler.join();
|
|
||||||
wine_io_handler.join();
|
|
||||||
|
|
||||||
delete this;
|
delete this;
|
||||||
|
|
||||||
return return_value;
|
return return_value;
|
||||||
|
|||||||
@@ -128,19 +128,10 @@ class PluginBridge {
|
|||||||
boost::asio::local::stream_protocol::socket host_vst_parameters;
|
boost::asio::local::stream_protocol::socket host_vst_parameters;
|
||||||
boost::asio::local::stream_protocol::socket host_vst_process_replacing;
|
boost::asio::local::stream_protocol::socket host_vst_process_replacing;
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether we're done accepting sockets. The plugin may hang indefinitely if
|
|
||||||
* the Wine process fails to start, since then nothing will connect to our
|
|
||||||
* sockets. While we're waiting for our sockets we'll periodically poll the
|
|
||||||
* Wine process to see if it's still running, and terminate the socket
|
|
||||||
* accepting if it is not.
|
|
||||||
*/
|
|
||||||
std::atomic_bool finished_accepting_sockets;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The thread that handles host callbacks.
|
* The thread that handles host callbacks.
|
||||||
*/
|
*/
|
||||||
std::thread host_callback_handler;
|
std::jthread host_callback_handler;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A binary semaphore to prevent race conditions from the dispatch function
|
* A binary semaphore to prevent race conditions from the dispatch function
|
||||||
@@ -185,7 +176,7 @@ class PluginBridge {
|
|||||||
* Runs the Boost.Asio `io_context` thread for logging the Wine process
|
* Runs the Boost.Asio `io_context` thread for logging the Wine process
|
||||||
* STDOUT and STDERR messages.
|
* STDOUT and STDERR messages.
|
||||||
*/
|
*/
|
||||||
std::thread wine_io_handler;
|
std::jthread wine_io_handler;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A scratch buffer for sending and receiving data during `process` and
|
* A scratch buffer for sending and receiving data during `process` and
|
||||||
|
|||||||
@@ -102,12 +102,11 @@ GroupBridge::GroupBridge(boost::filesystem::path group_socket_path)
|
|||||||
async_log_pipe_lines(stdout_redirect.pipe, stdout_buffer, "[STDOUT] ");
|
async_log_pipe_lines(stdout_redirect.pipe, stdout_buffer, "[STDOUT] ");
|
||||||
async_log_pipe_lines(stderr_redirect.pipe, stderr_buffer, "[STDERR] ");
|
async_log_pipe_lines(stderr_redirect.pipe, stderr_buffer, "[STDERR] ");
|
||||||
|
|
||||||
stdio_handler = std::thread([&]() { stdio_context.run(); });
|
stdio_handler = std::jthread([&]() { stdio_context.run(); });
|
||||||
}
|
}
|
||||||
|
|
||||||
GroupBridge::~GroupBridge() {
|
GroupBridge::~GroupBridge() {
|
||||||
stdio_context.stop();
|
stdio_context.stop();
|
||||||
stdio_handler.join();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GroupBridge::handle_plugin_dispatch(const GroupRequest request) {
|
void GroupBridge::handle_plugin_dispatch(const GroupRequest request) {
|
||||||
@@ -129,8 +128,7 @@ void GroupBridge::handle_plugin_dispatch(const GroupRequest request) {
|
|||||||
boost::asio::post(plugin_context, [&, request]() {
|
boost::asio::post(plugin_context, [&, request]() {
|
||||||
std::lock_guard lock(active_plugins_mutex);
|
std::lock_guard lock(active_plugins_mutex);
|
||||||
|
|
||||||
auto& [thread, bridge] = active_plugins.at(request);
|
// The join is implicit because we're using std::jthread
|
||||||
thread.join();
|
|
||||||
active_plugins.erase(request);
|
active_plugins.erase(request);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -220,7 +218,7 @@ void GroupBridge::accept_requests() {
|
|||||||
// socket on another thread. The actual event handling will
|
// socket on another thread. The actual event handling will
|
||||||
// still occur within this IO context.
|
// still occur within this IO context.
|
||||||
active_plugins[request] =
|
active_plugins[request] =
|
||||||
std::pair(std::thread([&, request]() {
|
std::pair(std::jthread([&, request]() {
|
||||||
handle_plugin_dispatch(request);
|
handle_plugin_dispatch(request);
|
||||||
}),
|
}),
|
||||||
std::move(bridge));
|
std::move(bridge));
|
||||||
|
|||||||
@@ -240,7 +240,7 @@ class GroupBridge {
|
|||||||
/**
|
/**
|
||||||
* A thread that runs the `stdio_context` loop.
|
* A thread that runs the `stdio_context` loop.
|
||||||
*/
|
*/
|
||||||
std::thread stdio_handler;
|
std::jthread stdio_handler;
|
||||||
|
|
||||||
boost::asio::local::stream_protocol::endpoint group_socket_endpoint;
|
boost::asio::local::stream_protocol::endpoint group_socket_endpoint;
|
||||||
/**
|
/**
|
||||||
@@ -255,14 +255,9 @@ class GroupBridge {
|
|||||||
* initialization has failed, the thread handling it will remove itself from
|
* initialization has failed, the thread handling it will remove itself from
|
||||||
* this map. This is to keep track of the amount of plugins currently
|
* this map. This is to keep track of the amount of plugins currently
|
||||||
* running with their associated thread handles.
|
* running with their associated thread handles.
|
||||||
*
|
|
||||||
* TODO: Check again if we can just use std::thread here instead, that would
|
|
||||||
* make everything much simpler. `std::thread` was a problem with
|
|
||||||
* gdiplus in the past as Serum would randomly crash because calling
|
|
||||||
* conventions were nto being respected.
|
|
||||||
*/
|
*/
|
||||||
std::unordered_map<GroupRequest,
|
std::unordered_map<GroupRequest,
|
||||||
std::pair<std::thread, std::unique_ptr<Vst2Bridge>>>
|
std::pair<std::jthread, std::unique_ptr<Vst2Bridge>>>
|
||||||
active_plugins;
|
active_plugins;
|
||||||
/**
|
/**
|
||||||
* A mutex to prevent two threads from simultaneously accessing the plugins
|
* A mutex to prevent two threads from simultaneously accessing the plugins
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ int __cdecl main(int argc, char* argv[]) {
|
|||||||
|
|
||||||
// We'll listen for `dispatcher()` calls on a different thread, but the
|
// We'll listen for `dispatcher()` calls on a different thread, but the
|
||||||
// actual events will still be executed within the IO context
|
// actual events will still be executed within the IO context
|
||||||
std::thread dispatch_handler([&]() { bridge->handle_dispatch(); });
|
std::jthread dispatch_handler([&]() { bridge->handle_dispatch(); });
|
||||||
|
|
||||||
// Handle Win32 messages and X11 events on a timer, just like in
|
// Handle Win32 messages and X11 events on a timer, just like in
|
||||||
// `GroupBridge::async_handle_events()``
|
// `GroupBridge::async_handle_events()``
|
||||||
@@ -102,7 +102,6 @@ int __cdecl main(int argc, char* argv[]) {
|
|||||||
async_handle_events(events_timer, *bridge);
|
async_handle_events(events_timer, *bridge);
|
||||||
|
|
||||||
io_context.run();
|
io_context.run();
|
||||||
dispatch_handler.join();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void async_handle_events(boost::asio::steady_timer& timer, Vst2Bridge& bridge) {
|
void async_handle_events(boost::asio::steady_timer& timer, Vst2Bridge& bridge) {
|
||||||
|
|||||||
@@ -45,8 +45,8 @@
|
|||||||
* converting stateless lambdas to this format, but clang (as used for IDE
|
* converting stateless lambdas to this format, but clang (as used for IDE
|
||||||
* tooling) does not.
|
* tooling) does not.
|
||||||
*
|
*
|
||||||
* @note This should be used instead of `std::thread` whenever the thread
|
* @note This should be used instead of `std::thread` or `std::jthread` whenever
|
||||||
* directly calls third party library code, i.e. `LoadLibrary()`,
|
* the thread directly calls third party library code, i.e. `LoadLibrary()`,
|
||||||
* `FreeLibrary()`, the plugin's entry point, or any of the `AEffect::*()`
|
* `FreeLibrary()`, the plugin's entry point, or any of the `AEffect::*()`
|
||||||
* functions.
|
* functions.
|
||||||
*/
|
*/
|
||||||
|
|||||||
Reference in New Issue
Block a user