Path: blob/master/thirdparty/sdl/core/linux/SDL_threadprio.c
10279 views
/*1Simple DirectMedia Layer2Copyright (C) 1997-2025 Sam Lantinga <[email protected]>34This software is provided 'as-is', without any express or implied5warranty. In no event will the authors be held liable for any damages6arising from the use of this software.78Permission is granted to anyone to use this software for any purpose,9including commercial applications, and to alter it and redistribute it10freely, subject to the following restrictions:11121. The origin of this software must not be misrepresented; you must not13claim that you wrote the original software. If you use this software14in a product, an acknowledgment in the product documentation would be15appreciated but is not required.162. Altered source versions must be plainly marked as such, and must not be17misrepresented as being the original software.183. This notice may not be removed or altered from any source distribution.19*/20#include "SDL_internal.h"2122#ifdef SDL_PLATFORM_LINUX2324#ifndef SDL_THREADS_DISABLED25#include <sys/time.h>26#include <sys/resource.h>27#include <pthread.h>28#include <sched.h>29#include <unistd.h>3031// RLIMIT_RTTIME requires kernel >= 2.6.25 and is in glibc >= 2.1432#ifndef RLIMIT_RTTIME33#define RLIMIT_RTTIME 1534#endif35// SCHED_RESET_ON_FORK is in kernel >= 2.6.32.36#ifndef SCHED_RESET_ON_FORK37#define SCHED_RESET_ON_FORK 0x4000000038#endif3940#include "SDL_dbus.h"4142#ifdef SDL_USE_LIBDBUS4344// d-bus queries to org.freedesktop.RealtimeKit1.45#define RTKIT_DBUS_NODE "org.freedesktop.RealtimeKit1"46#define RTKIT_DBUS_PATH "/org/freedesktop/RealtimeKit1"47#define RTKIT_DBUS_INTERFACE "org.freedesktop.RealtimeKit1"4849// d-bus queries to the XDG portal interface to RealtimeKit150#define XDG_PORTAL_DBUS_NODE "org.freedesktop.portal.Desktop"51#define XDG_PORTAL_DBUS_PATH "/org/freedesktop/portal/desktop"52#define XDG_PORTAL_DBUS_INTERFACE "org.freedesktop.portal.Realtime"5354static bool rtkit_use_session_conn;55static const char *rtkit_dbus_node;56static const char *rtkit_dbus_path;57static const char *rtkit_dbus_interface;5859static pthread_once_t rtkit_initialize_once = PTHREAD_ONCE_INIT;60static Sint32 rtkit_min_nice_level = -20;61static Sint32 rtkit_max_realtime_priority = 99;62static Sint64 rtkit_max_rttime_usec = 200000;6364/*65* Checking that the RTTimeUSecMax property exists and is an int64 confirms that:66* - The desktop portal exists and supports the realtime interface.67* - The realtime interface is new enough to have the required bug fixes applied.68*/69static bool realtime_portal_supported(DBusConnection *conn)70{71Sint64 res;72return SDL_DBus_QueryPropertyOnConnection(conn, XDG_PORTAL_DBUS_NODE, XDG_PORTAL_DBUS_PATH, XDG_PORTAL_DBUS_INTERFACE,73"RTTimeUSecMax", DBUS_TYPE_INT64, &res);74}7576static void set_rtkit_interface(void)77{78SDL_DBusContext *dbus = SDL_DBus_GetContext();7980// xdg-desktop-portal works in all instances, so check for it first.81if (dbus && realtime_portal_supported(dbus->session_conn)) {82rtkit_use_session_conn = true;83rtkit_dbus_node = XDG_PORTAL_DBUS_NODE;84rtkit_dbus_path = XDG_PORTAL_DBUS_PATH;85rtkit_dbus_interface = XDG_PORTAL_DBUS_INTERFACE;86} else { // Fall back to the standard rtkit interface in all other cases.87rtkit_use_session_conn = false;88rtkit_dbus_node = RTKIT_DBUS_NODE;89rtkit_dbus_path = RTKIT_DBUS_PATH;90rtkit_dbus_interface = RTKIT_DBUS_INTERFACE;91}92}9394static DBusConnection *get_rtkit_dbus_connection(void)95{96SDL_DBusContext *dbus = SDL_DBus_GetContext();9798if (dbus) {99return rtkit_use_session_conn ? dbus->session_conn : dbus->system_conn;100}101102return NULL;103}104105static void rtkit_initialize(void)106{107DBusConnection *dbus_conn;108109set_rtkit_interface();110dbus_conn = get_rtkit_dbus_connection();111112// Try getting minimum nice level: this is often greater than PRIO_MIN (-20).113if (!dbus_conn || !SDL_DBus_QueryPropertyOnConnection(dbus_conn, rtkit_dbus_node, rtkit_dbus_path, rtkit_dbus_interface, "MinNiceLevel",114DBUS_TYPE_INT32, &rtkit_min_nice_level)) {115rtkit_min_nice_level = -20;116}117118// Try getting maximum realtime priority: this can be less than the POSIX default (99).119if (!dbus_conn || !SDL_DBus_QueryPropertyOnConnection(dbus_conn, rtkit_dbus_node, rtkit_dbus_path, rtkit_dbus_interface, "MaxRealtimePriority",120DBUS_TYPE_INT32, &rtkit_max_realtime_priority)) {121rtkit_max_realtime_priority = 99;122}123124// Try getting maximum rttime allowed by rtkit: exceeding this value will result in SIGKILL125if (!dbus_conn || !SDL_DBus_QueryPropertyOnConnection(dbus_conn, rtkit_dbus_node, rtkit_dbus_path, rtkit_dbus_interface, "RTTimeUSecMax",126DBUS_TYPE_INT64, &rtkit_max_rttime_usec)) {127rtkit_max_rttime_usec = 200000;128}129}130131static bool rtkit_initialize_realtime_thread(void)132{133// Following is an excerpt from rtkit README that outlines the requirements134// a thread must meet before making rtkit requests:135//136// * Only clients with RLIMIT_RTTIME set will get RT scheduling137//138// * RT scheduling will only be handed out to processes with139// SCHED_RESET_ON_FORK set to guarantee that the scheduling140// settings cannot 'leak' to child processes, thus making sure141// that 'RT fork bombs' cannot be used to bypass RLIMIT_RTTIME142// and take the system down.143//144// * Limits are enforced on all user controllable resources, only145// a maximum number of users, processes, threads can request RT146// scheduling at the same time.147//148// * Only a limited number of threads may be made RT in a149// specific time frame.150//151// * Client authorization is verified with PolicyKit152153int err;154struct rlimit rlimit;155int nLimit = RLIMIT_RTTIME;156pid_t nPid = 0; // self157int nSchedPolicy = sched_getscheduler(nPid) | SCHED_RESET_ON_FORK;158struct sched_param schedParam;159160SDL_zero(schedParam);161162// Requirement #1: Set RLIMIT_RTTIME163err = getrlimit(nLimit, &rlimit);164if (err) {165return false;166}167168// Current rtkit allows a max of 200ms right now169rlimit.rlim_max = rtkit_max_rttime_usec;170rlimit.rlim_cur = rlimit.rlim_max / 2;171err = setrlimit(nLimit, &rlimit);172if (err) {173return false;174}175176// Requirement #2: Add SCHED_RESET_ON_FORK to the scheduler policy177err = sched_getparam(nPid, &schedParam);178if (err) {179return false;180}181182err = sched_setscheduler(nPid, nSchedPolicy, &schedParam);183if (err) {184return false;185}186187return true;188}189190static bool rtkit_setpriority_nice(pid_t thread, int nice_level)191{192DBusConnection *dbus_conn;193Uint64 pid = (Uint64)getpid();194Uint64 tid = (Uint64)thread;195Sint32 nice = (Sint32)nice_level;196197pthread_once(&rtkit_initialize_once, rtkit_initialize);198dbus_conn = get_rtkit_dbus_connection();199200if (nice < rtkit_min_nice_level) {201nice = rtkit_min_nice_level;202}203204if (!dbus_conn || !SDL_DBus_CallMethodOnConnection(dbus_conn,205rtkit_dbus_node, rtkit_dbus_path, rtkit_dbus_interface, "MakeThreadHighPriorityWithPID",206DBUS_TYPE_UINT64, &pid, DBUS_TYPE_UINT64, &tid, DBUS_TYPE_INT32, &nice, DBUS_TYPE_INVALID,207DBUS_TYPE_INVALID)) {208return false;209}210return true;211}212213static bool rtkit_setpriority_realtime(pid_t thread, int rt_priority)214{215DBusConnection *dbus_conn;216Uint64 pid = (Uint64)getpid();217Uint64 tid = (Uint64)thread;218Uint32 priority = (Uint32)rt_priority;219220pthread_once(&rtkit_initialize_once, rtkit_initialize);221dbus_conn = get_rtkit_dbus_connection();222223if (priority > rtkit_max_realtime_priority) {224priority = rtkit_max_realtime_priority;225}226227// We always perform the thread state changes necessary for rtkit.228// This wastes some system calls if the state is already set but229// typically code sets a thread priority and leaves it so it's230// not expected that this wasted effort will be an issue.231// We also do not quit if this fails, we let the rtkit request232// go through to determine whether it really needs to fail or not.233rtkit_initialize_realtime_thread();234235if (!dbus_conn || !SDL_DBus_CallMethodOnConnection(dbus_conn,236rtkit_dbus_node, rtkit_dbus_path, rtkit_dbus_interface, "MakeThreadRealtimeWithPID",237DBUS_TYPE_UINT64, &pid, DBUS_TYPE_UINT64, &tid, DBUS_TYPE_UINT32, &priority, DBUS_TYPE_INVALID,238DBUS_TYPE_INVALID)) {239return false;240}241return true;242}243#else244245#define rtkit_max_realtime_priority 99246247#endif // dbus248#endif // threads249250// this is a public symbol, so it has to exist even if threads are disabled.251bool SDL_SetLinuxThreadPriority(Sint64 threadID, int priority)252{253#ifdef SDL_THREADS_DISABLED254return SDL_Unsupported();255#else256if (setpriority(PRIO_PROCESS, (id_t)threadID, priority) == 0) {257return true;258}259260#ifdef SDL_USE_LIBDBUS261/* Note that this fails you most likely:262* Have your process's scheduler incorrectly configured.263See the requirements at:264http://git.0pointer.net/rtkit.git/tree/README#n16265* Encountered dbus/polkit security restrictions. Note266that the RealtimeKit1 dbus endpoint is inaccessible267over ssh connections for most common distro configs.268You might want to check your local config for details:269/usr/share/polkit-1/actions/org.freedesktop.RealtimeKit1.policy270271README and sample code at: http://git.0pointer.net/rtkit.git272*/273if (rtkit_setpriority_nice((pid_t)threadID, priority)) {274return true;275}276#endif277278return SDL_SetError("setpriority() failed");279#endif280}281282// this is a public symbol, so it has to exist even if threads are disabled.283bool SDL_SetLinuxThreadPriorityAndPolicy(Sint64 threadID, int sdlPriority, int schedPolicy)284{285#ifdef SDL_THREADS_DISABLED286return SDL_Unsupported();287#else288int osPriority;289290if (schedPolicy == SCHED_RR || schedPolicy == SCHED_FIFO) {291if (sdlPriority == SDL_THREAD_PRIORITY_LOW) {292osPriority = 1;293} else if (sdlPriority == SDL_THREAD_PRIORITY_HIGH) {294osPriority = rtkit_max_realtime_priority * 3 / 4;295} else if (sdlPriority == SDL_THREAD_PRIORITY_TIME_CRITICAL) {296osPriority = rtkit_max_realtime_priority;297} else {298osPriority = rtkit_max_realtime_priority / 2;299}300} else {301if (sdlPriority == SDL_THREAD_PRIORITY_LOW) {302osPriority = 19;303} else if (sdlPriority == SDL_THREAD_PRIORITY_HIGH) {304osPriority = -10;305} else if (sdlPriority == SDL_THREAD_PRIORITY_TIME_CRITICAL) {306osPriority = -20;307} else {308osPriority = 0;309}310311if (setpriority(PRIO_PROCESS, (id_t)threadID, osPriority) == 0) {312return true;313}314}315316#ifdef SDL_USE_LIBDBUS317/* Note that this fails you most likely:318* Have your process's scheduler incorrectly configured.319See the requirements at:320http://git.0pointer.net/rtkit.git/tree/README#n16321* Encountered dbus/polkit security restrictions. Note322that the RealtimeKit1 dbus endpoint is inaccessible323over ssh connections for most common distro configs.324You might want to check your local config for details:325/usr/share/polkit-1/actions/org.freedesktop.RealtimeKit1.policy326327README and sample code at: http://git.0pointer.net/rtkit.git328*/329if (schedPolicy == SCHED_RR || schedPolicy == SCHED_FIFO) {330if (rtkit_setpriority_realtime((pid_t)threadID, osPriority)) {331return true;332}333} else {334if (rtkit_setpriority_nice((pid_t)threadID, osPriority)) {335return true;336}337}338#endif339340return SDL_SetError("setpriority() failed");341#endif342}343344#endif // SDL_PLATFORM_LINUX345346347