diff --git a/include/SDL_hints.h b/include/SDL_hints.h index a9dbbae7e..24b9022c5 100644 --- a/include/SDL_hints.h +++ b/include/SDL_hints.h @@ -799,9 +799,32 @@ extern "C" { * * pthread hint values are "current", "other", "fifo" and "rr". * Currently no other platform hint values are defined but may be in the future. +* +* \note On Linux, the kernel may send SIGKILL to realtime tasks which exceed the distro +* configured execution budget for rtkit. This budget is queriably through RLIMIT_RTTIME +* after calling SDL_SetThreadPriority(). */ #define SDL_HINT_THREAD_PRIORITY_POLICY "SDL_THREAD_PRIORITY_POLICY" +/** + * \brief Specifies whether SDL_THREAD_PRIORITY_TIME_CRITICAL should be treated as realtime. + * + * On some platforms, like Linux, a realtime priority thread may be subject to restrictions + * that require special handling by the application. This hint exists to let SDL know that + * the app is prepared to handle said restrictions. + * + * On Linux, SDL will apply the following configuration to any thread that becomes realtime: + * * The SCHED_RESET_ON_FORK bit will be set on the scheduling policy, + * * An RLIMIT_RTTIME budget will be configured to the rtkit specified limit. + * * Exceeding this limit will result in the kernel sending SIGKILL to the app, + * * Refer to the man pages for more information. + * + * This variable can be set to the following values: + * "0" - default platform specific behaviour + * "1" - Force SDL_THREAD_PRIORITY_TIME_CRITICAL to a realtime scheduling policy + */ +#define SDL_HINT_THREAD_FORCE_REALTIME_TIME_CRITICAL "SDL_THREAD_FORCE_REALTIME_TIME_CRITICAL" + /** * \brief If set to 1, then do not allow high-DPI windows. ("Retina" on Mac and iOS) */ diff --git a/src/core/linux/SDL_threadprio.c b/src/core/linux/SDL_threadprio.c index 275c15ca7..5b1835c9c 100644 --- a/src/core/linux/SDL_threadprio.c +++ b/src/core/linux/SDL_threadprio.c @@ -50,6 +50,7 @@ static pthread_once_t rtkit_initialize_once = PTHREAD_ONCE_INIT; static Sint32 rtkit_min_nice_level = -20; static Sint32 rtkit_max_realtime_priority = 99; +static Sint64 rtkit_max_rttime_usec = 200000; static void rtkit_initialize() @@ -67,10 +68,16 @@ rtkit_initialize() DBUS_TYPE_INT32, &rtkit_max_realtime_priority)) { rtkit_max_realtime_priority = 99; } + + /* Try getting maximum rttime allowed by rtkit: exceeding this value will result in SIGKILL */ + if (!dbus || !SDL_DBus_QueryPropertyOnConnection(dbus->system_conn, RTKIT_DBUS_NODE, RTKIT_DBUS_PATH, RTKIT_DBUS_INTERFACE, "RTTimeUSecMax", + DBUS_TYPE_INT64, &rtkit_max_rttime_usec)) { + rtkit_max_rttime_usec = 200000; + } } static SDL_bool -rtkit_initialize_thread() +rtkit_initialize_realtime_thread() { // Following is an excerpt from rtkit README that outlines the requirements // a thread must meet before making rtkit requests: @@ -107,7 +114,8 @@ rtkit_initialize_thread() } // Current rtkit allows a max of 200ms right now - rlimit.rlim_cur = rlimit.rlim_max = 100000; + rlimit.rlim_max = rtkit_max_rttime_usec; + rlimit.rlim_cur = rlimit.rlim_max / 2; err = setrlimit(nLimit, &rlimit); if (err) { @@ -142,14 +150,6 @@ rtkit_setpriority_nice(pid_t thread, int nice_level) if (si32 < rtkit_min_nice_level) si32 = rtkit_min_nice_level; - // We always perform the thread state changes necessary for rtkit. - // This wastes some system calls if the state is already set but - // typically code sets a thread priority and leaves it so it's - // not expected that this wasted effort will be an issue. - // We also do not quit if this fails, we let the rtkit request - // go through to determine whether it really needs to fail or not. - rtkit_initialize_thread(); - if (!dbus || !SDL_DBus_CallMethodOnConnection(dbus->system_conn, RTKIT_DBUS_NODE, RTKIT_DBUS_PATH, RTKIT_DBUS_INTERFACE, "MakeThreadHighPriority", DBUS_TYPE_UINT64, &ui64, DBUS_TYPE_INT32, &si32, DBUS_TYPE_INVALID, @@ -177,7 +177,7 @@ rtkit_setpriority_realtime(pid_t thread, int rt_priority) // not expected that this wasted effort will be an issue. // We also do not quit if this fails, we let the rtkit request // go through to determine whether it really needs to fail or not. - rtkit_initialize_thread(); + rtkit_initialize_realtime_thread(); if (!dbus || !SDL_DBus_CallMethodOnConnection(dbus->system_conn, RTKIT_DBUS_NODE, RTKIT_DBUS_PATH, RTKIT_DBUS_INTERFACE, "MakeThreadRealtime", diff --git a/src/thread/pthread/SDL_systhread.c b/src/thread/pthread/SDL_systhread.c index 6df049c78..b6cd04a5a 100644 --- a/src/thread/pthread/SDL_systhread.c +++ b/src/thread/pthread/SDL_systhread.c @@ -208,6 +208,7 @@ SDL_SYS_SetThreadPriority(SDL_ThreadPriority priority) int pri_policy; pthread_t thread = pthread_self(); const char *policyhint = SDL_GetHint(SDL_HINT_THREAD_PRIORITY_POLICY); + const SDL_bool allow_realtime_hint = SDL_GetHintBoolean(SDL_HINT_THREAD_FORCE_REALTIME_TIME_CRITICAL, SDL_FALSE); if (pthread_getschedparam(thread, &policy, &sched) != 0) { return SDL_SetError("pthread_getschedparam() failed"); @@ -223,8 +224,14 @@ SDL_SYS_SetThreadPriority(SDL_ThreadPriority priority) break; case SDL_THREAD_PRIORITY_HIGH: case SDL_THREAD_PRIORITY_TIME_CRITICAL: +#if defined(__MACOSX__) || defined(__IPHONEOS__) || defined(__TVOS__) + /* Apple requires SCHED_RR for high priority threads */ pri_policy = SCHED_RR; break; +#else + pri_policy = allow_realtime_hint ? SCHED_RR : SCHED_OTHER; + break; +#endif default: pri_policy = policy; break;