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:
Robbert van der Helm
2022-04-10 21:29:14 +02:00
parent d9ab04ba35
commit 556b0e38f9
34 changed files with 465 additions and 200 deletions
+2
View File
@@ -23,6 +23,8 @@ Versioning](https://semver.org/spec/v2.0.0.html).
- Added a dependency on the headers-only
[`ghc::filesystem`](https://github.com/gulrak/filesystem) library to replace
Boost.Filesystem.
- Added a dependency on the headers-only [Asio](http://think-async.com/Asio/)
library to replace Boost.Asio.
- Fixed a deprecation warning in the Meson build, causing the minimum supported
Meson version to be bumped up to **Meson 0.56** from 0.55.
+1
View File
@@ -770,6 +770,7 @@ the following dependencies:
The following dependencies are included in the repository as a Meson wrap:
- [Asio](http://think-async.com/Asio/)
- [bitsery](https://github.com/fraillt/bitsery)
- [function2](https://github.com/Naios/function2)
- [`ghc::filesystem`](https://github.com/gulrak/filesystem)
+4 -1
View File
@@ -46,7 +46,7 @@ compiler_options = [
'-fvisibility-inlines-hidden',
# Disable the use of concepts in Boost.Asio until Boost 1.73 gets released
# https://github.com/boostorg/asio/issues/312
# TODO: Rename after switching to non-Boost ASIO
# TODO: Rename after switching out Boost.Process
'-DBOOST_ASIO_DISABLE_CONCEPTS',
# Boost.Process's auto detection for vfork() support doesn't seem to work
# TODO: Remove after adding our own library
@@ -232,6 +232,7 @@ endif
# These are all headers-only libraries, and thus won't require separate 32-bit
# and 64-bit versions
asio_dep = dependency('asio', version : '>=1.22.0')
boost_dep = dependency('boost', version : '>=1.66', static : with_static_boost)
if meson.version().version_compare('>=0.60')
# Bitsery's CMake build definition is capitalized for some reason
@@ -295,6 +296,7 @@ shared_library(
dependencies : [
configuration_dep,
asio_dep,
boost_dep,
with_32bit_libraries
? boost_filesystem_32bit_dep
@@ -320,6 +322,7 @@ if with_vst3
dependencies : [
configuration_dep,
asio_dep,
boost_dep,
with_32bit_libraries
? boost_filesystem_32bit_dep
+1 -1
View File
@@ -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>
+1
View File
@@ -17,6 +17,7 @@
#include "common.h"
#include <random>
#include <sstream>
#include "../utils.h"
+69 -74
View File
@@ -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
+1 -1
View File
@@ -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);
+8 -8
View File
@@ -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),
+6 -6
View File
@@ -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
View File
@@ -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;
}
+8 -8
View File
@@ -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_;
};
+4 -4
View File
@@ -74,7 +74,7 @@ class PluginBridge {
* found, or if it could not locate and load a VST3 module.
*/
template <invocable_returning<TSockets,
boost::asio::io_context&,
asio::io_context&,
const PluginInfo&> F>
PluginBridge(PluginType plugin_type, F&& create_socket_instance)
// This is still correct for VST3 plugins because we can configure an
@@ -367,7 +367,7 @@ class PluginBridge {
// sockets and we'll be hanging here indefinitely. To prevent this,
// we'll periodically poll whether the Wine process is still running,
// and throw when it is not. The alternative would be to rewrite this to
// using `async_accept`, Boost.Asio timers, and another IO context, but
// using `async_accept`, Asio timers, and another IO context, but
// I feel like this a much simpler solution.
host_watchdog_handler_ = std::jthread([&](std::stop_token st) {
using namespace std::literals::chrono_literals;
@@ -441,7 +441,7 @@ class PluginBridge {
*/
const PluginInfo info_;
boost::asio::io_context io_context_;
asio::io_context io_context_;
/**
* The sockets used for communication with the Wine process.
@@ -485,7 +485,7 @@ class PluginBridge {
std::future<bool> has_realtime_priority_;
/**
* Runs the Boost.Asio `io_context_` thread for logging the Wine process
* Runs the Asio `io_context_` thread for logging the Wine process
* STDOUT and STDERR messages.
*/
std::jthread wine_io_handler_;
+3 -3
View File
@@ -38,7 +38,7 @@ Vst2PluginBridge& get_bridge_instance(const AEffect& plugin) noexcept {
Vst2PluginBridge::Vst2PluginBridge(audioMasterCallback host_callback)
: PluginBridge(
PluginType::vst2,
[](boost::asio::io_context& io_context, const PluginInfo& info) {
[](asio::io_context& io_context, const PluginInfo& info) {
return Vst2Sockets<std::jthread>(
io_context,
generate_endpoint_base(info.native_library_path_.filename()
@@ -199,7 +199,7 @@ Vst2PluginBridge::~Vst2PluginBridge() noexcept {
// The `stop()` method will cause the IO context to just drop all of its
// outstanding work immediately
io_context_.stop();
} catch (const boost::system::system_error&) {
} catch (const std::system_error&) {
// It could be that the sockets have already been closed or that the
// process has already exited (at which point we probably won't be
// executing this, but maybe if all the stars align)
@@ -538,7 +538,7 @@ intptr_t Vst2PluginBridge::dispatch(AEffect* /*plugin*/,
return_value = sockets_.host_vst_dispatch_.send_event(
converter, std::pair<Vst2Logger&, bool>(logger_, true),
opcode, index, value, data, option);
} catch (const boost::system::system_error&) {
} catch (const std::system_error&) {
// Thrown when the socket gets closed because the VST plugin
// loaded into the Wine process crashed during shutdown
logger_.log("The plugin crashed during shutdown, ignoring");
+1 -1
View File
@@ -18,7 +18,7 @@
#include <vestige/aeffectx.h>
#include <boost/asio/io_context.hpp>
#include <asio/io_context.hpp>
#include <thread>
#include "../../common/communication/vst2.h"
@@ -32,7 +32,7 @@ class RunLoopTasks : public Steinberg::Linux::IEventHandler {
public:
/**
* Register an event handler in the host's run loop so we can schedule tasks
* to be run from there. This works very much like how we use Boost.Asio IO
* to be run from there. This works very much like how we use Asio IO
* contexts everywhere else to run functions on other threads. All of this
* is backed by a dummy Unix domain socket, although REAPER will call the
* event handler regardless of whether the file descriptor is ready or not.
+2 -2
View File
@@ -27,7 +27,7 @@ using namespace std::literals::string_literals;
Vst3PluginBridge::Vst3PluginBridge()
: PluginBridge(
PluginType::vst3,
[](boost::asio::io_context& io_context, const PluginInfo& info) {
[](asio::io_context& io_context, const PluginInfo& info) {
return Vst3Sockets<std::jthread>(
io_context,
generate_endpoint_base(info.native_library_path_.filename()
@@ -415,7 +415,7 @@ Vst3PluginBridge::~Vst3PluginBridge() noexcept {
// Drop all work make sure all sockets are closed
plugin_host_->terminate();
io_context_.stop();
} catch (const boost::system::system_error&) {
} catch (const std::system_error&) {
// It could be that the sockets have already been closed or that the
// process has already exited (at which point we probably won't be
// executing this, but maybe if all the stars align)
+8 -8
View File
@@ -16,7 +16,7 @@
#include "host-process.h"
#include <boost/asio/read_until.hpp>
#include <asio/read_until.hpp>
#include <boost/process/env.hpp>
#include <boost/process/start_dir.hpp>
@@ -25,7 +25,7 @@
namespace bp = boost::process;
namespace fs = ghc::filesystem;
HostProcess::HostProcess(boost::asio::io_context& io_context,
HostProcess::HostProcess(asio::io_context& io_context,
Logger& logger,
const Configuration& config,
Sockets& sockets)
@@ -53,7 +53,7 @@ HostProcess::HostProcess(boost::asio::io_context& io_context,
HostProcess::~HostProcess() noexcept {}
IndividualHost::IndividualHost(boost::asio::io_context& io_context,
IndividualHost::IndividualHost(asio::io_context& io_context,
Logger& logger,
const Configuration& config,
Sockets& sockets,
@@ -115,7 +115,7 @@ void IndividualHost::terminate() {
host_.wait();
}
GroupHost::GroupHost(boost::asio::io_context& io_context,
GroupHost::GroupHost(asio::io_context& io_context,
Logger& logger,
const Configuration& config,
Sockets& sockets,
@@ -140,7 +140,7 @@ GroupHost::GroupHost(boost::asio::io_context& io_context,
plugin_info.plugin_arch_);
const auto connect = [&io_context, host_request, endpoint_base_dir,
group_socket_path]() {
boost::asio::local::stream_protocol::socket group_socket(io_context);
asio::local::stream_protocol::socket group_socket(io_context);
group_socket.connect(group_socket_path.string());
write_object(group_socket, host_request);
@@ -151,7 +151,7 @@ GroupHost::GroupHost(boost::asio::io_context& io_context,
try {
// Request an existing group host process to host our plugin
connect();
} catch (const boost::system::system_error&) {
} catch (const std::system_error&) {
// In case we could not connect to the socket, then we'll start a
// new group host process. This process is detached immediately
// because it should run independently of this yabridge instance as
@@ -178,7 +178,7 @@ GroupHost::GroupHost(boost::asio::io_context& io_context,
try {
connect();
return;
} catch (const boost::system::system_error&) {
} catch (const std::system_error&) {
// Keep trying to connect until either connection gets
// accepted or the group host crashes
}
@@ -191,7 +191,7 @@ GroupHost::GroupHost(boost::asio::io_context& io_context,
// connect once more, before concluding that we failed.
try {
connect();
} catch (const boost::system::system_error&) {
} catch (const std::system_error&) {
startup_failed_ = true;
}
});
+12 -9
View File
@@ -18,8 +18,8 @@
#include <thread>
#include <boost/asio/local/stream_protocol.hpp>
#include <boost/asio/streambuf.hpp>
#include <asio/local/stream_protocol.hpp>
#include <asio/streambuf.hpp>
#include <boost/process/child.hpp>
#include <boost/process/extend.hpp>
#include <boost/process/io.hpp>
@@ -91,8 +91,11 @@ class HostProcess {
// FIXME: Replace Boost.Filesystem
host_path.string(),
#endif // WITH_WINEDBG
boost::process::std_out = stdout_pipe_,
boost::process::std_err = stderr_pipe_,
// FIXME: This won't work with our patched async_pipe version
// boost::process::std_out = stdout_pipe_,
// boost::process::std_err = stderr_pipe_,
patched_async_pipe_out<1, -1>(stdout_pipe_),
patched_async_pipe_out<2, -1>(stderr_pipe_),
// NOTE: If the Wine process outlives the host, then it may cause
// issues if our process is still keeping the host's file
// descriptors alive that. This can prevent Ardour from
@@ -140,7 +143,7 @@ class HostProcess {
* 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,
HostProcess(asio::io_context& io_context,
Logger& logger,
const Configuration& config,
Sockets& sockets);
@@ -171,8 +174,8 @@ class HostProcess {
*/
Logger& logger_;
boost::asio::streambuf stdout_buffer_;
boost::asio::streambuf stderr_buffer_;
asio::streambuf stdout_buffer_;
asio::streambuf stderr_buffer_;
};
/**
@@ -201,7 +204,7 @@ class IndividualHost : public HostProcess {
* @throw std::runtime_error When `plugin_path` does not point to a valid
* 32-bit or 64-bit .dll file.
*/
IndividualHost(boost::asio::io_context& io_context,
IndividualHost(asio::io_context& io_context,
Logger& logger,
const Configuration& config,
Sockets& sockets,
@@ -249,7 +252,7 @@ class GroupHost : public HostProcess {
* @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.
*/
GroupHost(boost::asio::io_context& io_context,
GroupHost(asio::io_context& io_context,
Logger& logger,
const Configuration& config,
Sockets& sockets,
+1 -2
View File
@@ -22,7 +22,6 @@
#include <boost/dll/runtime_symbol_info.hpp>
#include <boost/process/io.hpp>
#include <boost/process/pipe.hpp>
#include <boost/process/posix.hpp>
#include <boost/process/search_path.hpp>
#include <boost/process/system.hpp>
#include <sstream>
@@ -466,7 +465,7 @@ bool send_notification(const std::string& title,
<< "\">"
<< xml_escape(this_library.filename().string())
<< "</a>";
} catch (const boost::system::system_error&) {
} catch (const std::system_error&) {
// I don't think this can fail in the way we're using it, but the
// last thing we want is our notification informing the user of an
// exception to trigger another exception
+52
View File
@@ -0,0 +1,52 @@
// yabridge: a Wine VST bridge
// Copyright (C) 2020-2022 Robbert van der Helm
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#pragma once
// Libraries like (Boost.)Asio think we're compiling on Windows or using a MSVC
// toolchain. This will cause them to make incorrect assumptions which platform
// specific features are available. The only way around this I could think of
// was to just temporarily undefine the macros these libraries use to detect
// it's running under a WIN32 environment. If anyone knows a better way to do
// this, please let me know!
#pragma push_macro("WIN32")
#pragma push_macro("_WIN32")
#pragma push_macro("__WIN32__")
#pragma push_macro("_WIN64")
#undef WIN32
#undef _WIN32
#undef __WIN32__
#undef _WIN64
// This would be the minimal include needed to get Asio to work. The commented
// out includes are the actual header that would cause compile errors if not
// included here, but including headers from the detail directory directly
// didn't sound like a great idea.
// FIXME: Remove Boost stuff
#include <boost/predef.h>
#include <asio/basic_socket_streambuf.hpp>
#include <boost/asio/basic_socket_streambuf.hpp>
#include <boost/interprocess/mapped_region.hpp>
// #include <asio/asio/detail/timer_queue_ptime.hpp>
// #include <boost/interprocess/detail/workaround.hpp>
#pragma pop_macro("WIN32")
#pragma pop_macro("_WIN32")
#pragma pop_macro("__WIN32__")
#pragma pop_macro("_WIN64")
+1 -1
View File
@@ -16,7 +16,7 @@
#pragma once
#include "../boost-fix.h"
#include "../asio-fix.h"
#include <ghc/filesystem.hpp>
+17 -19
View File
@@ -16,7 +16,7 @@
#include "group.h"
#include "../boost-fix.h"
#include "../asio-fix.h"
#include <unistd.h>
#include <regex>
@@ -49,23 +49,23 @@ using namespace std::literals::chrono_literals;
* @throw std::runtime_error If another process is already listening on the
* endpoint.
*/
boost::asio::local::stream_protocol::acceptor create_acceptor_if_inactive(
boost::asio::io_context& io_context,
boost::asio::local::stream_protocol::endpoint& endpoint);
asio::local::stream_protocol::acceptor create_acceptor_if_inactive(
asio::io_context& io_context,
asio::local::stream_protocol::endpoint& endpoint);
/**
* Create a logger prefix containing the group name based on the socket path.
*/
std::string create_logger_prefix(const fs::path& socket_path);
StdIoCapture::StdIoCapture(boost::asio::io_context& io_context,
int file_descriptor)
StdIoCapture::StdIoCapture(asio::io_context& io_context, int file_descriptor)
: pipe_(io_context),
target_fd_(file_descriptor),
original_fd_copy_(dup(file_descriptor)) {
// We'll use the second element of these two file descriptors to reopen
// `file_descriptor`, and the first one to read the captured contents from
if (::pipe(pipe_fd_) != 0) {
std::cerr << "Could not create pipe" << std::endl;
throw std::system_error(errno, std::system_category());
}
@@ -171,12 +171,12 @@ void GroupBridge::handle_incoming_connections() {
void GroupBridge::accept_requests() {
group_socket_acceptor_.async_accept(
[&](const boost::system::error_code& error,
boost::asio::local::stream_protocol::socket socket) {
[&](const std::error_code& error,
asio::local::stream_protocol::socket socket) {
std::lock_guard lock(active_plugins_mutex_);
// Stop the whole process when the socket gets closed unexpectedly
if (error.failed()) {
if (error) {
logger_.log("Error while listening for incoming connections:");
logger_.log(error.message());
@@ -284,14 +284,13 @@ void GroupBridge::async_handle_events() {
[&]() { return !is_event_loop_inhibited(); });
}
boost::asio::local::stream_protocol::acceptor create_acceptor_if_inactive(
boost::asio::io_context& io_context,
boost::asio::local::stream_protocol::endpoint& endpoint) {
asio::local::stream_protocol::acceptor create_acceptor_if_inactive(
asio::io_context& io_context,
asio::local::stream_protocol::endpoint& endpoint) {
// First try to listen on the endpoint normally
try {
return boost::asio::local::stream_protocol::acceptor(io_context,
endpoint);
} catch (const boost::system::system_error&) {
return asio::local::stream_protocol::acceptor(io_context, endpoint);
} catch (const std::system_error&) {
// If this failed, then either there is a stale socket file or another
// process is already is already listening. In the last case we will
// simply throw so the other process can handle the request.
@@ -312,8 +311,7 @@ boost::asio::local::stream_protocol::acceptor create_acceptor_if_inactive(
// At this point we can remove the stale socket and start listening
fs::remove(endpoint_path);
return boost::asio::local::stream_protocol::acceptor(io_context,
endpoint);
return asio::local::stream_protocol::acceptor(io_context, endpoint);
}
}
@@ -322,10 +320,10 @@ void GroupBridge::maybe_schedule_shutdown(
std::lock_guard lock(shutdown_timer_mutex_);
shutdown_timer_.expires_after(delay);
shutdown_timer_.async_wait([this](const boost::system::error_code& error) {
shutdown_timer_.async_wait([this](const std::error_code& error) {
// A previous timer gets canceled automatically when another plugin
// exits
if (error.failed()) {
if (error) {
return;
}
+12 -12
View File
@@ -19,9 +19,9 @@
#include <atomic>
#include <thread>
#include "../boost-fix.h"
#include "../asio-fix.h"
#include <boost/asio/local/stream_protocol.hpp>
#include <asio/local/stream_protocol.hpp>
#include "../common/logging/common.h"
#include "../utils.h"
@@ -49,7 +49,7 @@ class StdIoCapture {
*
* @throw std::system_error If the pipe could not be created.
*/
StdIoCapture(boost::asio::io_context& io_context, int file_descriptor);
StdIoCapture(asio::io_context& io_context, int file_descriptor);
/**
* On cleanup, close the outgoing file descriptor from the pipe and restore
@@ -65,9 +65,9 @@ class StdIoCapture {
/**
* The pipe endpoint where all output from the original file descriptor gets
* redirected to. This can be read from like any other `Boost.Asio` stream.
* redirected to. This can be read from like any other Asio stream.
*/
boost::asio::posix::stream_descriptor pipe_;
asio::posix::stream_descriptor pipe_;
private:
/**
@@ -116,7 +116,7 @@ class GroupBridge {
* where `<wine_prefix_id>` is a numerical hash as explained in the
* `create_logger_prefix()` function in `./group.cpp`.
*
* @throw boost::system::system_error If we can't listen on the socket.
* @throw std::system_error If we can't listen on the socket.
* @throw std::system_error If the pipe could not be created.
*
* @note Creating an `GroupBridge` instance has the side effect that the
@@ -220,10 +220,10 @@ class GroupBridge {
* related operation should be run from the same thread, we can't just add
* another thread to the main IO context.
*/
boost::asio::io_context stdio_context_;
asio::io_context stdio_context_;
boost::asio::streambuf stdout_buffer_;
boost::asio::streambuf stderr_buffer_;
asio::streambuf stdout_buffer_;
asio::streambuf stderr_buffer_;
/**
* Contains a pipe used for capturing this process's STDOUT stream. Needed
* to be able to process the output generated by Wine and plugins and to be
@@ -241,12 +241,12 @@ class GroupBridge {
*/
Win32Thread stdio_handler_;
boost::asio::local::stream_protocol::endpoint group_socket_endpoint_;
asio::local::stream_protocol::endpoint group_socket_endpoint_;
/**
* The UNIX domain socket acceptor that will be used to listen for incoming
* connections to spawn new plugins within this process.
*/
boost::asio::local::stream_protocol::acceptor group_socket_acceptor_;
asio::local::stream_protocol::acceptor group_socket_acceptor_;
/**
* A map of threads that are currently hosting a plugin within this process
@@ -281,7 +281,7 @@ class GroupBridge {
*
* @see handle_plugin_run
*/
boost::asio::steady_timer shutdown_timer_;
asio::steady_timer shutdown_timer_;
/**
* A mutex to prevent two threads from simultaneously modifying the shutdown
* timer when multiple plugins exit at the same time.
+1 -1
View File
@@ -623,7 +623,7 @@ class HostCallbackDataConverter : public DefaultDataConverter {
}
Vst2EventResult send_event(
boost::asio::local::stream_protocol::socket& socket,
asio::local::stream_protocol::socket& socket,
const Vst2Event& event,
SerializationBufferBase& buffer) const override {
if (mutually_recursive_callbacks.contains(event.opcode)) {
+1 -1
View File
@@ -16,7 +16,7 @@
#pragma once
#include "../boost-fix.h"
#include "../asio-fix.h"
#include <vestige/aeffectx.h>
#include <windows.h>
+5 -5
View File
@@ -228,17 +228,17 @@ DeferredWin32Window::~DeferredWin32Window() noexcept {
// `IPlugView::~IPlugView`. Delaying this seems to be a best of both
// worlds solution that works as expected in every host I've tested.
try {
std::shared_ptr<boost::asio::steady_timer> destroy_timer =
std::make_shared<boost::asio::steady_timer>(main_context_.context_);
std::shared_ptr<asio::steady_timer> destroy_timer =
std::make_shared<asio::steady_timer>(main_context_.context_);
destroy_timer->expires_after(1s);
// Note that we capture a copy of `destroy_timer` here. This way we
// don't have to manage the timer instance ourselves as it will just
// clean itself up after this lambda gets called.
destroy_timer->async_wait(
[destroy_timer, handle = handle_, x11_connection = x11_connection_](
const boost::system::error_code& error) {
if (error.failed()) {
[destroy_timer, handle = handle_,
x11_connection = x11_connection_](const std::error_code& error) {
if (error) {
return;
}
+4 -6
View File
@@ -76,21 +76,19 @@ __cdecl
// Blocks the main thread until all plugins have exited
bridge.handle_incoming_connections();
} catch (const boost::system::system_error& error) {
} catch (const std::system_error& error) {
// If another process is already listening on the socket, we'll just
// print a message and exit quietly. This could happen if the host
// starts multiple yabridge instances that all use the same plugin group
// at the same time.
// The same error is also used if we could not create a pipe. Since that
// error is so rare, we'll just print to STDERR before that happens to
// differentiate the two cases.
std::cerr << "Another process is already listening on this group's "
"socket, connecting to the existing process:"
<< std::endl;
std::cerr << error.what() << std::endl;
return 0;
} catch (const std::system_error& error) {
std::cerr << "Could not create pipe:" << std::endl;
std::cerr << error.what() << std::endl;
return 0;
}
+2
View File
@@ -10,6 +10,7 @@ if is_64bit_system
host_64bit_deps = [
configuration_dep,
asio_dep,
boost_dep,
bitsery_dep,
function2_dep,
@@ -35,6 +36,7 @@ if with_bitbridge
host_32bit_deps = [
configuration_dep,
asio_dep,
boost_dep,
ghc_filesystem_dep,
bitsery_dep,
+2 -2
View File
@@ -175,8 +175,8 @@ void MainContext::async_handle_watchdog_timer(
// Try to keep a steady framerate, but add in delays to let other events
// get handled if the GUI message handling somehow takes very long.
watchdog_timer_.expires_at(std::chrono::steady_clock::now() + interval);
watchdog_timer_.async_wait([&](const boost::system::error_code& error) {
if (error.failed()) {
watchdog_timer_.async_wait([&](const std::error_code& error) {
if (error) {
return;
}
+12 -12
View File
@@ -16,7 +16,7 @@
#pragma once
#include "boost-fix.h"
#include "asio-fix.h"
#include <future>
#include <memory>
@@ -24,8 +24,8 @@
#include <unordered_set>
#include <windows.h>
#include <boost/asio/dispatch.hpp>
#include <boost/asio/io_context.hpp>
#include <asio/dispatch.hpp>
#include <asio/io_context.hpp>
#include <function2/function2.hpp>
#include "../common/utils.h"
@@ -152,7 +152,7 @@ class Win32Timer {
};
/**
* A wrapper around `boost::asio::io_context()` to serve as the application's
* A wrapper around `asio::io_context()` to serve as the application's
* main IO context, run from the GUI thread. A single instance is shared for all
* plugins in a plugin group so that several important events can be handled on
* the main thread, which can be required because in the Win32 model all GUI
@@ -254,7 +254,7 @@ class MainContext {
std::packaged_task<Result()> call_fn(std::forward<F>(fn));
std::future<Result> result = call_fn.get_future();
boost::asio::dispatch(context_, std::move(call_fn));
asio::dispatch(context_, std::move(call_fn));
return result;
}
@@ -266,7 +266,7 @@ class MainContext {
*/
template <std::invocable F>
void schedule_task(F&& fn) {
boost::asio::post(context_, std::forward<F>(fn));
asio::post(context_, std::forward<F>(fn));
}
/**
@@ -294,8 +294,8 @@ class MainContext {
std::max(events_timer_.expiry() + timer_interval_,
std::chrono::steady_clock::now() + timer_interval_ / 4));
events_timer_.async_wait(
[&, handler, predicate](const boost::system::error_code& error) {
if (error.failed()) {
[&, handler, predicate](const std::error_code& error) {
if (error) {
return;
}
@@ -311,7 +311,7 @@ class MainContext {
* The raw IO context. Used to bind our sockets onto. Running things within
* this IO context should be done with the functions above.
*/
boost::asio::io_context context_;
asio::io_context context_;
private:
/**
@@ -334,7 +334,7 @@ class MainContext {
/**
* The timer used to periodically handle X11 events and Win32 messages.
*/
boost::asio::steady_timer events_timer_;
asio::steady_timer events_timer_;
/**
* The time between timer ticks in `async_handle_events`. This gets
@@ -349,7 +349,7 @@ class MainContext {
/**
* The IO context used for the watchdog described below.
*/
boost::asio::io_context watchdog_context_;
asio::io_context watchdog_context_;
/**
* The timer used to periodically check if the host processes are still
@@ -357,7 +357,7 @@ class MainContext {
* itself) when the host has exited and the sockets are somehow not closed
* yet..
*/
boost::asio::steady_timer watchdog_timer_;
asio::steady_timer watchdog_timer_;
/**
* All of the bridges we're watching as part of our watchdog. We're storing
+1 -1
View File
@@ -18,7 +18,7 @@
#include <memory>
#include "boost-fix.h"
#include "asio-fix.h"
// Use the native version of xcb
#pragma push_macro("_WIN32")
+2
View File
@@ -1,6 +1,7 @@
/*/*
# The above pattern doesn't match submodules
/asio
/bitsery
/function2
/ghc_filesystem
@@ -8,6 +9,7 @@
/vst3
# And we obviously don't want to ignore our overlays
!/packagefiles/asio
!/packagefiles/bitsery
!/packagefiles/function2
!/packagefiles/ghc_filesystem
+9
View File
@@ -0,0 +1,9 @@
[wrap-git]
url = https://github.com/chriskohlhoff/asio.git
# This is tag asio-1-22-1
revision = bba12d10501418fd3789ce01c9f86a77d37df7ed
depth = 1
patch_directory = asio
[provide]
asio = asio_dep
@@ -0,0 +1,3 @@
project('asio', 'cpp', version : '1.22.1')
asio_dep = declare_dependency(include_directories : include_directories('asio/include'))