diff --git a/meson_options.txt b/meson_options.txt index bfed7af27..1f2d404eb 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -192,6 +192,7 @@ option('solaris_output', type: 'feature', description: 'Solaris /dev/audio suppo # option('dbus', type: 'feature', description: 'D-Bus support') +option('rtkit', type: 'feature', description: 'Use RealtimeKit for requesting realtime scheduling') option('expat', type: 'feature', description: 'Expat XML support') option('icu', type: 'feature', description: 'Use libicu for Unicode') option('iconv', type: 'feature', description: 'Use iconv() for character set conversion') diff --git a/src/thread/Util.cxx b/src/thread/Util.cxx index 50bd95af6..0ba3d9d06 100644 --- a/src/thread/Util.cxx +++ b/src/thread/Util.cxx @@ -8,10 +8,206 @@ #include #include #include +#include +#include +#include +#include #elif defined(_WIN32) #include #endif + +#ifdef __linux__ +const char* RTKIT_SERVICE_NAME = "org.freedesktop.RealtimeKit1"; +const char* RTKIT_OBJECT_PATH = "/org/freedesktop/RealtimeKit1"; + +// static int +// rtkit_get_max_realtime_priority(DBusConnection &connection) +// { +// // TODO: Implement this function +// DBusError error; +// } + +// static void +// rtkit_make_realtime(DBusConnection &connection, pid_t thread, int priority) +// { +// // TODO: Implement this function + +// } +#endif + +// TODO: minimize and drop +#ifdef __linux__ + +#define RTKIT_SERVICE_NAME "org.freedesktop.RealtimeKit1" +#define RTKIT_OBJECT_PATH "/org/freedesktop/RealtimeKit1" + +static pid_t _gettid(void) { + return (pid_t) syscall(SYS_gettid); +} + +static 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; +} + +static long long rtkit_get_int_property(DBusConnection *connection, const char* propname, long long* propval) { + DBusMessage *m = NULL, *r = NULL; + DBusMessageIter iter, subiter; + dbus_int64_t i64; + dbus_int32_t i32; + DBusError error; + int current_type; + long long ret; + const char * interfacestr = "org.freedesktop.RealtimeKit1"; + + dbus_error_init(&error); + + if (!(m = dbus_message_new_method_call( + RTKIT_SERVICE_NAME, + RTKIT_OBJECT_PATH, + "org.freedesktop.DBus.Properties", + "Get"))) { + ret = -ENOMEM; + goto finish; + } + + if (!dbus_message_append_args( + m, + DBUS_TYPE_STRING, &interfacestr, + DBUS_TYPE_STRING, &propname, + DBUS_TYPE_INVALID)) { + ret = -ENOMEM; + goto finish; + } + + if (!(r = dbus_connection_send_with_reply_and_block(connection, m, -1, &error))) { + ret = translate_error(error.name); + goto finish; + } + + if (dbus_set_error_from_message(&error, r)) { + ret = translate_error(error.name); + goto finish; + } + + ret = -EBADMSG; + dbus_message_iter_init(r, &iter); + while ((current_type = dbus_message_iter_get_arg_type (&iter)) != DBUS_TYPE_INVALID) { + + if (current_type == DBUS_TYPE_VARIANT) { + dbus_message_iter_recurse(&iter, &subiter); + + while ((current_type = dbus_message_iter_get_arg_type (&subiter)) != DBUS_TYPE_INVALID) { + + if (current_type == DBUS_TYPE_INT32) { + dbus_message_iter_get_basic(&subiter, &i32); + *propval = i32; + ret = 0; + } + + if (current_type == DBUS_TYPE_INT64) { + dbus_message_iter_get_basic(&subiter, &i64); + *propval = i64; + ret = 0; + } + + dbus_message_iter_next (&subiter); + } + } + dbus_message_iter_next (&iter); + } + +finish: + + if (m) + dbus_message_unref(m); + + if (r) + dbus_message_unref(r); + + dbus_error_free(&error); + + return ret; +} + +static int rtkit_get_max_realtime_priority(DBusConnection *connection) { + long long retval; + int err; + + err = rtkit_get_int_property(connection, "MaxRealtimePriority", &retval); + return err < 0 ? err : retval; +} + +static int rtkit_make_realtime(DBusConnection *connection, pid_t thread, int priority) { + DBusMessage *m = NULL, *r = NULL; + dbus_uint64_t u64; + dbus_uint32_t u32; + DBusError error; + int ret; + + dbus_error_init(&error); + + if (thread == 0) + thread = _gettid(); + + if (!(m = dbus_message_new_method_call( + RTKIT_SERVICE_NAME, + RTKIT_OBJECT_PATH, + "org.freedesktop.RealtimeKit1", + "MakeThreadRealtime"))) { + ret = -ENOMEM; + goto finish; + } + + u64 = (dbus_uint64_t) thread; + u32 = (dbus_uint32_t) priority; + + if (!dbus_message_append_args( + m, + DBUS_TYPE_UINT64, &u64, + DBUS_TYPE_UINT32, &u32, + DBUS_TYPE_INVALID)) { + ret = -ENOMEM; + goto finish; + } + + if (!(r = dbus_connection_send_with_reply_and_block(connection, m, -1, &error))) { + ret = translate_error(error.name); + goto finish; + } + + + if (dbus_set_error_from_message(&error, r)) { + ret = translate_error(error.name); + goto finish; + } + + ret = 0; + +finish: + + if (m) + dbus_message_unref(m); + + if (r) + dbus_message_unref(r); + + dbus_error_free(&error); + + return ret; +} + +#endif + #ifdef __linux__ #ifndef ANDROID @@ -82,7 +278,39 @@ SetThreadRealtime() policy |= SCHED_RESET_ON_FORK; #endif - if (linux_sched_setscheduler(0, policy, &sched_param) < 0) - throw MakeErrno("sched_setscheduler failed"); + int res = linux_sched_setscheduler(0, policy, &sched_param); + + if (res < 0 && errno == EPERM) { + DBusError error; + DBusConnection *system_bus; + + if (!(system_bus = dbus_bus_get(DBUS_BUS_SYSTEM, &error))) { + throw MakeErrno("could not connect to dbus"); + } + + int max_realtime_priority; + if ((max_realtime_priority = rtkit_get_max_realtime_priority(system_bus)) < 0) { + dbus_connection_unref(system_bus); + throw MakeErrno("could not retrieve maximum realtime priority allowed"); + } + + if (max_realtime_priority < sched_param.sched_priority) { + dbus_connection_unref(system_bus); + throw MakeErrno("maximum realtime priority is below 40"); + } + + pid_t pid = getpid(); + if (rtkit_make_realtime(system_bus, pid, sched_param.sched_priority) < 0) { + dbus_connection_unref(system_bus); + throw MakeErrno("failed to request realtime scheduling"); + } + + dbus_connection_unref(system_bus); + res = 0; + } + + // if (res < 0) { + // throw MakeErrno("sched_setscheduler failed"); + // } #endif // __linux__ -} +} \ No newline at end of file diff --git a/src/thread/meson.build b/src/thread/meson.build index ec4aa0ff6..3ebe6115b 100644 --- a/src/thread/meson.build +++ b/src/thread/meson.build @@ -7,6 +7,13 @@ else threads_dep = dependency('threads') endif +if is_linux + dbus_dep = dependency('dbus-1', required: get_option('dbus')) + conf.set('ENABLE_DBUS', dbus_dep.found()) +else + dbus_dep = [] +endif + conf.set('HAVE_PTHREAD_SETNAME_NP', compiler.has_function('pthread_setname_np', dependencies: threads_dep)) thread = static_library( @@ -16,6 +23,7 @@ thread = static_library( include_directories: inc, dependencies: [ threads_dep, + dbus_dep, ], )