Replace all threads with Win32Thread in Wine host

This commit is contained in:
Robbert van der Helm
2020-10-27 18:29:38 +01:00
parent bafc36614b
commit 28886e7073
6 changed files with 111 additions and 117 deletions
-94
View File
@@ -81,100 +81,6 @@ intptr_t DefaultDataConverter::return_value(const int /*opcode*/,
return original;
}
EventHandler::EventHandler(
boost::asio::io_context& io_context,
boost::asio::local::stream_protocol::endpoint endpoint,
bool listen)
: io_context(io_context), endpoint(endpoint), socket(io_context) {
if (listen) {
fs::create_directories(fs::path(endpoint.path()).parent_path());
acceptor.emplace(io_context, endpoint);
}
}
void EventHandler::connect() {
if (acceptor) {
acceptor->accept(socket);
// As mentioned in `acceptor's` docstring, this acceptor will be
// recreated in `receive()` on another context, and potentially on the
// other side of the connection in the case of `vst_host_callback`
acceptor.reset();
fs::remove(endpoint.path());
} else {
socket.connect(endpoint);
}
}
void EventHandler::close() {
socket.shutdown(boost::asio::local::stream_protocol::socket::shutdown_both);
socket.close();
}
Sockets::Sockets(boost::asio::io_context& io_context,
const boost::filesystem::path& endpoint_base_dir,
bool listen)
: base_dir(endpoint_base_dir),
host_vst_dispatch(io_context,
(base_dir / "host_vst_dispatch.sock").string(),
listen),
host_vst_dispatch_midi_events(
io_context,
(base_dir / "host_vst_dispatch_midi_events.sock").string(),
listen),
vst_host_callback(io_context,
(base_dir / "vst_host_callback.sock").string(),
listen),
host_vst_parameters(io_context),
host_vst_process_replacing(io_context),
host_vst_control(io_context),
host_vst_parameters_endpoint(
(base_dir / "host_vst_parameters.sock").string()),
host_vst_process_replacing_endpoint(
(base_dir / "host_vst_process_replacing.sock").string()),
host_vst_control_endpoint((base_dir / "host_vst_control.sock").string()) {
if (listen) {
fs::create_directories(base_dir);
acceptors = Acceptors{
.host_vst_parameters{io_context, host_vst_parameters_endpoint},
.host_vst_process_replacing{io_context,
host_vst_process_replacing_endpoint},
.host_vst_control{io_context, host_vst_control_endpoint},
};
}
}
Sockets::~Sockets() {
// Only clean if we're the ones who have created these files, although it
// should not cause any harm to also do this on the Wine side
if (acceptors) {
try {
fs::remove_all(base_dir);
} catch (const fs::filesystem_error&) {
// There should not be any filesystem errors since only one side
// removes the files, but if we somehow can't delete the file then
// we can just silently ignore this
}
}
}
void Sockets::connect() {
host_vst_dispatch.connect();
host_vst_dispatch_midi_events.connect();
vst_host_callback.connect();
if (acceptors) {
acceptors->host_vst_parameters.accept(host_vst_parameters);
acceptors->host_vst_process_replacing.accept(
host_vst_process_replacing);
acceptors->host_vst_control.accept(host_vst_control);
} else {
host_vst_parameters.connect(host_vst_parameters_endpoint);
host_vst_process_replacing.connect(host_vst_process_replacing_endpoint);
host_vst_control.connect(host_vst_control_endpoint);
}
}
boost::filesystem::path generate_endpoint_base(const std::string& plugin_name) {
fs::path temp_directory = get_temporary_directory();
+103 -15
View File
@@ -18,7 +18,6 @@
#include <atomic>
#include <iostream>
#include <thread>
#include <bitsery/adapter/buffer.h>
#include <bitsery/bitsery.h>
@@ -204,7 +203,11 @@ class DefaultDataConverter {
* above. Similarly, the `EventHandler::receive()` method first sets up
* asynchronous listeners for the socket endpoint, and then block and handle
* events until the main socket is closed.
*
* @tparam Thread The thread implementation to use. On the Linux side this
* should be `std::jthread` and on the Wine side this should be `Win32Thread`.
*/
template <typename Thread>
class EventHandler {
public:
/**
@@ -223,20 +226,44 @@ class EventHandler {
*/
EventHandler(boost::asio::io_context& io_context,
boost::asio::local::stream_protocol::endpoint endpoint,
bool listen);
bool listen)
: io_context(io_context), endpoint(endpoint), socket(io_context) {
if (listen) {
boost::filesystem::create_directories(
boost::filesystem::path(endpoint.path()).parent_path());
acceptor.emplace(io_context, endpoint);
}
}
/**
* Depending on the value of the `listen` argument passed to the
* constructor, either accept connections made to the sockets on the Linux
* side or connect to the sockets on the Wine side
*/
void connect();
void connect() {
if (acceptor) {
acceptor->accept(socket);
// As mentioned in `acceptor's` docstring, this acceptor will be
// recreated in `receive()` on another context, and potentially on
// the other side of the connection in the case of
// `vst_host_callback`
acceptor.reset();
boost::filesystem::remove(endpoint.path());
} else {
socket.connect(endpoint);
}
}
/**
* Close the socket. Both sides that are actively listening will be thrown a
* `boost::system_error` when this happens.
*/
void close();
void close() {
socket.shutdown(
boost::asio::local::stream_protocol::socket::shutdown_both);
socket.close();
}
/**
* Serialize and send an event over a socket. This is used for both the host
@@ -384,7 +411,7 @@ class EventHandler {
// This works the exact same was as `active_plugins` and
// `next_plugin_id` in `GroupBridge`
std::map<size_t, std::jthread> active_secondary_requests{};
std::map<size_t, Thread> active_secondary_requests{};
std::atomic_size_t next_request_id{};
std::mutex active_secondary_requests_mutex{};
accept_requests(
@@ -395,7 +422,7 @@ class EventHandler {
// We have to make sure to keep moving these sockets into the
// threads that will handle them
std::lock_guard lock(active_secondary_requests_mutex);
active_secondary_requests[request_id] = std::jthread(
active_secondary_requests[request_id] = Thread(
[&, request_id](boost::asio::local::stream_protocol::socket
secondary_socket) {
// TODO: Factor this out
@@ -427,15 +454,14 @@ class EventHandler {
active_secondary_requests_mutex);
// The join is implicit because we're using
// std::jthread
// std::jthread/Win32Thread
active_secondary_requests.erase(request_id);
});
},
std::move(secondary_socket));
});
std::jthread secondary_requests_handler(
[&]() { secondary_context.run(); });
Thread secondary_requests_handler([&]() { secondary_context.run(); });
while (true) {
try {
@@ -555,7 +581,11 @@ class EventHandler {
* `true` before launching the Wine VST host. This will start listening on the
* sockets, and the call to `connect()` will then accept any incoming
* connections.
*
* @tparam Thread The thread implementation to use. On the Linux side this
* should be `std::jthread` and on the Wine side this should be `Win32Thread`.
*/
template <typename Thread>
class Sockets {
public:
/**
@@ -574,20 +604,78 @@ class Sockets {
*/
Sockets(boost::asio::io_context& io_context,
const boost::filesystem::path& endpoint_base_dir,
bool listen);
bool listen)
: base_dir(endpoint_base_dir),
host_vst_dispatch(io_context,
(base_dir / "host_vst_dispatch.sock").string(),
listen),
host_vst_dispatch_midi_events(
io_context,
(base_dir / "host_vst_dispatch_midi_events.sock").string(),
listen),
vst_host_callback(io_context,
(base_dir / "vst_host_callback.sock").string(),
listen),
host_vst_parameters(io_context),
host_vst_process_replacing(io_context),
host_vst_control(io_context),
host_vst_parameters_endpoint(
(base_dir / "host_vst_parameters.sock").string()),
host_vst_process_replacing_endpoint(
(base_dir / "host_vst_process_replacing.sock").string()),
host_vst_control_endpoint(
(base_dir / "host_vst_control.sock").string()) {
if (listen) {
boost::filesystem::create_directories(base_dir);
acceptors = Acceptors{
.host_vst_parameters{io_context, host_vst_parameters_endpoint},
.host_vst_process_replacing{
io_context, host_vst_process_replacing_endpoint},
.host_vst_control{io_context, host_vst_control_endpoint},
};
}
}
/**
* Cleans up the directory containing the socket endpoints when yabridge
* shuts down if it still exists.
*/
~Sockets();
~Sockets() {
// Only clean if we're the ones who have created these files, although
// it should not cause any harm to also do this on the Wine side
if (acceptors) {
try {
boost::filesystem::remove_all(base_dir);
} catch (const boost::filesystem::filesystem_error&) {
// There should not be any filesystem errors since only one side
// removes the files, but if we somehow can't delete the file
// then we can just silently ignore this
}
}
}
/**
* Depending on the value of the `listen` argument passed to the
* constructor, either accept connections made to the sockets on the Linux
* side or connect to the sockets on the Wine side
*/
void connect();
void connect() {
host_vst_dispatch.connect();
host_vst_dispatch_midi_events.connect();
vst_host_callback.connect();
if (acceptors) {
acceptors->host_vst_parameters.accept(host_vst_parameters);
acceptors->host_vst_process_replacing.accept(
host_vst_process_replacing);
acceptors->host_vst_control.accept(host_vst_control);
} else {
host_vst_parameters.connect(host_vst_parameters_endpoint);
host_vst_process_replacing.connect(
host_vst_process_replacing_endpoint);
host_vst_control.connect(host_vst_control_endpoint);
}
}
/**
* The base directory for our socket endpoints. All `*_endpoint` variables
@@ -604,19 +692,19 @@ class Sockets {
* The socket that forwards all `dispatcher()` calls from the VST host to
* the plugin.
*/
EventHandler host_vst_dispatch;
EventHandler<Thread> host_vst_dispatch;
/**
* Used specifically for the `effProcessEvents` opcode. This is needed
* because the Win32 API is designed to block during certain GUI
* interactions such as resizing a window or opening a dropdown. Without
* this MIDI input would just stop working at times.
*/
EventHandler host_vst_dispatch_midi_events;
EventHandler<Thread> host_vst_dispatch_midi_events;
/**
* The socket that forwards all `audioMaster()` calls from the Windows VST
* plugin to the host.
*/
EventHandler vst_host_callback;
EventHandler<Thread> vst_host_callback;
/**
* Used for both `getParameter` and `setParameter` since they mostly
* overlap.
+2 -2
View File
@@ -83,7 +83,7 @@ void HostProcess::async_log_pipe_lines(patched_async_pipe& pipe,
IndividualHost::IndividualHost(boost::asio::io_context& io_context,
Logger& logger,
fs::path plugin_path,
const Sockets& sockets)
const Sockets<std::jthread>& sockets)
: HostProcess(io_context, logger),
plugin_arch(find_vst_architecture(plugin_path)),
host_path(find_vst_host(plugin_arch, false)),
@@ -130,7 +130,7 @@ void IndividualHost::terminate() {
GroupHost::GroupHost(boost::asio::io_context& io_context,
Logger& logger,
fs::path plugin_path,
Sockets& sockets,
Sockets<std::jthread>& sockets,
std::string group_name)
: HostProcess(io_context, logger),
plugin_arch(find_vst_architecture(plugin_path)),
+4 -4
View File
@@ -25,8 +25,8 @@
#include <boost/process/child.hpp>
#include <thread>
#include "../common/logging.h"
#include "../common/communication.h"
#include "../common/logging.h"
#include "utils.h"
/**
@@ -127,7 +127,7 @@ class IndividualHost : public HostProcess {
IndividualHost(boost::asio::io_context& io_context,
Logger& logger,
boost::filesystem::path plugin_path,
const Sockets& sockets);
const Sockets<std::jthread>& sockets);
PluginArchitecture architecture() override;
boost::filesystem::path path() override;
@@ -169,7 +169,7 @@ class GroupHost : public HostProcess {
GroupHost(boost::asio::io_context& io_context,
Logger& logger,
boost::filesystem::path plugin_path,
Sockets& socket_endpoint,
Sockets<std::jthread>& socket_endpoint,
std::string group_name);
PluginArchitecture architecture() override;
@@ -192,7 +192,7 @@ class GroupHost : public HostProcess {
* The associated sockets for the plugin we're hosting. This is used to
* terminate the plugin.
*/
Sockets& sockets;
Sockets<std::jthread>& sockets;
/**
* A thread that waits for the group host to have started and then ask it to
+1 -1
View File
@@ -124,7 +124,7 @@ class PluginBridge {
void log_init_message();
boost::asio::io_context io_context;
Sockets sockets;
Sockets<std::jthread> sockets;
/**
* The thread that handles host callbacks.
+1 -1
View File
@@ -189,7 +189,7 @@ class Vst2Bridge {
/**
* All sockets used for communicating with this specific plugin.
*/
Sockets sockets;
Sockets<Win32Thread> sockets;
/**
* The thread that specifically handles `effProcessEvents` opcodes so the