mirror of
https://github.com/robbert-vdh/yabridge.git
synced 2026-06-10 06:12:14 +02:00
Replace Boost.Asio with standalone Asio library
We had to add an even hackier hack now to get Boost.Process to interoperate with Asio's IO contexts. This will be replaced later when we replace Boost.Process.
This commit is contained in:
@@ -19,7 +19,7 @@
|
||||
#include <vector>
|
||||
|
||||
#ifdef __WINE__
|
||||
#include "../wine-host/boost-fix.h"
|
||||
#include "../wine-host/asio-fix.h"
|
||||
#endif
|
||||
#include <boost/interprocess/mapped_region.hpp>
|
||||
#include <boost/interprocess/shared_memory_object.hpp>
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include "common.h"
|
||||
|
||||
#include <random>
|
||||
#include <sstream>
|
||||
|
||||
#include "../utils.h"
|
||||
|
||||
|
||||
@@ -24,12 +24,12 @@
|
||||
#include <bitsery/traits/vector.h>
|
||||
|
||||
#ifdef __WINE__
|
||||
#include "../wine-host/boost-fix.h"
|
||||
#include "../wine-host/asio-fix.h"
|
||||
#endif
|
||||
#include <boost/asio/io_context.hpp>
|
||||
#include <boost/asio/local/stream_protocol.hpp>
|
||||
#include <boost/asio/read.hpp>
|
||||
#include <boost/asio/write.hpp>
|
||||
#include <asio/io_context.hpp>
|
||||
#include <asio/local/stream_protocol.hpp>
|
||||
#include <asio/read.hpp>
|
||||
#include <asio/write.hpp>
|
||||
#include <boost/container/small_vector.hpp>
|
||||
#include <ghc/filesystem.hpp>
|
||||
|
||||
@@ -79,51 +79,49 @@ using SerializationBuffer = boost::container::small_vector<uint8_t, N>;
|
||||
*/
|
||||
using SerializationBufferBase = boost::container::small_vector_base<uint8_t>;
|
||||
|
||||
namespace boost {
|
||||
namespace asio {
|
||||
|
||||
template <typename PodType, typename Allocator>
|
||||
inline BOOST_ASIO_MUTABLE_BUFFER buffer(
|
||||
inline ASIO_MUTABLE_BUFFER buffer(
|
||||
boost::container::small_vector_base<PodType, Allocator>& data)
|
||||
BOOST_ASIO_NOEXCEPT {
|
||||
return BOOST_ASIO_MUTABLE_BUFFER(
|
||||
ASIO_NOEXCEPT {
|
||||
return ASIO_MUTABLE_BUFFER(
|
||||
data.size() ? &data[0] : 0, data.size() * sizeof(PodType)
|
||||
#if defined(BOOST_ASIO_ENABLE_BUFFER_DEBUGGING)
|
||||
#if defined(ASIO_ENABLE_BUFFER_DEBUGGING)
|
||||
,
|
||||
detail::buffer_debug_check<typename boost::container::small_vector_base<
|
||||
PodType, Allocator>::iterator>(data.begin())
|
||||
#endif // BOOST_ASIO_ENABLE_BUFFER_DEBUGGING
|
||||
#endif // ASIO_ENABLE_BUFFER_DEBUGGING
|
||||
);
|
||||
}
|
||||
|
||||
// These are copied verbatim `boost::asio::buffer(std::vector<PodType,
|
||||
// These are copied verbatim `asio::buffer(std::vector<PodType,
|
||||
// Allocator>&, std::size_t)`, since `boost::container::small_vector` is
|
||||
// compatible with the STL vector.
|
||||
template <typename PodType, typename Allocator>
|
||||
inline BOOST_ASIO_MUTABLE_BUFFER buffer(
|
||||
inline ASIO_MUTABLE_BUFFER buffer(
|
||||
boost::container::small_vector_base<PodType, Allocator>& data,
|
||||
std::size_t max_size_in_bytes) BOOST_ASIO_NOEXCEPT {
|
||||
return BOOST_ASIO_MUTABLE_BUFFER(
|
||||
std::size_t max_size_in_bytes) ASIO_NOEXCEPT {
|
||||
return ASIO_MUTABLE_BUFFER(
|
||||
data.size() ? &data[0] : 0,
|
||||
data.size() * sizeof(PodType) < max_size_in_bytes
|
||||
? data.size() * sizeof(PodType)
|
||||
: max_size_in_bytes
|
||||
#if defined(BOOST_ASIO_ENABLE_BUFFER_DEBUGGING)
|
||||
#if defined(ASIO_ENABLE_BUFFER_DEBUGGING)
|
||||
,
|
||||
detail::buffer_debug_check<typename boost::container::small_vector_base<
|
||||
PodType, Allocator>::iterator>(data.begin())
|
||||
#endif // BOOST_ASIO_ENABLE_BUFFER_DEBUGGING
|
||||
#endif // ASIO_ENABLE_BUFFER_DEBUGGING
|
||||
);
|
||||
}
|
||||
|
||||
} // namespace asio
|
||||
} // namespace boost
|
||||
|
||||
/**
|
||||
* Serialize an object using bitsery and write it to a socket. This will write
|
||||
* both the size of the serialized object and the object itself over the socket.
|
||||
*
|
||||
* @param socket The Boost.Asio socket to write to.
|
||||
* @param socket The Asio socket to write to.
|
||||
* @param object The object to write to the stream.
|
||||
* @param buffer The buffer to write to. This is useful for sending audio and
|
||||
* chunk data since that can vary in size by a lot.
|
||||
@@ -149,10 +147,9 @@ inline void write_object(Socket& socket,
|
||||
// bit bridge. This won't make any function difference aside from the
|
||||
// 32-bit host application having to convert between 64 and 32 bit
|
||||
// integers.
|
||||
boost::asio::write(socket,
|
||||
boost::asio::buffer(std::array<uint64_t, 1>{size}));
|
||||
asio::write(socket, asio::buffer(std::array<uint64_t, 1>{size}));
|
||||
const size_t bytes_written =
|
||||
boost::asio::write(socket, boost::asio::buffer(buffer, size));
|
||||
asio::write(socket, asio::buffer(buffer, size));
|
||||
assert(bytes_written == size);
|
||||
}
|
||||
|
||||
@@ -171,7 +168,7 @@ inline void write_object(Socket& socket, const T& object) {
|
||||
* Deserialize an object by reading it from a socket. This should be used
|
||||
* together with `write_object`. This will block until the object is available.
|
||||
*
|
||||
* @param socket The Boost.Asio socket to read from.
|
||||
* @param socket The Asio socket to read from.
|
||||
* @param object The object to serialize into. There are also overrides that
|
||||
* create a new default initialized `T`
|
||||
* @param buffer The buffer to read into. This is useful for sending audio and
|
||||
@@ -180,7 +177,7 @@ inline void write_object(Socket& socket, const T& object) {
|
||||
* @return The deserialized object.
|
||||
*
|
||||
* @throw std::runtime_error If the conversion to an object was not successful.
|
||||
* @throw boost::system::system_error If the socket is closed or gets closed
|
||||
* @throw std::system_error If the socket is closed or gets closed
|
||||
* while reading.
|
||||
*
|
||||
* @relates write_object
|
||||
@@ -191,18 +188,17 @@ inline T& read_object(Socket& socket,
|
||||
SerializationBufferBase& buffer) {
|
||||
// See the note above on the use of `uint64_t` instead of `size_t`
|
||||
std::array<uint64_t, 1> message_length;
|
||||
boost::asio::read(socket, boost::asio::buffer(message_length),
|
||||
boost::asio::transfer_exactly(sizeof(message_length)));
|
||||
asio::read(socket, asio::buffer(message_length),
|
||||
asio::transfer_exactly(sizeof(message_length)));
|
||||
|
||||
// Make sure the buffer is large enough
|
||||
const size_t size = message_length[0];
|
||||
buffer.resize(size);
|
||||
|
||||
// `boost::asio::read/write` will handle all the packet splitting and
|
||||
// `asio::read/write` will handle all the packet splitting and
|
||||
// merging for us, since local domain sockets have packet limits somewhere
|
||||
// in the hundreds of kilobytes
|
||||
boost::asio::read(socket, boost::asio::buffer(buffer),
|
||||
boost::asio::transfer_exactly(size));
|
||||
asio::read(socket, asio::buffer(buffer), asio::transfer_exactly(size));
|
||||
|
||||
auto [_, success] =
|
||||
bitsery::quickDeserialization<InputAdapter<SerializationBufferBase>>(
|
||||
@@ -371,8 +367,8 @@ class SocketHandler {
|
||||
*
|
||||
* @see Sockets::connect
|
||||
*/
|
||||
SocketHandler(boost::asio::io_context& io_context,
|
||||
boost::asio::local::stream_protocol::endpoint endpoint,
|
||||
SocketHandler(asio::io_context& io_context,
|
||||
asio::local::stream_protocol::endpoint endpoint,
|
||||
bool listen)
|
||||
: endpoint_(endpoint), socket_(io_context) {
|
||||
if (listen) {
|
||||
@@ -397,13 +393,13 @@ class SocketHandler {
|
||||
|
||||
/**
|
||||
* Close the socket. Both sides that are actively listening will be thrown a
|
||||
* `boost::system_error` when this happens.
|
||||
* `std::system_error` when this happens.
|
||||
*/
|
||||
void close() {
|
||||
// The shutdown can fail when the socket is already closed
|
||||
boost::system::error_code err;
|
||||
socket_.shutdown(
|
||||
boost::asio::local::stream_protocol::socket::shutdown_both, err);
|
||||
std::error_code err;
|
||||
socket_.shutdown(asio::local::stream_protocol::socket::shutdown_both,
|
||||
err);
|
||||
socket_.close();
|
||||
}
|
||||
|
||||
@@ -414,7 +410,7 @@ class SocketHandler {
|
||||
* @param buffer The buffer to use for the serialization. This is used to
|
||||
* prevent excess allocations when sending audio.
|
||||
*
|
||||
* @throw boost::system::system_error If the socket is closed or gets closed
|
||||
* @throw std::system_error If the socket is closed or gets closed
|
||||
* during sending.
|
||||
*
|
||||
* @warning This operation is not atomic, and calling this function with the
|
||||
@@ -454,7 +450,7 @@ class SocketHandler {
|
||||
*
|
||||
* @throw std::runtime_error If the conversion to an object was not
|
||||
* successful.
|
||||
* @throw boost::system::system_error If the socket is closed or gets closed
|
||||
* @throw std::system_error If the socket is closed or gets closed
|
||||
* while reading.
|
||||
*
|
||||
* @note This function can safely be called within the lambda of
|
||||
@@ -513,7 +509,7 @@ class SocketHandler {
|
||||
receive_single<T>(object, buffer);
|
||||
|
||||
callback(object, buffer);
|
||||
} catch (const boost::system::system_error&) {
|
||||
} catch (const std::system_error&) {
|
||||
// This happens when the sockets got closed because the plugin
|
||||
// is being shut down
|
||||
break;
|
||||
@@ -522,14 +518,14 @@ class SocketHandler {
|
||||
}
|
||||
|
||||
private:
|
||||
boost::asio::local::stream_protocol::endpoint endpoint_;
|
||||
boost::asio::local::stream_protocol::socket socket_;
|
||||
asio::local::stream_protocol::endpoint endpoint_;
|
||||
asio::local::stream_protocol::socket socket_;
|
||||
|
||||
/**
|
||||
* Will be used in `connect()` on the listening side to establish the
|
||||
* connection.
|
||||
*/
|
||||
std::optional<boost::asio::local::stream_protocol::acceptor> acceptor_;
|
||||
std::optional<asio::local::stream_protocol::acceptor> acceptor_;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -572,8 +568,8 @@ class AdHocSocketHandler {
|
||||
*
|
||||
* @see Sockets::connect
|
||||
*/
|
||||
AdHocSocketHandler(boost::asio::io_context& io_context,
|
||||
boost::asio::local::stream_protocol::endpoint endpoint,
|
||||
AdHocSocketHandler(asio::io_context& io_context,
|
||||
asio::local::stream_protocol::endpoint endpoint,
|
||||
bool listen)
|
||||
: io_context_(io_context), endpoint_(endpoint), socket_(io_context) {
|
||||
if (listen) {
|
||||
@@ -606,13 +602,13 @@ class AdHocSocketHandler {
|
||||
|
||||
/**
|
||||
* Close the socket. Both sides that are actively listening will be thrown a
|
||||
* `boost::system_error` when this happens.
|
||||
* `std::system_error` when this happens.
|
||||
*/
|
||||
void close() {
|
||||
// The shutdown can fail when the socket is already closed
|
||||
boost::system::error_code err;
|
||||
socket_.shutdown(
|
||||
boost::asio::local::stream_protocol::socket::shutdown_both, err);
|
||||
std::error_code err;
|
||||
socket_.shutdown(asio::local::stream_protocol::socket::shutdown_both,
|
||||
err);
|
||||
socket_.close();
|
||||
|
||||
while (currently_listening_) {
|
||||
@@ -637,15 +633,15 @@ class AdHocSocketHandler {
|
||||
* socket. This is either the primary `socket`, or a new ad hock socket if
|
||||
* this function is currently being called from another thread.
|
||||
*/
|
||||
template <std::invocable<boost::asio::local::stream_protocol::socket&> F>
|
||||
std::invoke_result_t<F, boost::asio::local::stream_protocol::socket&> send(
|
||||
template <std::invocable<asio::local::stream_protocol::socket&> F>
|
||||
std::invoke_result_t<F, asio::local::stream_protocol::socket&> send(
|
||||
F&& callback) {
|
||||
// A bit of template and constexpr nastiness to allow us to either
|
||||
// return a value from the callback (for when writing the response to a
|
||||
// new object) or to return void (when we deserialize into an existing
|
||||
// object)
|
||||
constexpr bool returns_void = std::is_void_v<std::invoke_result_t<
|
||||
F, boost::asio::local::stream_protocol::socket&>>;
|
||||
constexpr bool returns_void = std::is_void_v<
|
||||
std::invoke_result_t<F, asio::local::stream_protocol::socket&>>;
|
||||
|
||||
// XXX: Maybe at some point we should benchmark how often this
|
||||
// ad hoc socket spawning mechanism gets used. If some hosts
|
||||
@@ -667,12 +663,12 @@ class AdHocSocketHandler {
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
boost::asio::local::stream_protocol::socket secondary_socket(
|
||||
asio::local::stream_protocol::socket secondary_socket(
|
||||
io_context_);
|
||||
secondary_socket.connect(endpoint_);
|
||||
|
||||
return callback(secondary_socket);
|
||||
} catch (const boost::system::system_error&) {
|
||||
} catch (const std::system_error&) {
|
||||
// So, what do we do when noone is listening on the endpoint
|
||||
// yet? This can happen with plugin groups when the Wine
|
||||
// host process does an `audioMaster()` call before the
|
||||
@@ -721,8 +717,8 @@ class AdHocSocketHandler {
|
||||
* same thing as `primary_callback`, but secondary sockets may need some
|
||||
* different handling.
|
||||
*/
|
||||
template <std::invocable<boost::asio::local::stream_protocol::socket&> F,
|
||||
std::invocable<boost::asio::local::stream_protocol::socket&> G>
|
||||
template <std::invocable<asio::local::stream_protocol::socket&> F,
|
||||
std::invocable<asio::local::stream_protocol::socket&> G>
|
||||
void receive_multi(std::optional<std::reference_wrapper<Logger>> logger,
|
||||
F&& primary_callback,
|
||||
G&& secondary_callback) {
|
||||
@@ -738,7 +734,7 @@ class AdHocSocketHandler {
|
||||
// thread to handle the request. When `socket` closes and this loop
|
||||
// breaks, the listener and any still active threads will be cleaned up
|
||||
// before this function exits.
|
||||
boost::asio::io_context secondary_context{};
|
||||
asio::io_context secondary_context{};
|
||||
|
||||
// The previous acceptor has already been shut down by
|
||||
// `AdHocSocketHandler::connect()`
|
||||
@@ -751,21 +747,21 @@ class AdHocSocketHandler {
|
||||
std::mutex active_secondary_requests_mutex{};
|
||||
accept_requests(
|
||||
*acceptor_, logger,
|
||||
[&](boost::asio::local::stream_protocol::socket secondary_socket) {
|
||||
[&](asio::local::stream_protocol::socket secondary_socket) {
|
||||
const size_t request_id = next_request_id.fetch_add(1);
|
||||
|
||||
// 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] = Thread(
|
||||
[&, request_id](boost::asio::local::stream_protocol::socket
|
||||
secondary_socket) {
|
||||
[&, request_id](
|
||||
asio::local::stream_protocol::socket secondary_socket) {
|
||||
secondary_callback(secondary_socket);
|
||||
|
||||
// When we have processed this request, we'll join the
|
||||
// thread again with the thread that's handling
|
||||
// `secondary_context`
|
||||
boost::asio::post(secondary_context, [&, request_id]() {
|
||||
asio::post(secondary_context, [&, request_id]() {
|
||||
std::lock_guard lock(
|
||||
active_secondary_requests_mutex);
|
||||
|
||||
@@ -791,7 +787,7 @@ class AdHocSocketHandler {
|
||||
while (true) {
|
||||
try {
|
||||
primary_callback(socket_);
|
||||
} catch (const boost::system::system_error&) {
|
||||
} catch (const std::system_error&) {
|
||||
// This happens when the sockets got closed because the plugin
|
||||
// is being shut down
|
||||
break;
|
||||
@@ -814,7 +810,7 @@ class AdHocSocketHandler {
|
||||
*
|
||||
* @overload
|
||||
*/
|
||||
template <std::invocable<boost::asio::local::stream_protocol::socket&> F>
|
||||
template <std::invocable<asio::local::stream_protocol::socket&> F>
|
||||
void receive_multi(std::optional<std::reference_wrapper<Logger>> logger,
|
||||
F&& callback) {
|
||||
receive_multi(logger, callback, std::forward<F>(callback));
|
||||
@@ -831,16 +827,15 @@ class AdHocSocketHandler {
|
||||
* should only be passed on the plugin side.
|
||||
* @param callback A function that handles the new socket connection.
|
||||
*/
|
||||
template <std::invocable<boost::asio::local::stream_protocol::socket> F>
|
||||
void accept_requests(
|
||||
boost::asio::local::stream_protocol::acceptor& acceptor,
|
||||
std::optional<std::reference_wrapper<Logger>> logger,
|
||||
F&& callback) {
|
||||
template <std::invocable<asio::local::stream_protocol::socket> F>
|
||||
void accept_requests(asio::local::stream_protocol::acceptor& acceptor,
|
||||
std::optional<std::reference_wrapper<Logger>> logger,
|
||||
F&& callback) {
|
||||
acceptor.async_accept(
|
||||
[&, logger, callback](
|
||||
const boost::system::error_code& error,
|
||||
boost::asio::local::stream_protocol::socket secondary_socket) {
|
||||
if (error.failed()) {
|
||||
const std::error_code& error,
|
||||
asio::local::stream_protocol::socket secondary_socket) {
|
||||
if (error) {
|
||||
// On the Wine side it's expected that the primary socket
|
||||
// connection will be dropped during shutdown, so we can
|
||||
// silently ignore any related socket errors on the Wine
|
||||
@@ -865,10 +860,10 @@ class AdHocSocketHandler {
|
||||
* bound to this context. In `receive_multi()` we'll create a new IO context
|
||||
* since we want to do all listening there on a dedicated thread.
|
||||
*/
|
||||
boost::asio::io_context& io_context_;
|
||||
asio::io_context& io_context_;
|
||||
|
||||
boost::asio::local::stream_protocol::endpoint endpoint_;
|
||||
boost::asio::local::stream_protocol::socket socket_;
|
||||
asio::local::stream_protocol::endpoint endpoint_;
|
||||
asio::local::stream_protocol::socket socket_;
|
||||
|
||||
/**
|
||||
* This acceptor will be used once synchronously on the listening side
|
||||
@@ -880,7 +875,7 @@ class AdHocSocketHandler {
|
||||
* but all additional incoming connections of course have to be listened for
|
||||
* on the plugin side.
|
||||
*/
|
||||
std::optional<boost::asio::local::stream_protocol::acceptor> acceptor_;
|
||||
std::optional<asio::local::stream_protocol::acceptor> acceptor_;
|
||||
|
||||
/**
|
||||
* After the socket gets closed, we do some cleanup at the end of
|
||||
|
||||
@@ -73,7 +73,7 @@ intptr_t DefaultDataConverter::return_value(const int /*opcode*/,
|
||||
}
|
||||
|
||||
Vst2EventResult DefaultDataConverter::send_event(
|
||||
boost::asio::local::stream_protocol::socket& socket,
|
||||
asio::local::stream_protocol::socket& socket,
|
||||
const Vst2Event& event,
|
||||
SerializationBufferBase& buffer) const {
|
||||
write_object(socket, event, buffer);
|
||||
|
||||
@@ -86,7 +86,7 @@ class DefaultDataConverter {
|
||||
* specific opcodes to allow mutually recursive calling sequences.
|
||||
*/
|
||||
virtual Vst2EventResult send_event(
|
||||
boost::asio::local::stream_protocol::socket& socket,
|
||||
asio::local::stream_protocol::socket& socket,
|
||||
const Vst2Event& event,
|
||||
SerializationBufferBase& buffer) const;
|
||||
};
|
||||
@@ -137,8 +137,8 @@ class Vst2EventHandler : public AdHocSocketHandler<Thread> {
|
||||
*
|
||||
* @see Sockets::connect
|
||||
*/
|
||||
Vst2EventHandler(boost::asio::io_context& io_context,
|
||||
boost::asio::local::stream_protocol::endpoint endpoint,
|
||||
Vst2EventHandler(asio::io_context& io_context,
|
||||
asio::local::stream_protocol::endpoint endpoint,
|
||||
bool listen)
|
||||
: AdHocSocketHandler<Thread>(io_context, endpoint, listen) {}
|
||||
|
||||
@@ -206,7 +206,7 @@ class Vst2EventHandler : public AdHocSocketHandler<Thread> {
|
||||
// that potentially need to have their responses handled on the same
|
||||
// calling thread (i.e. mutual recursion).
|
||||
const Vst2EventResult response = this->send(
|
||||
[&](boost::asio::local::stream_protocol::socket& socket) {
|
||||
[&](asio::local::stream_protocol::socket& socket) {
|
||||
return data_converter.send_event(socket, event,
|
||||
serialization_buffer());
|
||||
});
|
||||
@@ -250,7 +250,7 @@ class Vst2EventHandler : public AdHocSocketHandler<Thread> {
|
||||
// Reading, processing, and writing back event data from the sockets
|
||||
// works in the same way regardless of which socket we're using
|
||||
const auto process_event =
|
||||
[&](boost::asio::local::stream_protocol::socket& socket,
|
||||
[&](asio::local::stream_protocol::socket& socket,
|
||||
bool on_main_thread) {
|
||||
SerializationBufferBase& buffer = serialization_buffer();
|
||||
|
||||
@@ -276,10 +276,10 @@ class Vst2EventHandler : public AdHocSocketHandler<Thread> {
|
||||
this->receive_multi(
|
||||
logging ? std::optional(std::ref(logging->first.logger_))
|
||||
: std::nullopt,
|
||||
[&](boost::asio::local::stream_protocol::socket& socket) {
|
||||
[&](asio::local::stream_protocol::socket& socket) {
|
||||
process_event(socket, true);
|
||||
},
|
||||
[&](boost::asio::local::stream_protocol::socket& socket) {
|
||||
[&](asio::local::stream_protocol::socket& socket) {
|
||||
process_event(socket, false);
|
||||
});
|
||||
}
|
||||
@@ -342,7 +342,7 @@ class Vst2Sockets final : public Sockets {
|
||||
*
|
||||
* @see Vst2Sockets::connect
|
||||
*/
|
||||
Vst2Sockets(boost::asio::io_context& io_context,
|
||||
Vst2Sockets(asio::io_context& io_context,
|
||||
const ghc::filesystem::path& endpoint_base_dir,
|
||||
bool listen)
|
||||
: Sockets(endpoint_base_dir),
|
||||
|
||||
@@ -57,8 +57,8 @@ class Vst3MessageHandler : public AdHocSocketHandler<Thread> {
|
||||
*
|
||||
* @see Sockets::connect
|
||||
*/
|
||||
Vst3MessageHandler(boost::asio::io_context& io_context,
|
||||
boost::asio::local::stream_protocol::endpoint endpoint,
|
||||
Vst3MessageHandler(asio::io_context& io_context,
|
||||
asio::local::stream_protocol::endpoint endpoint,
|
||||
bool listen)
|
||||
: AdHocSocketHandler<Thread>(io_context, endpoint, listen) {}
|
||||
|
||||
@@ -138,7 +138,7 @@ class Vst3MessageHandler : public AdHocSocketHandler<Thread> {
|
||||
// messages from arriving out of order. `AdHocSocketHandler::send()`
|
||||
// will either use a long-living primary socket, or if that's currently
|
||||
// in use it will spawn a new socket for us.
|
||||
this->send([&](boost::asio::local::stream_protocol::socket& socket) {
|
||||
this->send([&](asio::local::stream_protocol::socket& socket) {
|
||||
write_object(socket, Request(object), buffer);
|
||||
read_object<TResponse>(socket, response_object, buffer);
|
||||
});
|
||||
@@ -205,7 +205,7 @@ class Vst3MessageHandler : public AdHocSocketHandler<Thread> {
|
||||
// we receive works in the same way regardless of which socket we're
|
||||
// using
|
||||
const auto process_message =
|
||||
[&](boost::asio::local::stream_protocol::socket& socket) {
|
||||
[&](asio::local::stream_protocol::socket& socket) {
|
||||
// The persistent buffer is only used when the
|
||||
// `persistent_buffers` template value is enabled, but we'll
|
||||
// always use the thread local persistent object. Because of
|
||||
@@ -310,7 +310,7 @@ class Vst3Sockets final : public Sockets {
|
||||
*
|
||||
* @see Vst3Sockets::connect
|
||||
*/
|
||||
Vst3Sockets(boost::asio::io_context& io_context,
|
||||
Vst3Sockets(asio::io_context& io_context,
|
||||
const ghc::filesystem::path& endpoint_base_dir,
|
||||
bool listen)
|
||||
: Sockets(endpoint_base_dir),
|
||||
@@ -513,7 +513,7 @@ class Vst3Sockets final : public Sockets {
|
||||
audio_processor_buffer);
|
||||
}
|
||||
|
||||
boost::asio::io_context& io_context_;
|
||||
asio::io_context& io_context_;
|
||||
|
||||
/**
|
||||
* Every `IAudioProcessor` or `IComponent` instance (which likely implements
|
||||
|
||||
+208
-11
@@ -21,12 +21,21 @@
|
||||
#include <ostream>
|
||||
|
||||
#ifdef __WINE__
|
||||
#include "../wine-host/boost-fix.h"
|
||||
#include "../wine-host/asio-fix.h"
|
||||
#endif
|
||||
|
||||
#include <boost/asio/read_until.hpp>
|
||||
#include <boost/asio/streambuf.hpp>
|
||||
#include <asio/read_until.hpp>
|
||||
#include <asio/streambuf.hpp>
|
||||
|
||||
// FIXME: Remove when we get rid of the patched_async_pipe
|
||||
#include <asio/io_context.hpp>
|
||||
#include <asio/posix/stream_descriptor.hpp>
|
||||
#include <asio/post.hpp>
|
||||
|
||||
// FIXME: Get rid of Boost.Process and all of the wrangling below
|
||||
#include <boost/process/async_pipe.hpp>
|
||||
#include <boost/process/detail/posix/pipe_out.hpp>
|
||||
#include <boost/process/posix.hpp>
|
||||
|
||||
#include "../utils.h"
|
||||
|
||||
@@ -39,12 +48,200 @@
|
||||
*
|
||||
* Check if this is still needed for other distros after Arch starts packaging
|
||||
* Boost 1.73.
|
||||
*
|
||||
* FIXME: This has been adopted to work with standalone Asio, we should replace
|
||||
* this when we replace Boost.Process
|
||||
*/
|
||||
class patched_async_pipe : public boost::process::async_pipe {
|
||||
public:
|
||||
using boost::process::async_pipe::async_pipe;
|
||||
class patched_async_pipe {
|
||||
::asio::posix::stream_descriptor _source;
|
||||
::asio::posix::stream_descriptor _sink;
|
||||
|
||||
public:
|
||||
typedef int native_handle_type;
|
||||
typedef ::asio::posix::stream_descriptor handle_type;
|
||||
typedef typename handle_type::executor_type executor_type;
|
||||
|
||||
executor_type get_executor() { return _source.get_executor(); }
|
||||
|
||||
inline patched_async_pipe(asio::io_context& ios)
|
||||
: patched_async_pipe(ios, ios) {}
|
||||
|
||||
inline patched_async_pipe(asio::io_context& ios_source,
|
||||
asio::io_context& ios_sink)
|
||||
: _source(ios_source), _sink(ios_sink) {
|
||||
int fds[2];
|
||||
if (::pipe(fds) == -1)
|
||||
boost::process::detail::throw_last_error("pipe(2) failed");
|
||||
|
||||
_source.assign(fds[0]);
|
||||
_sink.assign(fds[1]);
|
||||
};
|
||||
|
||||
inline patched_async_pipe(const patched_async_pipe& lhs);
|
||||
patched_async_pipe(patched_async_pipe&& lhs)
|
||||
: _source(std::move(lhs._source)), _sink(std::move(lhs._sink)) {
|
||||
lhs._source =
|
||||
::asio::posix::stream_descriptor{lhs._source.get_executor()};
|
||||
lhs._sink = ::asio::posix::stream_descriptor{lhs._sink.get_executor()};
|
||||
}
|
||||
|
||||
template <class CharT, class Traits = std::char_traits<CharT>>
|
||||
explicit patched_async_pipe(
|
||||
::asio::io_context& ios_source,
|
||||
::asio::io_context& ios_sink,
|
||||
const boost::process::detail::posix::basic_pipe<CharT, Traits>& p)
|
||||
: _source(ios_source, p.native_source()),
|
||||
_sink(ios_sink, p.native_sink()) {}
|
||||
|
||||
template <class CharT, class Traits = std::char_traits<CharT>>
|
||||
explicit patched_async_pipe(
|
||||
asio::io_context& ios,
|
||||
const boost::process::detail::posix::basic_pipe<CharT, Traits>& p)
|
||||
: patched_async_pipe(ios, ios, p) {}
|
||||
|
||||
template <class CharT, class Traits = std::char_traits<CharT>>
|
||||
inline patched_async_pipe& operator=(
|
||||
const boost::process::detail::posix::basic_pipe<CharT, Traits>& p);
|
||||
inline patched_async_pipe& operator=(const patched_async_pipe& rhs);
|
||||
|
||||
inline patched_async_pipe& operator=(patched_async_pipe&& lhs);
|
||||
|
||||
~patched_async_pipe() {
|
||||
std::error_code ec;
|
||||
close(ec);
|
||||
}
|
||||
|
||||
template <class CharT, class Traits = std::char_traits<CharT>>
|
||||
inline explicit
|
||||
operator boost::process::detail::posix::basic_pipe<CharT, Traits>() const;
|
||||
|
||||
void cancel() {
|
||||
if (_sink.is_open())
|
||||
_sink.cancel();
|
||||
if (_source.is_open())
|
||||
_source.cancel();
|
||||
}
|
||||
|
||||
void close() {
|
||||
if (_sink.is_open())
|
||||
_sink.close();
|
||||
if (_source.is_open())
|
||||
_source.close();
|
||||
}
|
||||
void close(std::error_code& ec) {
|
||||
if (_sink.is_open())
|
||||
_sink.close(ec);
|
||||
if (_source.is_open())
|
||||
_source.close(ec);
|
||||
}
|
||||
|
||||
bool is_open() const { return _sink.is_open() || _source.is_open(); }
|
||||
void async_close() {
|
||||
if (_sink.is_open())
|
||||
asio::post(_sink.get_executor(), [this] { _sink.close(); });
|
||||
if (_source.is_open())
|
||||
asio::post(_source.get_executor(), [this] { _source.close(); });
|
||||
}
|
||||
|
||||
template <typename MutableBufferSequence>
|
||||
std::size_t read_some(const MutableBufferSequence& buffers) {
|
||||
return _source.read_some(buffers);
|
||||
}
|
||||
template <typename MutableBufferSequence>
|
||||
std::size_t write_some(const MutableBufferSequence& buffers) {
|
||||
return _sink.write_some(buffers);
|
||||
}
|
||||
|
||||
template <typename MutableBufferSequence>
|
||||
std::size_t read_some(const MutableBufferSequence& buffers,
|
||||
std::error_code& ec) noexcept {
|
||||
return _source.read_some(buffers, ec);
|
||||
}
|
||||
template <typename MutableBufferSequence>
|
||||
std::size_t write_some(const MutableBufferSequence& buffers,
|
||||
std::error_code& ec) noexcept {
|
||||
return _sink.write_some(buffers, ec);
|
||||
}
|
||||
|
||||
native_handle_type native_source() const {
|
||||
return const_cast<asio::posix::stream_descriptor&>(_source)
|
||||
.native_handle();
|
||||
}
|
||||
native_handle_type native_sink() const {
|
||||
return const_cast<asio::posix::stream_descriptor&>(_sink)
|
||||
.native_handle();
|
||||
}
|
||||
|
||||
template <typename MutableBufferSequence, typename ReadHandler>
|
||||
ASIO_INITFN_RESULT_TYPE(ReadHandler, void(std::error_code, std::size_t))
|
||||
async_read_some(const MutableBufferSequence& buffers,
|
||||
ReadHandler&& handler) {
|
||||
return _source.async_read_some(buffers,
|
||||
std::forward<ReadHandler>(handler));
|
||||
}
|
||||
|
||||
template <typename ConstBufferSequence, typename WriteHandler>
|
||||
ASIO_INITFN_RESULT_TYPE(WriteHandler, void(std::error_code, std::size_t))
|
||||
async_write_some(const ConstBufferSequence& buffers,
|
||||
WriteHandler&& handler) {
|
||||
return _sink.async_write_some(buffers,
|
||||
std::forward<WriteHandler>(handler));
|
||||
}
|
||||
|
||||
const handle_type& sink() const& { return _sink; }
|
||||
const handle_type& source() const& { return _source; }
|
||||
|
||||
handle_type&& sink() && { return std::move(_sink); }
|
||||
handle_type&& source() && { return std::move(_source); }
|
||||
|
||||
handle_type source(::asio::io_context& ios) && {
|
||||
::asio::posix::stream_descriptor stolen(ios, _source.release());
|
||||
return stolen;
|
||||
}
|
||||
handle_type sink(::asio::io_context& ios) && {
|
||||
::asio::posix::stream_descriptor stolen(ios, _sink.release());
|
||||
return stolen;
|
||||
}
|
||||
|
||||
handle_type source(::asio::io_context& ios) const& {
|
||||
auto source_in = const_cast<::asio::posix::stream_descriptor&>(_source)
|
||||
.native_handle();
|
||||
return ::asio::posix::stream_descriptor(ios, ::dup(source_in));
|
||||
}
|
||||
handle_type sink(::asio::io_context& ios) const& {
|
||||
auto sink_in = const_cast<::asio::posix::stream_descriptor&>(_sink)
|
||||
.native_handle();
|
||||
return ::asio::posix::stream_descriptor(ios, ::dup(sink_in));
|
||||
}
|
||||
};
|
||||
|
||||
// Even more of a mess, we can't use the nice `bp::std_out = ...`/`bp::std_err =
|
||||
// ...` syntax anymore.
|
||||
template <int p1, int p2>
|
||||
struct patched_async_pipe_out
|
||||
: public boost::process::detail::posix::pipe_out<p1, p2> {
|
||||
patched_async_pipe& pipe;
|
||||
template <typename AsyncPipe>
|
||||
patched_async_pipe_out(AsyncPipe& p)
|
||||
: boost::process::detail::posix::pipe_out<p1, p2>(p.native_sink(),
|
||||
p.native_source()),
|
||||
pipe(p) {}
|
||||
|
||||
template <typename Pipe, typename Executor>
|
||||
static void close(Pipe& pipe, Executor&) {
|
||||
std::error_code ec;
|
||||
std::move(pipe).sink().close(ec);
|
||||
}
|
||||
|
||||
template <typename Executor>
|
||||
void on_error(Executor& exec, const std::error_code&) {
|
||||
close(pipe, exec);
|
||||
}
|
||||
|
||||
template <typename Executor>
|
||||
void on_success(Executor& exec) {
|
||||
close(pipe, exec);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -160,21 +357,21 @@ class Logger {
|
||||
* Write output from an async pipe to the log on a line by line basis.
|
||||
* Useful for logging the Wine process's STDOUT and STDERR streams.
|
||||
*
|
||||
* @param pipe Some Boost.Asio stream that can be read from. Probably either
|
||||
* @param pipe Some Asio stream that can be read from. Probably either
|
||||
* `patched_async_pipe` or a stream descriptor.
|
||||
* @param buffer The buffer that will be used to read from `pipe`.
|
||||
* @param prefix Text to prepend to the line before writing to the log.
|
||||
*/
|
||||
template <typename T>
|
||||
void async_log_pipe_lines(T& pipe,
|
||||
boost::asio::streambuf& buffer,
|
||||
asio::streambuf& buffer,
|
||||
std::string prefix = "") {
|
||||
boost::asio::async_read_until(
|
||||
asio::async_read_until(
|
||||
pipe, buffer, '\n',
|
||||
[&, prefix](const boost::system::error_code& error, size_t) {
|
||||
[&, prefix](const std::error_code& error, size_t) {
|
||||
// When we get an error code then that likely means that the
|
||||
// pipe has been clsoed and we have reached the end of the file
|
||||
if (error.failed()) {
|
||||
if (error) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -21,10 +21,10 @@
|
||||
#include <type_traits>
|
||||
|
||||
#ifdef __WINE__
|
||||
#include "../wine-host/boost-fix.h"
|
||||
#include "../wine-host/asio-fix.h"
|
||||
#endif
|
||||
#include <boost/asio/dispatch.hpp>
|
||||
#include <boost/asio/io_context.hpp>
|
||||
#include <asio/dispatch.hpp>
|
||||
#include <asio/io_context.hpp>
|
||||
|
||||
/**
|
||||
* A helper to allow mutually recursive calling sequences with remote function
|
||||
@@ -83,8 +83,8 @@ class MutualRecursionHelper {
|
||||
// as we need to support multiple levels of mutual recursion. This can
|
||||
// for instance happen during `IPlugView::attached() ->
|
||||
// IPlugFrame::resizeView() -> IPlugView::onSize()`.
|
||||
std::shared_ptr<boost::asio::io_context> current_io_context =
|
||||
std::make_shared<boost::asio::io_context>();
|
||||
std::shared_ptr<asio::io_context> current_io_context =
|
||||
std::make_shared<asio::io_context>();
|
||||
{
|
||||
std::unique_lock lock(mutual_recursion_contexts_mutex_);
|
||||
mutual_recursion_contexts_.push_back(current_io_context);
|
||||
@@ -93,7 +93,7 @@ class MutualRecursionHelper {
|
||||
// Instead of directly stopping the IO context, we'll reset this work
|
||||
// guard instead. This prevents us from accidentally cancelling any
|
||||
// outstanding tasks.
|
||||
auto work_guard = boost::asio::make_work_guard(*current_io_context);
|
||||
auto work_guard = asio::make_work_guard(*current_io_context);
|
||||
|
||||
// We will call the function from another thread so we can handle calls
|
||||
// to `handle()`/`maybe_handle()` from this thread
|
||||
@@ -168,7 +168,7 @@ class MutualRecursionHelper {
|
||||
// pretend that we're not doing any async things here
|
||||
std::packaged_task<Result()> do_call(std::forward<F>(fn));
|
||||
std::future<Result> do_call_response = do_call.get_future();
|
||||
boost::asio::dispatch(*mutual_recursion_contexts_.back(),
|
||||
asio::dispatch(*mutual_recursion_contexts_.back(),
|
||||
std::move(do_call));
|
||||
mutual_recursion_lock.unlock();
|
||||
|
||||
@@ -186,7 +186,7 @@ class MutualRecursionHelper {
|
||||
* active one. If the stack is empty, then there's currently no mutual
|
||||
* recursion going on.
|
||||
*/
|
||||
std::vector<std::shared_ptr<boost::asio::io_context>>
|
||||
std::vector<std::shared_ptr<asio::io_context>>
|
||||
mutual_recursion_contexts_;
|
||||
std::mutex mutual_recursion_contexts_mutex_;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user