mirror of
https://github.com/robbert-vdh/yabridge.git
synced 2026-05-07 03:50:11 +02:00
Create base class for sockets and derive from that
Now the host launching procedure can be agnostic of the socket implementation.
This commit is contained in:
@@ -139,6 +139,79 @@ inline T read_object(Socket& socket) {
|
|||||||
return read_object<T>(socket, buffer);
|
return read_object<T>(socket, buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a unique base directory that can be used as a prefix for all Unix
|
||||||
|
* domain socket endpoints used in `Vst2PluginBridge`/`Vst2Bridge`. This will
|
||||||
|
* usually return `/run/user/<uid>/yabridge-<plugin_name>-<random_id>/`.
|
||||||
|
*
|
||||||
|
* Sockets for group hosts are handled separately. See
|
||||||
|
* `../plugin/utils.h:generate_group_endpoint` for more information on those.
|
||||||
|
*
|
||||||
|
* @param plugin_name The name of the plugin we're generating endpoints for.
|
||||||
|
* Used as a visual indication of what plugin is using this endpoint.
|
||||||
|
*/
|
||||||
|
boost::filesystem::path generate_endpoint_base(const std::string& plugin_name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manages all the sockets used for communicating between the plugin and the
|
||||||
|
* Wine host. Every plugin will get its own directory (the socket endpoint base
|
||||||
|
* directory), and all socket endpoints are created within this directory. This
|
||||||
|
* is usually `/run/user/<uid>/yabridge-<plugin_name>-<random_id>/`.
|
||||||
|
*/
|
||||||
|
class Sockets {
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Sets up the the base directory for the sockets. Classes inheriting this
|
||||||
|
* should set up their sockets here.
|
||||||
|
*
|
||||||
|
* @param endpoint_base_dir The base directory that will be used for the
|
||||||
|
* Unix domain sockets.
|
||||||
|
*
|
||||||
|
* @see Sockets::connect
|
||||||
|
*/
|
||||||
|
Sockets(const boost::filesystem::path& endpoint_base_dir)
|
||||||
|
: base_dir(endpoint_base_dir) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shuts down and closes all sockets and then cleans up the directory
|
||||||
|
* containing the socket endpoints when yabridge shuts down if it still
|
||||||
|
* exists.
|
||||||
|
*
|
||||||
|
* @note Classes overriding this should call `close()` in their destructor.
|
||||||
|
*/
|
||||||
|
virtual ~Sockets() {
|
||||||
|
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
|
||||||
|
*/
|
||||||
|
virtual void connect() = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shut down and close all sockets. Called during the destructor and also
|
||||||
|
* explicitly called when shutting down a plugin in a group host process.
|
||||||
|
*
|
||||||
|
* It should be safe to call this function more than once, and it should be
|
||||||
|
* called in the overridden class's destructor.
|
||||||
|
*/
|
||||||
|
virtual void close() = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The base directory for our socket endpoints. All `*_endpoint` variables
|
||||||
|
* below are files within this directory.
|
||||||
|
*/
|
||||||
|
const boost::filesystem::path base_dir;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A single, long-living socket
|
* A single, long-living socket
|
||||||
*/
|
*/
|
||||||
@@ -154,7 +227,7 @@ class SocketHandler {
|
|||||||
* connections will be accepted when `connect()` gets called. This should
|
* connections will be accepted when `connect()` gets called. This should
|
||||||
* be set to `true` on the plugin side, and `false` on the Wine host side.
|
* be set to `true` on the plugin side, and `false` on the Wine host side.
|
||||||
*
|
*
|
||||||
* @see Vst2Sockets::connect
|
* @see Sockets::connect
|
||||||
*/
|
*/
|
||||||
SocketHandler(boost::asio::io_context& io_context,
|
SocketHandler(boost::asio::io_context& io_context,
|
||||||
boost::asio::local::stream_protocol::endpoint endpoint,
|
boost::asio::local::stream_protocol::endpoint endpoint,
|
||||||
@@ -304,16 +377,3 @@ class SocketHandler {
|
|||||||
*/
|
*/
|
||||||
std::optional<boost::asio::local::stream_protocol::acceptor> acceptor;
|
std::optional<boost::asio::local::stream_protocol::acceptor> acceptor;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Generate a unique base directory that can be used as a prefix for all Unix
|
|
||||||
* domain socket endpoints used in `Vst2PluginBridge`/`Vst2Bridge`. This will
|
|
||||||
* usually return `/run/user/<uid>/yabridge-<plugin_name>-<random_id>/`.
|
|
||||||
*
|
|
||||||
* Sockets for group hosts are handled separately. See
|
|
||||||
* `../plugin/utils.h:generate_group_endpoint` for more information on those.
|
|
||||||
*
|
|
||||||
* @param plugin_name The name of the plugin we're generating endpoints for.
|
|
||||||
* Used as a visual indication of what plugin is using this endpoint.
|
|
||||||
*/
|
|
||||||
boost::filesystem::path generate_endpoint_base(const std::string& plugin_name);
|
|
||||||
|
|||||||
@@ -124,7 +124,7 @@ class EventHandler {
|
|||||||
* connections will be accepted when `connect()` gets called. This should
|
* connections will be accepted when `connect()` gets called. This should
|
||||||
* be set to `true` on the plugin side, and `false` on the Wine host side.
|
* be set to `true` on the plugin side, and `false` on the Wine host side.
|
||||||
*
|
*
|
||||||
* @see Vst2Sockets::connect
|
* @see Sockets::connect
|
||||||
*/
|
*/
|
||||||
EventHandler(boost::asio::io_context& io_context,
|
EventHandler(boost::asio::io_context& io_context,
|
||||||
boost::asio::local::stream_protocol::endpoint endpoint,
|
boost::asio::local::stream_protocol::endpoint endpoint,
|
||||||
@@ -448,7 +448,7 @@ class EventHandler {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* This acceptor will be used once synchronously on the listening side
|
* This acceptor will be used once synchronously on the listening side
|
||||||
* during `Vst2Sockets::connect()`. When `EventHandler::receive_events()` is
|
* during `Sockets::connect()`. When `EventHandler::receive_events()` is
|
||||||
* then called, we'll recreate the acceptor to asynchronously listen for new
|
* then called, we'll recreate the acceptor to asynchronously listen for new
|
||||||
* incoming socket connections on `endpoint` using. This is important,
|
* incoming socket connections on `endpoint` using. This is important,
|
||||||
* because on the case of `vst_host_callback` the acceptor is first accepts
|
* because on the case of `vst_host_callback` the acceptor is first accepts
|
||||||
@@ -467,9 +467,7 @@ class EventHandler {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Manages all the sockets used for communicating between the plugin and the
|
* Manages all the sockets used for communicating between the plugin and the
|
||||||
* Wine host. Every plugin will get its own directory (the socket endpoint base
|
* Wine host when hosting a VST2 plugin.
|
||||||
* directory), and all socket endpoints are created within this directory. This
|
|
||||||
* is usually `/run/user/<uid>/yabridge-<plugin_name>-<random_id>/`.
|
|
||||||
*
|
*
|
||||||
* On the plugin side this class should be initialized with `listen` set to
|
* On the plugin side this class should be initialized with `listen` set to
|
||||||
* `true` before launching the Wine VST host. This will start listening on the
|
* `true` before launching the Wine VST host. This will start listening on the
|
||||||
@@ -480,7 +478,7 @@ class EventHandler {
|
|||||||
* should be `std::jthread` and on the Wine side this should be `Win32Thread`.
|
* should be `std::jthread` and on the Wine side this should be `Win32Thread`.
|
||||||
*/
|
*/
|
||||||
template <typename Thread>
|
template <typename Thread>
|
||||||
class Vst2Sockets {
|
class Vst2Sockets : public Sockets {
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* Sets up the sockets using the specified base directory. The sockets won't
|
* Sets up the sockets using the specified base directory. The sockets won't
|
||||||
@@ -499,7 +497,7 @@ class Vst2Sockets {
|
|||||||
Vst2Sockets(boost::asio::io_context& io_context,
|
Vst2Sockets(boost::asio::io_context& io_context,
|
||||||
const boost::filesystem::path& endpoint_base_dir,
|
const boost::filesystem::path& endpoint_base_dir,
|
||||||
bool listen)
|
bool listen)
|
||||||
: base_dir(endpoint_base_dir),
|
: Sockets(endpoint_base_dir),
|
||||||
host_vst_dispatch(io_context,
|
host_vst_dispatch(io_context,
|
||||||
(base_dir / "host_vst_dispatch.sock").string(),
|
(base_dir / "host_vst_dispatch.sock").string(),
|
||||||
listen),
|
listen),
|
||||||
@@ -517,36 +515,9 @@ class Vst2Sockets {
|
|||||||
(base_dir / "host_vst_control.sock").string(),
|
(base_dir / "host_vst_control.sock").string(),
|
||||||
listen) {}
|
listen) {}
|
||||||
|
|
||||||
/**
|
~Vst2Sockets() { close(); }
|
||||||
* Cleans up the directory containing the socket endpoints when yabridge
|
|
||||||
* shuts down if it still exists.
|
|
||||||
*/
|
|
||||||
~Vst2Sockets() {
|
|
||||||
// Manually close all sockets so we break out of any blocking operations
|
|
||||||
// that may still be active
|
|
||||||
host_vst_dispatch.close();
|
|
||||||
vst_host_callback.close();
|
|
||||||
host_vst_parameters.close();
|
|
||||||
host_vst_process_replacing.close();
|
|
||||||
host_vst_control.close();
|
|
||||||
|
|
||||||
// Only clean if we're the ones who have created these files, although
|
void connect() override {
|
||||||
// it should not cause any harm to also do this on the Wine side
|
|
||||||
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() {
|
|
||||||
host_vst_dispatch.connect();
|
host_vst_dispatch.connect();
|
||||||
vst_host_callback.connect();
|
vst_host_callback.connect();
|
||||||
host_vst_parameters.connect();
|
host_vst_parameters.connect();
|
||||||
@@ -554,11 +525,15 @@ class Vst2Sockets {
|
|||||||
host_vst_control.connect();
|
host_vst_control.connect();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
void close() override {
|
||||||
* The base directory for our socket endpoints. All `*_endpoint` variables
|
// Manually close all sockets so we break out of any blocking operations
|
||||||
* below are files within this directory.
|
// that may still be active
|
||||||
*/
|
host_vst_dispatch.close();
|
||||||
const boost::filesystem::path base_dir;
|
vst_host_callback.close();
|
||||||
|
host_vst_parameters.close();
|
||||||
|
host_vst_process_replacing.close();
|
||||||
|
host_vst_control.close();
|
||||||
|
}
|
||||||
|
|
||||||
// The naming convention for these sockets is `<from>_<to>_<event>`. For
|
// The naming convention for these sockets is `<from>_<to>_<event>`. For
|
||||||
// instance the socket named `host_vst_dispatch` forwards
|
// instance the socket named `host_vst_dispatch` forwards
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ void HostProcess::async_log_pipe_lines(patched_async_pipe& pipe,
|
|||||||
IndividualHost::IndividualHost(boost::asio::io_context& io_context,
|
IndividualHost::IndividualHost(boost::asio::io_context& io_context,
|
||||||
Logger& logger,
|
Logger& logger,
|
||||||
fs::path plugin_path,
|
fs::path plugin_path,
|
||||||
const Vst2Sockets<std::jthread>& sockets)
|
const Sockets& sockets)
|
||||||
: HostProcess(io_context, logger),
|
: HostProcess(io_context, logger),
|
||||||
plugin_arch(find_vst_architecture(plugin_path)),
|
plugin_arch(find_vst_architecture(plugin_path)),
|
||||||
host_path(find_vst_host(plugin_arch, false)),
|
host_path(find_vst_host(plugin_arch, false)),
|
||||||
@@ -134,7 +134,7 @@ void IndividualHost::terminate() {
|
|||||||
GroupHost::GroupHost(boost::asio::io_context& io_context,
|
GroupHost::GroupHost(boost::asio::io_context& io_context,
|
||||||
Logger& logger,
|
Logger& logger,
|
||||||
fs::path plugin_path,
|
fs::path plugin_path,
|
||||||
Vst2Sockets<std::jthread>& sockets,
|
Sockets& sockets,
|
||||||
std::string group_name)
|
std::string group_name)
|
||||||
: HostProcess(io_context, logger),
|
: HostProcess(io_context, logger),
|
||||||
plugin_arch(find_vst_architecture(plugin_path)),
|
plugin_arch(find_vst_architecture(plugin_path)),
|
||||||
@@ -251,8 +251,8 @@ bool GroupHost::running() {
|
|||||||
void GroupHost::terminate() {
|
void GroupHost::terminate() {
|
||||||
// There's no need to manually terminate group host processes as they will
|
// There's no need to manually terminate group host processes as they will
|
||||||
// shut down automatically after all plugins have exited. Manually closing
|
// shut down automatically after all plugins have exited. Manually closing
|
||||||
// the dispatch socket will cause the associated plugin to exit.
|
// the sockets will cause the associated plugin to exit.
|
||||||
sockets.host_vst_dispatch.close();
|
sockets.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool pid_running(pid_t pid) {
|
bool pid_running(pid_t pid) {
|
||||||
|
|||||||
@@ -25,10 +25,7 @@
|
|||||||
#include <boost/process/child.hpp>
|
#include <boost/process/child.hpp>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
// TODO: Those host process implementation now directly uses the Vst2Sockets and
|
#include "../common/communication/common.h"
|
||||||
// thus requires `communication/vst2.h`. We should create a simple common
|
|
||||||
// interface for this instead.
|
|
||||||
#include "../common/communication/vst2.h"
|
|
||||||
#include "../common/logging.h"
|
#include "../common/logging.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
@@ -130,7 +127,7 @@ class IndividualHost : public HostProcess {
|
|||||||
IndividualHost(boost::asio::io_context& io_context,
|
IndividualHost(boost::asio::io_context& io_context,
|
||||||
Logger& logger,
|
Logger& logger,
|
||||||
boost::filesystem::path plugin_path,
|
boost::filesystem::path plugin_path,
|
||||||
const Vst2Sockets<std::jthread>& sockets);
|
const Sockets& sockets);
|
||||||
|
|
||||||
PluginArchitecture architecture() override;
|
PluginArchitecture architecture() override;
|
||||||
boost::filesystem::path path() override;
|
boost::filesystem::path path() override;
|
||||||
@@ -172,7 +169,7 @@ class GroupHost : public HostProcess {
|
|||||||
GroupHost(boost::asio::io_context& io_context,
|
GroupHost(boost::asio::io_context& io_context,
|
||||||
Logger& logger,
|
Logger& logger,
|
||||||
boost::filesystem::path plugin_path,
|
boost::filesystem::path plugin_path,
|
||||||
Vst2Sockets<std::jthread>& socket_endpoint,
|
Sockets& socket_endpoint,
|
||||||
std::string group_name);
|
std::string group_name);
|
||||||
|
|
||||||
PluginArchitecture architecture() override;
|
PluginArchitecture architecture() override;
|
||||||
@@ -206,7 +203,7 @@ class GroupHost : public HostProcess {
|
|||||||
* The associated sockets for the plugin we're hosting. This is used to
|
* The associated sockets for the plugin we're hosting. This is used to
|
||||||
* terminate the plugin.
|
* terminate the plugin.
|
||||||
*/
|
*/
|
||||||
Vst2Sockets<std::jthread>& sockets;
|
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
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ class Vst2Bridge {
|
|||||||
* @param plugin_dll_path A (Unix style) path to the VST plugin .dll file to
|
* @param plugin_dll_path A (Unix style) path to the VST plugin .dll file to
|
||||||
* 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 `Vst2Sockets` for more information.
|
* endpoints. See `Sockets` for more information.
|
||||||
*
|
*
|
||||||
* @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()`.
|
||||||
|
|||||||
Reference in New Issue
Block a user