From 1191c18641805d70585c12213583db953eb28516 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Thu, 6 Nov 2025 11:35:10 +0100 Subject: [PATCH] thread: add thread.reset-on-fork Add a new thread.reset-on-fork property for the thread creator. when set to false, it will clear the default SCHED_RESET_ON_FORK flag and new RT threads will be able to fork and inherit the rt policy and priority. When creating a thread make sure we set SCHED_RESET_ON_FORK when the thread.reset-on-fork property is not explicitly false; module-rt needs to preserve the SCHED_RESET_ON_FORK flag when changing the policy. Set thread.reset-on-fork=false explicitly for JACK clients to restore the JACK behaviour where implementations can fork and inherit the RT policy and priority by default. Fixes #4966 --- pipewire-jack/src/pipewire-jack.c | 1 + spa/include/spa/support/thread.h | 2 ++ src/modules/module-rt.c | 35 +++++++++++++++++++++++-------- src/pipewire/data-loop.c | 7 ++++++- src/pipewire/private.h | 1 + src/pipewire/thread.c | 13 +++++++++++- 6 files changed, 48 insertions(+), 11 deletions(-) diff --git a/pipewire-jack/src/pipewire-jack.c b/pipewire-jack/src/pipewire-jack.c index f05bf48f3..1bef74283 100644 --- a/pipewire-jack/src/pipewire-jack.c +++ b/pipewire-jack/src/pipewire-jack.c @@ -4299,6 +4299,7 @@ jack_client_t * jack_client_open (const char *client_name, client->props = pw_properties_new( PW_KEY_LOOP_CANCEL, "true", + SPA_KEY_THREAD_RESET_ON_FORK, "false", PW_KEY_REMOTE_NAME, client->server_name, PW_KEY_CLIENT_NAME, client_name, PW_KEY_CLIENT_API, "jack", diff --git a/spa/include/spa/support/thread.h b/spa/include/spa/support/thread.h index bb523ce5c..64584f076 100644 --- a/spa/include/spa/support/thread.h +++ b/spa/include/spa/support/thread.h @@ -117,6 +117,8 @@ SPA_API_THREAD int spa_thread_utils_drop_rt(struct spa_thread_utils *o, #define SPA_KEY_THREAD_STACK_SIZE "thread.stack-size" /* the stack size of the thread */ #define SPA_KEY_THREAD_AFFINITY "thread.affinity" /* array of CPUs for this thread */ #define SPA_KEY_THREAD_CREATOR "thread.creator" /* platform specific thread creator function */ +#define SPA_KEY_THREAD_RESET_ON_FORK "thread.reset-on-fork" /* reset priority and policy for real-time threads + on fork. Default true */ /** * \} diff --git a/src/modules/module-rt.c b/src/modules/module-rt.c index 2475ca6fc..4543ccb0e 100644 --- a/src/modules/module-rt.c +++ b/src/modules/module-rt.c @@ -650,8 +650,8 @@ static int set_rlimit(struct rlimit *rlim) static int acquire_rt_sched(struct spa_thread *thread, int priority) { - int err, min, max; - struct sched_param sp; + int err, min, max, new_policy, old_policy; + struct sched_param new_sched_params, old_sched_params; pthread_t pt = (pthread_t)thread; min = max = 0; @@ -663,10 +663,18 @@ static int acquire_rt_sched(struct spa_thread *thread, int priority) priority, min, max, REALTIME_POLICY); priority = SPA_CLAMP(priority, min, max); } + if ((err = pthread_getschedparam(pt, &old_policy, &old_sched_params)) != 0) { + pw_log_warn("Failed to get scheduling params: %s", strerror(err)); + old_policy = SCHED_RESET_ON_FORK; + } - spa_zero(sp); - sp.sched_priority = priority; - if ((err = pthread_setschedparam(pt, REALTIME_POLICY | SCHED_RESET_ON_FORK, &sp)) != 0) { + spa_zero(new_sched_params); + new_sched_params.sched_priority = priority; + new_policy = REALTIME_POLICY; + if ((old_policy & SCHED_RESET_ON_FORK) != 0) + new_policy |= SCHED_RESET_ON_FORK; + + if ((err = pthread_setschedparam(pt, new_policy, &new_sched_params)) != 0) { pw_log_warn("could not make thread %p realtime: %s", thread, strerror(err)); return -err; } @@ -677,12 +685,21 @@ static int acquire_rt_sched(struct spa_thread *thread, int priority) static int impl_drop_rt_generic(void *object, struct spa_thread *thread) { - struct sched_param sp; + struct sched_param new_sched_params, old_sched_params; pthread_t pt = (pthread_t)thread; - int err; + int err, new_policy, old_policy; - spa_zero(sp); - if ((err = pthread_setschedparam(pt, SCHED_OTHER | SCHED_RESET_ON_FORK, &sp)) != 0) { + if ((err = pthread_getschedparam(pt, &old_policy, &old_sched_params)) != 0) { + pw_log_warn("Failed to get scheduling params: %s", strerror(err)); + old_policy = SCHED_RESET_ON_FORK; + } + + spa_zero(new_sched_params); + new_policy = SCHED_OTHER; + if (SPA_FLAG_IS_SET(old_policy, SCHED_RESET_ON_FORK)) + new_policy |= SCHED_RESET_ON_FORK; + + if ((err = pthread_setschedparam(pt, new_policy, &new_sched_params)) != 0) { pw_log_debug("thread %p: SCHED_OTHER|SCHED_RESET_ON_FORK failed: %s", thread, strerror(err)); return -err; diff --git a/src/pipewire/data-loop.c b/src/pipewire/data-loop.c index 3cee8a2e7..b8491d616 100644 --- a/src/pipewire/data-loop.c +++ b/src/pipewire/data-loop.c @@ -110,6 +110,7 @@ static struct pw_data_loop *loop_new(struct pw_loop *loop, const struct spa_dict } this->loop = loop; this->rt_prio = -1; + this->reset_on_fork = true; if (props != NULL) { if ((str = spa_dict_lookup(props, PW_KEY_LOOP_CANCEL)) != NULL) @@ -122,6 +123,8 @@ static struct pw_data_loop *loop_new(struct pw_loop *loop, const struct spa_dict name = str; if ((str = spa_dict_lookup(props, SPA_KEY_THREAD_AFFINITY)) != NULL) this->affinity = strdup(str); + if ((str = spa_dict_lookup(props, SPA_KEY_THREAD_RESET_ON_FORK)) != NULL) + this->reset_on_fork = spa_atob(str); } if (class == NULL) class = this->rt_prio != 0 ? "data.rt" : "data"; @@ -236,7 +239,7 @@ int pw_data_loop_start(struct pw_data_loop *loop) if (!loop->running) { struct spa_thread_utils *utils; struct spa_thread *thr; - struct spa_dict_item items[2]; + struct spa_dict_item items[3]; uint32_t n_items = 0; loop->running = true; @@ -248,6 +251,8 @@ int pw_data_loop_start(struct pw_data_loop *loop) if (loop->affinity) items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_THREAD_AFFINITY, loop->affinity); + items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_THREAD_RESET_ON_FORK, + loop->reset_on_fork ? "true" : "false"); thr = spa_thread_utils_create(utils, &SPA_DICT_INIT(items, n_items), do_loop, loop); loop->thread = (pthread_t)thr; diff --git a/src/pipewire/private.h b/src/pipewire/private.h index 36f85bd9d..81fe7dff5 100644 --- a/src/pipewire/private.h +++ b/src/pipewire/private.h @@ -431,6 +431,7 @@ struct pw_data_loop { char *class; char **classes; int rt_prio; + bool reset_on_fork; struct spa_hook_list listener_list; struct spa_thread_utils *thread_utils; diff --git a/src/pipewire/thread.c b/src/pipewire/thread.c index baca12212..3af0b4a44 100644 --- a/src/pipewire/thread.c +++ b/src/pipewire/thread.c @@ -92,8 +92,10 @@ static struct spa_thread *impl_create(void *object, pthread_t pt; pthread_attr_t *attr = NULL, attributes; const char *str; - int err; + int err, old_policy, new_policy; int (*create_func)(pthread_t *, const pthread_attr_t *attr, void *(*start)(void*), void *) = NULL; + struct sched_param sp; + bool reset_on_fork = true; attr = pw_thread_fill_attr(props, &attributes); @@ -118,7 +120,16 @@ static struct spa_thread *impl_create(void *object, if ((str = spa_dict_lookup(props, SPA_KEY_THREAD_AFFINITY)) != NULL && (err = thread_setaffinity(pt, str)) != 0) pw_log_warn("pthread_setaffinity error: %s", strerror(-err)); + if ((str = spa_dict_lookup(props, SPA_KEY_THREAD_RESET_ON_FORK)) != NULL) + reset_on_fork = spa_atob(str); } + + pthread_getschedparam(pt, &old_policy, &sp); + new_policy = old_policy; + SPA_FLAG_UPDATE(new_policy, SCHED_RESET_ON_FORK, reset_on_fork); + if (old_policy != new_policy) + pthread_setschedparam(pt, new_policy, &sp); + return (struct spa_thread*)pt; }