From 0e57f410a9875a4d9f908d6ca3953770b73173a3 Mon Sep 17 00:00:00 2001 From: Robbert van der Helm Date: Sun, 18 Jul 2021 23:12:10 +0200 Subject: [PATCH] Warn on startup if RLIMIT_MEMLOCK is set too low This should diagnose issues like #119. --- CHANGELOG.md | 2 ++ src/common/utils.cpp | 9 +++++++++ src/common/utils.h | 17 +++++++++++++---- src/plugin/bridges/common.h | 37 ++++++++++++++++++++++++++++++++++++- 4 files changed, 60 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e3a82b8c..b2c35764 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,8 @@ Versioning](https://semver.org/spec/v2.0.0.html). shared memory. If you have not yet set up realtime priviliges and memory locking limits for your user, then yabridge may not be able to map enough shared memory for processing audio in plugins with a lot of inputs or outputs. +- Also added a warning for thisq in the initialization message if yabridge + detects a low value for `RLIMIT_MEMLOCK`. ### Fixed diff --git a/src/common/utils.cpp b/src/common/utils.cpp index 3f6870a3..f748166f 100644 --- a/src/common/utils.cpp +++ b/src/common/utils.cpp @@ -57,6 +57,15 @@ bool set_realtime_priority(bool sched_fifo, int priority) noexcept { ¶ms) == 0; } +std::optional get_memlock_limit() noexcept { + rlimit limits{}; + if (getrlimit(RLIMIT_MEMLOCK, &limits) == 0) { + return limits.rlim_cur; + } else { + return std::nullopt; + } +} + std::optional get_rttime_limit() noexcept { rlimit limits{}; if (getrlimit(RLIMIT_RTTIME, &limits) == 0) { diff --git a/src/common/utils.h b/src/common/utils.h index 155f6538..02c80f34 100644 --- a/src/common/utils.h +++ b/src/common/utils.h @@ -106,10 +106,19 @@ std::optional get_realtime_priority() noexcept; bool set_realtime_priority(bool sched_fifo, int priority = 5) noexcept; /** - * Get the (soft) `RTTIME` resource limit, or the amount of time a `SCHED_FIFO` - * process may spend uninterrupted before being killed by the scheduler. A value - * of `-1`/`RLIM_INFINITY` means that there is no limit. If there was some error - * fetching this value, then a nullopt will be returned. + * Get the (soft) `RLIMIT_MEMLOCK` resource limit. If this is set to some low + * value, then we'll print a warning during initialization because mapping + * shared memory may fail. A value of `-1`/`RLIM_INFINITY` means that there is + * no limit. If there was some error fetching this value, then a nullopt will be + * returned. + */ +std::optional get_memlock_limit() noexcept; + +/** + * Get the (soft) `RLIMIT_RTTIME` resource limit, or the amount of time a + * `SCHED_FIFO` process may spend uninterrupted before being killed by the + * scheduler. A value of `-1`/`RLIM_INFINITY` means that there is no limit. If + * there was some error fetching this value, then a nullopt will be returned. * * This is useful to diagnose issues caused by PipeWire. They use rtkit at the * moment, and both rtkit and PipeWire's rtkit module will enable a realtime CPU diff --git a/src/plugin/bridges/common.h b/src/plugin/bridges/common.h index 9bd4218e..9566cd32 100644 --- a/src/plugin/bridges/common.h +++ b/src/plugin/bridges/common.h @@ -29,6 +29,14 @@ #include "../../common/utils.h" #include "../host-process.h" +/** + * If the amount of lockable memory is below this, then we'll warn about it + * during startup. Otherwise we may run into issues when mapping shared memory + * for plugins with a lot of inputs or outputs. We would of course prefer this + * to just be set to `RLIM_INFINITY`, but this seems like a reasonable amount. + */ +constexpr int memlock_min_safe_threshold = 256 << 20; + /** * PipeWire uses rtkit, and both set `RLIMIT_RTTIME` to some low value. Normally * this is kept at unlimited, and low values can cause the host process to get @@ -149,7 +157,8 @@ class PluginBridge { init_msg << " RLIMIT_RTTIME is set to " << *rttime_limit << " us. This can happen when" << std::endl; init_msg << " using PipeWire. yabridge may crash when " - << "loading plugins" << std::endl; + "loading plugins" + << std::endl; init_msg << " until you fix this." << std::endl; init_msg << std::endl; } else { @@ -162,6 +171,32 @@ class PluginBridge { } else { init_msg << "'no'" << std::endl; } + // This doesn't really fit here, but this seems like the place to warn + // about low memlock limits. Because this is meant to just be a helpful + // warning, we won't print anything at all when there's no need to. + if (auto memlock_limit = get_memlock_limit()) { + if (*memlock_limit != RLIM_INFINITY && + *memlock_limit < memlock_min_safe_threshold) { + init_msg << "memlock limit: 'WARNING: " << *memlock_limit + << " bytes, see below'" << std::endl; + init_msg << std::endl; + init_msg + << " With a low memory locking limit, yabridge may not be" + << std::endl; + init_msg << " be able to map enough shared memory for audio " + "buffers," + << std::endl; + init_msg << " yabridge may crash when using plugins with " + "many inputs" + << std::endl; + init_msg << " or outputs until you fix this." << std::endl; + init_msg << std::endl; + } + } else { + init_msg + << "memlock limit: 'WARNING: Could not fetch RLIMIT_MEMLOCK'" + << std::endl; + } init_msg << "sockets: '" << sockets.base_dir.string() << "'" << std::endl;