aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt25
-rw-r--r--alc/helpers.cpp77
-rw-r--r--config.h.in3
-rw-r--r--core/dbus_wrap.cpp46
-rw-r--r--core/dbus_wrap.h75
-rw-r--r--core/rtkit.cpp240
-rw-r--r--core/rtkit.h80
7 files changed, 528 insertions, 18 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index bd64c9d9..3f1894fa 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -673,7 +673,30 @@ set(CORE_OBJS
core/mastering.h
core/resampler_limits.h
core/uhjfilter.cpp
- core/uhjfilter.h
+ core/uhjfilter.h)
+
+set(HAVE_RTKIT 0)
+option(ALSOFT_REQUIRE_RTKIT "Require RTKit/D-Bus support" FALSE)
+find_package(DBus1)
+if(DBus1_FOUND)
+ option(ALSOFT_RTKIT "Enable RTKit support" ON)
+ if(ALSOFT_RTKIT)
+ set(HAVE_RTKIT 1)
+ set(CORE_OBJS ${CORE_OBJS} core/dbus_wrap.cpp core/dbus_wrap.h core/rtkit.cpp core/rtkit.h)
+ if(WIN32 OR HAVE_DLFCN_H)
+ set(INC_PATHS ${INC_PATHS} ${DBus1_INCLUDE_DIRS})
+ set(CPP_DEFS ${CPP_DEFS} ${DBus1_DEFINITIONS})
+ else()
+ set(EXTRA_LIBS ${EXTRA_LIBS} ${DBus1_LIBRARIES})
+ endif()
+ endif()
+endif()
+if(ALSOFT_REQUIRE_RTKIT AND NOT HAVE_RTKIT)
+ message(FATAL_ERROR "Failed to enabled required RTKit support")
+endif()
+
+# Default mixers, always available
+set(CORE_OBJS ${CORE_OBJS}
core/mixer/defs.h
core/mixer/hrtfbase.h
core/mixer/hrtfdefs.h
diff --git a/alc/helpers.cpp b/alc/helpers.cpp
index 6dbe4787..671a1ae5 100644
--- a/alc/helpers.cpp
+++ b/alc/helpers.cpp
@@ -203,6 +203,9 @@ void SetRTPriority(void)
#include <pthread.h>
#include <sched.h>
#endif
+#ifdef HAVE_RTKIT
+#include "core/rtkit.h"
+#endif
const PathNamePair &GetProcBinary()
{
@@ -409,28 +412,68 @@ al::vector<std::string> SearchDataFiles(const char *ext, const char *subdir)
void SetRTPriority()
{
+ if(RTPrioLevel <= 0)
+ return;
+
+ int err{-ENOTSUP};
#if defined(HAVE_PTHREAD_SETSCHEDPARAM) && !defined(__OpenBSD__)
- if(RTPrioLevel > 0)
- {
- struct sched_param param{};
- /* Use the minimum real-time priority possible for now (on Linux this
- * should be 1 for SCHED_RR).
- */
- param.sched_priority = sched_get_priority_min(SCHED_RR);
- int err;
+ struct sched_param param{};
+ /* Use the minimum real-time priority possible for now (on Linux this
+ * should be 1 for SCHED_RR).
+ */
+ param.sched_priority = sched_get_priority_min(SCHED_RR);
#ifdef SCHED_RESET_ON_FORK
- err = pthread_setschedparam(pthread_self(), SCHED_RR|SCHED_RESET_ON_FORK, &param);
- if(err == EINVAL)
+ err = pthread_setschedparam(pthread_self(), SCHED_RR|SCHED_RESET_ON_FORK, &param);
+ if(err == EINVAL)
+#endif
+ err = pthread_setschedparam(pthread_self(), SCHED_RR, &param);
+ if(err == 0) return;
+
+ WARN("pthread_setschedparam failed: %s (%d)\n", std::strerror(err), err);
#endif
- err = pthread_setschedparam(pthread_self(), SCHED_RR, &param);
- if(err != 0)
- ERR("Failed to set real-time priority for thread: %s (%d)\n", std::strerror(err), err);
+#ifdef HAVE_RTKIT
+ if(HasDBus())
+ {
+ dbus::Error error;
+ if(dbus::ConnectionPtr conn{(*pdbus_bus_get)(DBUS_BUS_SYSTEM, &error.get())})
+ {
+ /* Don't stupidly exit if the connection dies while doing this. */
+ (*pdbus_connection_set_exit_on_disconnect)(conn.get(), false);
+
+ int nicemin{};
+ rtkit_get_min_nice_level(conn.get(), &nicemin);
+ int rtmax{rtkit_get_max_realtime_priority(conn.get())};
+ TRACE("Maximum real-time priority: %d, minimum niceness: %d\n", rtmax, nicemin);
+
+ if(rtmax > 0)
+ {
+ /* Use half the maximum real-time priority allowed. */
+ TRACE("Making real-time with priority %d\n", (rtmax+1)/2);
+ err = rtkit_make_realtime(conn.get(), 0, (rtmax+1)/2);
+ if(err != 0)
+ {
+ err = std::abs(err);
+ WARN("Failed to set real-time priority: %s (%d)\n", std::strerror(err), err);
+ }
+ }
+ if(err != 0 && nicemin < 0)
+ {
+ TRACE("Making high priority with niceness %d\n", nicemin);
+ err = rtkit_make_high_priority(conn.get(), 0, nicemin);
+ if(err != 0)
+ {
+ err = std::abs(err);
+ WARN("Failed to set high priority: %s (%d)\n", std::strerror(err), err);
+ }
+ }
+ }
+ else
+ WARN("D-Bus connection failed with %s: %s\n", error->name, error->message);
}
-#else
- /* Real-time priority not available */
- if(RTPrioLevel > 0)
- ERR("Cannot set priority level for thread\n");
+ else
+ WARN("D-Bus not available\n");
#endif
+ ERR("Could not set elevated priority: %s (%d)\n", std::strerror(err), err);
}
#endif
diff --git a/config.h.in b/config.h.in
index f43b3f73..5cc78ae5 100644
--- a/config.h.in
+++ b/config.h.in
@@ -13,6 +13,9 @@
/* Define if we have the getopt function */
#cmakedefine HAVE_GETOPT
+/* Define if we have DBus/RTKit */
+#cmakedefine HAVE_RTKIT
+
/* Define if we have SSE CPU extensions */
#cmakedefine HAVE_SSE
#cmakedefine HAVE_SSE2
diff --git a/core/dbus_wrap.cpp b/core/dbus_wrap.cpp
new file mode 100644
index 00000000..506dd815
--- /dev/null
+++ b/core/dbus_wrap.cpp
@@ -0,0 +1,46 @@
+
+#include "config.h"
+
+#include "dbus_wrap.h"
+
+#ifdef HAVE_DYNLOAD
+
+#include <mutex>
+#include <type_traits>
+
+#include "logging.h"
+
+
+void *dbus_handle{nullptr};
+#define DECL_FUNC(x) decltype(x) *p##x{};
+DBUS_FUNCTIONS(DECL_FUNC)
+#undef DECL_FUNC
+
+void PrepareDBus()
+{
+ static constexpr char libname[] = "libdbus-1.so.3";
+
+ auto load_func = [](auto &f, const char *name) -> void
+ { f = reinterpret_cast<std::remove_reference_t<decltype(f)>>(GetSymbol(dbus_handle, name)); };
+#define LOAD_FUNC(x) do { \
+ load_func(p##x, #x); \
+ if(!p##x) \
+ { \
+ WARN("Failed to load function %s\n", #x); \
+ CloseLib(dbus_handle); \
+ dbus_handle = nullptr; \
+ return; \
+ } \
+} while(0);
+
+ dbus_handle = LoadLib(libname);
+ if(!dbus_handle)
+ {
+ WARN("Failed to load %s\n", libname);
+ return;
+ }
+
+DBUS_FUNCTIONS(LOAD_FUNC)
+#undef LOAD_FUNC
+}
+#endif
diff --git a/core/dbus_wrap.h b/core/dbus_wrap.h
new file mode 100644
index 00000000..61dbb971
--- /dev/null
+++ b/core/dbus_wrap.h
@@ -0,0 +1,75 @@
+#ifndef CORE_DBUS_WRAP_H
+#define CORE_DBUS_WRAP_H
+
+#include <memory>
+
+#include <dbus/dbus.h>
+
+#include "dynload.h"
+
+
+#define DBUS_FUNCTIONS(MAGIC) \
+MAGIC(dbus_error_init) \
+MAGIC(dbus_error_free) \
+MAGIC(dbus_bus_get) \
+MAGIC(dbus_connection_set_exit_on_disconnect) \
+MAGIC(dbus_connection_unref) \
+MAGIC(dbus_connection_send_with_reply_and_block) \
+MAGIC(dbus_message_unref) \
+MAGIC(dbus_message_new_method_call) \
+MAGIC(dbus_message_append_args) \
+MAGIC(dbus_message_iter_init) \
+MAGIC(dbus_message_iter_next) \
+MAGIC(dbus_message_iter_recurse) \
+MAGIC(dbus_message_iter_get_arg_type) \
+MAGIC(dbus_message_iter_get_basic) \
+MAGIC(dbus_set_error_from_message)
+
+#ifdef HAVE_DYNLOAD
+
+#include <mutex>
+
+extern void *dbus_handle;
+#define DECL_FUNC(x) extern decltype(x) *p##x;
+DBUS_FUNCTIONS(DECL_FUNC)
+#undef DECL_FUNC
+
+void PrepareDBus();
+
+inline auto HasDBus()
+{
+ static std::once_flag init_dbus{};
+ std::call_once(init_dbus, PrepareDBus);
+ return dbus_handle;
+}
+
+#else
+
+#define DECL_FUNC(x) constexpr auto p##x = &x;
+DBUS_FUNCTIONS(DECL_FUNC)
+#undef DECL_FUNC
+
+constexpr bool HasDBus() noexcept { return true; }
+#endif /* HAVE_DYNLOAD */
+
+
+namespace dbus {
+
+struct Error {
+ Error() { (*pdbus_error_init)(&mError); }
+ ~Error() { (*pdbus_error_free)(&mError); }
+ DBusError* operator->() { return &mError; }
+ DBusError &get() { return mError; }
+private:
+ DBusError mError{};
+};
+
+struct ConnectionDeleter {
+ void operator()(DBusConnection *c) { (*pdbus_connection_unref)(c); }
+};
+using ConnectionPtr = std::unique_ptr<DBusConnection,ConnectionDeleter>;
+
+} // namespace dbus
+
+
+#endif /* CORE_DBUS_WRAP_H */
diff --git a/core/rtkit.cpp b/core/rtkit.cpp
new file mode 100644
index 00000000..8b489e71
--- /dev/null
+++ b/core/rtkit.cpp
@@ -0,0 +1,240 @@
+/*-*- Mode: C; c-basic-offset: 8 -*-*/
+
+/***
+ Copyright 2009 Lennart Poettering
+ Copyright 2010 David Henningsson <[email protected]>
+ Copyright 2021 Chris Robinson
+
+ Permission is hereby granted, free of charge, to any person
+ obtaining a copy of this software and associated documentation files
+ (the "Software"), to deal in the Software without restriction,
+ including without limitation the rights to use, copy, modify, merge,
+ publish, distribute, sublicense, and/or sell copies of the Software,
+ and to permit persons to whom the Software is furnished to do so,
+ subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+***/
+
+#include "config.h"
+
+#include "rtkit.h"
+
+#include <errno.h>
+
+#ifdef __linux__
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include <memory>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/syscall.h>
+
+
+namespace dbus {
+ constexpr int TypeString{'s'};
+ constexpr int TypeVariant{'v'};
+ constexpr int TypeInt32{'i'};
+ constexpr int TypeUInt32{'u'};
+ constexpr int TypeInt64{'x'};
+ constexpr int TypeUInt64{'t'};
+ constexpr int TypeInvalid{'\0'};
+
+ struct MessageDeleter {
+ void operator()(DBusMessage *m) { (*pdbus_message_unref)(m); }
+ };
+ using MessagePtr = std::unique_ptr<DBusMessage,MessageDeleter>;
+} // namespace dbus
+
+namespace {
+
+inline pid_t _gettid()
+{ return static_cast<pid_t>(syscall(SYS_gettid)); }
+
+int translate_error(const char *name)
+{
+ if(strcmp(name, DBUS_ERROR_NO_MEMORY) == 0)
+ return -ENOMEM;
+ if(strcmp(name, DBUS_ERROR_SERVICE_UNKNOWN) == 0
+ || strcmp(name, DBUS_ERROR_NAME_HAS_NO_OWNER) == 0)
+ return -ENOENT;
+ if(strcmp(name, DBUS_ERROR_ACCESS_DENIED) == 0
+ || strcmp(name, DBUS_ERROR_AUTH_FAILED) == 0)
+ return -EACCES;
+ return -EIO;
+}
+
+int rtkit_get_int_property(DBusConnection *connection, const char *propname, long long *propval)
+{
+ dbus::MessagePtr m{(*pdbus_message_new_method_call)(RTKIT_SERVICE_NAME, RTKIT_OBJECT_PATH,
+ "org.freedesktop.DBus.Properties", "Get")};
+ if(!m) return -ENOMEM;
+
+ const char *interfacestr = RTKIT_SERVICE_NAME;
+ auto ready = (*pdbus_message_append_args)(m.get(),
+ dbus::TypeString, &interfacestr,
+ dbus::TypeString, &propname,
+ dbus::TypeInvalid);
+ if(!ready) return -ENOMEM;
+
+ dbus::Error error;
+ dbus::MessagePtr r{(*pdbus_connection_send_with_reply_and_block)(connection, m.get(), -1,
+ &error.get())};
+ if(!r) return translate_error(error->name);
+
+ if((*pdbus_set_error_from_message)(&error.get(), r.get()))
+ return translate_error(error->name);
+
+ int ret{-EBADMSG};
+ DBusMessageIter iter{};
+ (*pdbus_message_iter_init)(r.get(), &iter);
+ while(int curtype{(*pdbus_message_iter_get_arg_type)(&iter)})
+ {
+ if(curtype == dbus::TypeVariant)
+ {
+ DBusMessageIter subiter{};
+ (*pdbus_message_iter_recurse)(&iter, &subiter);
+
+ while((curtype=(*pdbus_message_iter_get_arg_type)(&subiter)) != dbus::TypeInvalid)
+ {
+ if(curtype == dbus::TypeInt32)
+ {
+ dbus_int32_t i32{};
+ (*pdbus_message_iter_get_basic)(&subiter, &i32);
+ *propval = i32;
+ ret = 0;
+ }
+
+ if(curtype == dbus::TypeInt64)
+ {
+ dbus_int64_t i64{};
+ (*pdbus_message_iter_get_basic)(&subiter, &i64);
+ *propval = i64;
+ ret = 0;
+ }
+
+ (*pdbus_message_iter_next)(&subiter);
+ }
+ }
+ (*pdbus_message_iter_next)(&iter);
+ }
+
+ return ret;
+}
+
+} // namespace
+
+extern "C" {
+int rtkit_get_max_realtime_priority(DBusConnection *connection)
+{
+ long long retval{};
+ int err{rtkit_get_int_property(connection, "MaxRealtimePriority", &retval)};
+ return err < 0 ? err : static_cast<int>(retval);
+}
+
+int rtkit_get_min_nice_level(DBusConnection *connection, int *min_nice_level)
+{
+ long long retval{};
+ int err{rtkit_get_int_property(connection, "MinNiceLevel", &retval)};
+ if(err >= 0) *min_nice_level = static_cast<int>(retval);
+ return err;
+}
+
+long long rtkit_get_rttime_usec_max(DBusConnection *connection)
+{
+ long long retval{};
+ int err{rtkit_get_int_property(connection, "RTTimeUSecMax", &retval)};
+ return err < 0 ? err : retval;
+}
+
+int rtkit_make_realtime(DBusConnection *connection, pid_t thread, int priority)
+{
+ if(thread == 0)
+ thread = _gettid();
+
+ dbus::MessagePtr m{(*pdbus_message_new_method_call)(RTKIT_SERVICE_NAME, RTKIT_OBJECT_PATH,
+ "org.freedesktop.RealtimeKit1", "MakeThreadRealtime")};
+ if(!m) return -ENOMEM;
+
+ auto u64 = static_cast<dbus_uint64_t>(thread);
+ auto u32 = static_cast<dbus_uint32_t>(priority);
+ auto ready = (*pdbus_message_append_args)(m.get(),
+ dbus::TypeUInt64, &u64,
+ dbus::TypeUInt32, &u32,
+ dbus::TypeInvalid);
+ if(!ready) return -ENOMEM;
+
+ dbus::Error error;
+ dbus::MessagePtr r{(*pdbus_connection_send_with_reply_and_block)(connection, m.get(), -1,
+ &error.get())};
+ if(!r) return translate_error(error->name);
+
+ if((*pdbus_set_error_from_message)(&error.get(), r.get()))
+ return translate_error(error->name);
+
+ return 0;
+}
+
+int rtkit_make_high_priority(DBusConnection *connection, pid_t thread, int nice_level)
+{
+ if(thread == 0)
+ thread = _gettid();
+
+ dbus::MessagePtr m{(*pdbus_message_new_method_call)(RTKIT_SERVICE_NAME, RTKIT_OBJECT_PATH,
+ "org.freedesktop.RealtimeKit1", "MakeThreadHighPriority")};
+ if(!m) return -ENOMEM;
+
+ auto u64 = static_cast<dbus_uint64_t>(thread);
+ auto s32 = static_cast<dbus_int32_t>(nice_level);
+ auto ready = (*pdbus_message_append_args)(m.get(),
+ dbus::TypeUInt64, &u64,
+ dbus::TypeInt32, &s32,
+ dbus::TypeInvalid);
+ if(!ready) return -ENOMEM;
+
+ dbus::Error error;
+ dbus::MessagePtr r{(*pdbus_connection_send_with_reply_and_block)(connection, m.get(), -1,
+ &error.get())};
+ if(!r) return translate_error(error->name);
+
+ if((*pdbus_set_error_from_message)(&error.get(), r.get()))
+ return translate_error(error->name);
+
+ return 0;
+}
+} // extern "C"
+
+#else
+
+extern "C" {
+int rtkit_make_realtime(DBusConnection *connection, pid_t thread, int priority)
+{ return -ENOTSUP; }
+
+int rtkit_make_high_priority(DBusConnection *connection, pid_t thread, int nice_level)
+{ return -ENOTSUP; }
+
+int rtkit_get_max_realtime_priority(DBusConnection *connection)
+{ return -ENOTSUP; }
+
+int rtkit_get_min_nice_level(DBusConnection *connection, int *min_nice_level)
+{ return -ENOTSUP; }
+
+long long rtkit_get_rttime_usec_max(DBusConnection *connection)
+{ return -ENOTSUP; }
+} // extern "C"
+
+#endif
diff --git a/core/rtkit.h b/core/rtkit.h
new file mode 100644
index 00000000..96e81d4a
--- /dev/null
+++ b/core/rtkit.h
@@ -0,0 +1,80 @@
+/*-*- Mode: C; c-basic-offset: 8 -*-*/
+
+#ifndef foortkithfoo
+#define foortkithfoo
+
+/***
+ Copyright 2009 Lennart Poettering
+ Copyright 2010 David Henningsson <[email protected]>
+
+ Permission is hereby granted, free of charge, to any person
+ obtaining a copy of this software and associated documentation files
+ (the "Software"), to deal in the Software without restriction,
+ including without limitation the rights to use, copy, modify, merge,
+ publish, distribute, sublicense, and/or sell copies of the Software,
+ and to permit persons to whom the Software is furnished to do so,
+ subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+***/
+
+#include <sys/types.h>
+
+#include "dbus_wrap.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* This is the reference implementation for a client for
+ * RealtimeKit. You don't have to use this, but if do, just copy these
+ * sources into your repository */
+
+#define RTKIT_SERVICE_NAME "org.freedesktop.RealtimeKit1"
+#define RTKIT_OBJECT_PATH "/org/freedesktop/RealtimeKit1"
+
+/* This is mostly equivalent to sched_setparam(thread, SCHED_RR, {
+ * .sched_priority = priority }). 'thread' needs to be a kernel thread
+ * id as returned by gettid(), not a pthread_t! If 'thread' is 0 the
+ * current thread is used. The returned value is a negative errno
+ * style error code, or 0 on success. */
+int rtkit_make_realtime(DBusConnection *system_bus, pid_t thread, int priority);
+
+/* This is mostly equivalent to setpriority(PRIO_PROCESS, thread,
+ * nice_level). 'thread' needs to be a kernel thread id as returned by
+ * gettid(), not a pthread_t! If 'thread' is 0 the current thread is
+ * used. The returned value is a negative errno style error code, or 0
+ * on success.*/
+int rtkit_make_high_priority(DBusConnection *system_bus, pid_t thread, int nice_level);
+
+/* Return the maximum value of realtime priority available. Realtime requests
+ * above this value will fail. A negative value is an errno style error code.
+ */
+int rtkit_get_max_realtime_priority(DBusConnection *system_bus);
+
+/* Retreive the minimum value of nice level available. High prio requests
+ * below this value will fail. The returned value is a negative errno
+ * style error code, or 0 on success.*/
+int rtkit_get_min_nice_level(DBusConnection *system_bus, int *min_nice_level);
+
+/* Return the maximum value of RLIMIT_RTTIME to set before attempting a
+ * realtime request. A negative value is an errno style error code.
+ */
+long long rtkit_get_rttime_usec_max(DBusConnection *system_bus);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif