Replace Boost.Interprocess for audio-shm

This commit is contained in:
Robbert van der Helm
2022-04-14 20:44:49 +02:00
parent b2b6a606ea
commit fd25010aca
4 changed files with 86 additions and 36 deletions
+10
View File
@@ -10,6 +10,16 @@ Versioning](https://semver.org/spec/v2.0.0.html).
### Changed
- Almost the entirety of yabridge's backend has had a rewrite to get rid of any
all dependencies on the Boost libraries to make packaging easier and to remove
the runtime dependency on Boost.Filesystem for distro packaged versions of
yabridge. This prevents yabridge from breaking when Boost gets updated
independently of the yabridge package.
- When mapping shared memory for audio and the user does not have permissions to
lock the memory, yabridge will now retry mapping the memory without locking it
instead of immediately terminating the process. An annoying desktop
notification will still be shown every time you load a plugin until you fix
this.
- `effProcessEvents` VST2 calls are now filtered out from the log when
`YABRIDGE_DEBUG_LEVEL` is set to 1.
+48 -21
View File
@@ -20,11 +20,17 @@
#include "logging/common.h"
using namespace std::literals::string_literals;
AudioShmBuffer::AudioShmBuffer(const Config& config)
: config_(config),
shm_(boost::interprocess::open_or_create,
config.name.c_str(),
boost::interprocess::read_write) {
shm_fd_(shm_open(config.name.c_str(), O_RDWR | O_CREAT, 0600)) {
if (shm_fd_ == -1) {
throw std::system_error(
std::error_code(errno, std::system_category()),
"Could not create shared memory object " + config_.name);
}
setup_mapping();
}
@@ -33,21 +39,23 @@ AudioShmBuffer::~AudioShmBuffer() noexcept {
// removed, so we'll do it on both sides to reduce the chance that we leak
// shared memory
if (!is_moved_) {
boost::interprocess::shared_memory_object::remove(config_.name.c_str());
munmap(shm_bytes_, config_.size);
close(shm_fd_);
shm_unlink(config_.name.c_str());
}
}
AudioShmBuffer::AudioShmBuffer(AudioShmBuffer&& o) noexcept
: config_(std::move(o.config_)),
shm_(std::move(o.shm_)),
buffer_(std::move(o.buffer_)) {
shm_fd_(std::move(o.shm_fd_)),
shm_bytes_(std::move(o.shm_bytes_)) {
o.is_moved_ = true;
}
AudioShmBuffer& AudioShmBuffer::operator=(AudioShmBuffer&& o) noexcept {
config_ = std::move(o.config_);
shm_ = std::move(o.shm_);
buffer_ = std::move(o.buffer_);
shm_fd_ = std::move(o.shm_fd_);
shm_bytes_ = std::move(o.shm_bytes_);
o.is_moved_ = true;
return *this;
@@ -65,17 +73,22 @@ void AudioShmBuffer::resize(const Config& new_config) {
}
void AudioShmBuffer::setup_mapping() {
try {
// Apparently you get a `Resource temporarily unavailable` when calling
// `ftruncate()` with a size of 0 on shared memory
if (config_.size > 0) {
shm_.truncate(config_.size);
buffer_ = boost::interprocess::mapped_region(
shm_, boost::interprocess::read_write, 0, config_.size, nullptr,
MAP_LOCKED);
}
} catch (const boost::interprocess::interprocess_exception& error) {
if (error.get_native_error() == EAGAIN) {
// Apparently you get a `Resource temporarily unavailable` when calling
// `ftruncate()` with a size of 0 on shared memory
if (config_.size > 0) {
// I don't think this can fail
assert(ftruncate(shm_fd_, config_.size) == 0);
// But this can, if the user does not have permissions to use (enough)
// locked emmory, we'll try it without locking memory and show a big
// obnoxious warning and try again without locking the memory.
uint8_t* old_shm_bytes = shm_bytes_;
shm_bytes_ = static_cast<uint8_t*>(
old_shm_bytes
? mremap(old_shm_bytes, shm_size_, config_.size, MREMAP_MAYMOVE)
: mmap(nullptr, config_.size, PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_LOCKED, shm_fd_, 0));
if (shm_bytes_ == MAP_FAILED) {
Logger logger = Logger::create_exception_logger();
logger.log("");
@@ -85,8 +98,22 @@ void AudioShmBuffer::setup_mapping() {
logger.log(" wiki for instructions on how to set up");
logger.log(" realtime privileges and memlock limits.");
logger.log("");
}
throw;
// Growing into a size that we cannot lock sounds like a super rare
// edge case, but let's handle it anyways
if (old_shm_bytes) {
assert(munmap(old_shm_bytes, shm_size_) == 0);
}
shm_bytes_ = static_cast<uint8_t*>(mmap(nullptr, config_.size,
PROT_READ | PROT_WRITE,
MAP_SHARED, shm_fd_, 0));
if (shm_bytes_ == MAP_FAILED) {
throw std::system_error(
std::error_code(errno, std::system_category()),
"Could not map shared memory");
}
}
}
shm_size_ = config_.size;
}
+28 -11
View File
@@ -16,13 +16,10 @@
#pragma once
#include <string>
#include <vector>
#ifdef __WINE__
#include "../wine-host/asio-fix.h"
#endif
#include <boost/interprocess/mapped_region.hpp>
#include <boost/interprocess/shared_memory_object.hpp>
#include <sys/mman.h>
/**
* A shared memory object that allows audio buffers to be shared between the
@@ -109,6 +106,9 @@ class AudioShmBuffer {
* Connect to or create the shared memory object and map it to this
* process's memory. The configuration is created on the Wine side using the
* process described in `Config`'s docstring.
*
* @throw std::system_error If the shared memory object could not be
* created or mapped.
*/
AudioShmBuffer(const Config& config);
@@ -131,6 +131,7 @@ class AudioShmBuffer {
*
* @throw `std::invalid_argument` If the config is for a buffer with a
* different name.
* @throw std::system_error If the shared memory object could not be mapped.
*/
void resize(const Config& new_config);
@@ -148,15 +149,17 @@ class AudioShmBuffer {
* addresses might change after a call to `resize()`.
*/
template <typename T>
// NOLINTNEXTLINE(bugprone-easily-swappable-parameters)
T* input_channel_ptr(const uint32_t bus, const uint32_t channel) noexcept {
return reinterpret_cast<T*>(buffer_.get_address()) +
return reinterpret_cast<T*>(shm_bytes_) +
config_.input_offsets[bus][channel];
}
template <typename T>
// NOLINTNEXTLINE(bugprone-easily-swappable-parameters)
const T* input_channel_ptr(const uint32_t bus,
const uint32_t channel) const noexcept {
return reinterpret_cast<const T*>(buffer_.get_address()) +
return reinterpret_cast<const T*>(shm_bytes_) +
config_.input_offsets[bus][channel];
}
@@ -166,15 +169,17 @@ class AudioShmBuffer {
* addresses might change after a call to `resize()`.
*/
template <typename T>
// NOLINTNEXTLINE(bugprone-easily-swappable-parameters)
T* output_channel_ptr(const uint32_t bus, const uint32_t channel) noexcept {
return reinterpret_cast<T*>(buffer_.get_address()) +
return reinterpret_cast<T*>(shm_bytes_) +
config_.output_offsets[bus][channel];
}
template <typename T>
// NOLINTNEXTLINE(bugprone-easily-swappable-parameters)
const T* output_channel_ptr(const uint32_t bus,
const uint32_t channel) const noexcept {
return reinterpret_cast<const T*>(buffer_.get_address()) +
return reinterpret_cast<const T*>(shm_bytes_) +
config_.output_offsets[bus][channel];
}
@@ -183,11 +188,23 @@ class AudioShmBuffer {
private:
/**
* Resize the shared memory object, and set up the memory mapping.
*
* @throw std::system_error If the shared memory object could not be mapped.
*/
void setup_mapping();
boost::interprocess::shared_memory_object shm_;
boost::interprocess::mapped_region buffer_;
/**
* The file descriptor for our shared memory object.
*/
int shm_fd_ = 0;
/**
* A pointer to our mapped shared memory region.
*/
uint8_t* shm_bytes_ = nullptr;
/**
* The size of the mapped shared memory area, used for remapping.
*/
size_t shm_size_ = 0;
bool is_moved_ = false;
};
-4
View File
@@ -38,12 +38,8 @@
// 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/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")