/* PipeWire * * Copyright © 2021 Axis Communications AB * * 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 (including the next * paragraph) 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "config.h" /** \page page_module_rt PipeWire Module: RT */ #define NAME "rt" PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME); #define PW_LOG_TOPIC_DEFAULT mod_topic #define DEFAULT_POLICY SCHED_FIFO #define DEFAULT_NICE_LEVEL -11 #define DEFAULT_RT_PRIO 88 #define DEFAULT_RT_TIME_SOFT 2000000 #define DEFAULT_RT_TIME_HARD 2000000 #define MODULE_USAGE \ "[nice.level=] " \ "[rt.prio=] " \ "[rt.time.soft=" }, { PW_KEY_MODULE_DESCRIPTION, "Set thread priorities" }, { PW_KEY_MODULE_USAGE, MODULE_USAGE }, { PW_KEY_MODULE_VERSION, PACKAGE_VERSION }, }; struct impl { struct pw_context *context; struct spa_thread_utils thread_utils; int rt_prio; rlim_t rt_time_soft; rlim_t rt_time_hard; struct spa_hook module_listener; }; static void module_destroy(void *data) { struct impl *impl = data; pw_thread_utils_set(NULL); spa_hook_remove(&impl->module_listener); free(impl); } static const struct pw_impl_module_events module_events = { PW_VERSION_IMPL_MODULE_EVENTS, .destroy = module_destroy, }; static int set_nice(struct impl *impl, int nice_level) { long tid; int res = 0; tid = syscall(SYS_gettid); if (tid < 0) { pw_log_warn("could not get main thread id: %m"); tid = 0; /* means current thread in setpriority() on linux */ } if (setpriority(PRIO_PROCESS, (id_t)tid, nice_level) < 0) res = -errno; if (res < 0) pw_log_warn("could not set nice-level to %d: %s", nice_level, spa_strerror(res)); else pw_log_info("main thread nice level set to %d", nice_level); return res; } static int set_rlimit(struct impl *impl) { struct rlimit rl; int res = 0; rl.rlim_cur = impl->rt_time_soft; rl.rlim_max = impl->rt_time_hard; if (setrlimit(RLIMIT_RTTIME, &rl) < 0) res = -errno; if (res < 0) pw_log_warn("could not set rlimit: %s", spa_strerror(res)); else pw_log_debug("rt.time.soft %"PRIi64", rt.time.hard %"PRIi64, (int64_t)rl.rlim_cur, (int64_t)rl.rlim_max); return res; } static struct spa_thread *impl_create(void *data, const struct spa_dict *props, void *(*start)(void*), void *arg) { pthread_t pt; int err; if ((err = pthread_create(&pt, NULL, start, arg)) != 0) { errno = err; return NULL; } return (struct spa_thread*)pt; } static int impl_join(void *data, struct spa_thread *thread, void **retval) { pthread_t pt = (pthread_t)thread; return pthread_join(pt, retval); } static int impl_get_rt_range(void *data, const struct spa_dict *props, int *min, int *max) { int policy = DEFAULT_POLICY; if (min) *min = sched_get_priority_min(policy); if (max) *max = sched_get_priority_max(policy); return 0; } static int impl_acquire_rt(void *data, struct spa_thread *thread, int priority) { int err, policy = DEFAULT_POLICY; int rtprio = priority; struct sched_param sp; pthread_t pt = (pthread_t)thread; if (rtprio < sched_get_priority_min(policy) || rtprio > sched_get_priority_max(policy)) { pw_log_warn("invalid priority %d for policy %d", rtprio, policy); return -EINVAL; } spa_zero(sp); sp.sched_priority = rtprio; if ((err = pthread_setschedparam(pt, policy | SCHED_RESET_ON_FORK, &sp)) != 0) { pw_log_warn("%p: could not make thread realtime: %s", thread, strerror(err)); return -err; } pw_log_info("thread %p has realtime priority %d", thread, rtprio); return 0; } static int impl_drop_rt(void *data, struct spa_thread *thread) { struct sched_param sp; pthread_t pt = (pthread_t)thread; int err; spa_zero(sp); if ((err = pthread_setschedparam(pt, SCHED_OTHER | SCHED_RESET_ON_FORK, &sp)) != 0) { pw_log_warn("%p: could not drop realtime: %s", thread, strerror(err)); return -err; } pw_log_info("thread %p dropped realtime priority", thread); return 0; } static const struct spa_thread_utils_methods impl_thread_utils = { SPA_VERSION_THREAD_UTILS_METHODS, .create = impl_create, .join = impl_join, .get_rt_range = impl_get_rt_range, .acquire_rt = impl_acquire_rt, .drop_rt = impl_drop_rt, }; SPA_EXPORT int pipewire__module_init(struct pw_impl_module *module, const char *args) { struct pw_context *context = pw_impl_module_get_context(module); struct impl *impl; struct pw_properties *props; int nice_level; int res; PW_LOG_TOPIC_INIT(mod_topic); impl = calloc(1, sizeof(struct impl)); if (impl == NULL) return -ENOMEM; pw_log_debug("module %p: new %s", impl, args); impl->context = context; props = args ? pw_properties_new_string(args) : pw_properties_new(NULL, NULL); if (props == NULL) { res = -errno; goto error; } nice_level = pw_properties_get_int32(props, "nice.level", DEFAULT_NICE_LEVEL); set_nice(impl, nice_level); impl->rt_prio = pw_properties_get_int32(props, "rt.prio", DEFAULT_RT_PRIO); impl->rt_time_soft = pw_properties_get_int32(props, "rt.time.soft", DEFAULT_RT_TIME_SOFT); impl->rt_time_hard = pw_properties_get_int32(props, "rt.time.hard", DEFAULT_RT_TIME_HARD); set_rlimit(impl); impl->thread_utils.iface = SPA_INTERFACE_INIT( SPA_TYPE_INTERFACE_ThreadUtils, SPA_VERSION_THREAD_UTILS, &impl_thread_utils, impl); pw_thread_utils_set(&impl->thread_utils); pw_impl_module_add_listener(module, &impl->module_listener, &module_events, impl); pw_impl_module_update_properties(module, &SPA_DICT_INIT_ARRAY(module_props)); pw_impl_module_update_properties(module, &props->dict); pw_properties_free(props); return 0; error: pw_properties_free(props); free(impl); return res; }