SDL_threadprio.c (10132B)
1 /* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org> 4 5 This software is provided 'as-is', without any express or implied 6 warranty. In no event will the authors be held liable for any damages 7 arising from the use of this software. 8 9 Permission is granted to anyone to use this software for any purpose, 10 including commercial applications, and to alter it and redistribute it 11 freely, subject to the following restrictions: 12 13 1. The origin of this software must not be misrepresented; you must not 14 claim that you wrote the original software. If you use this software 15 in a product, an acknowledgment in the product documentation would be 16 appreciated but is not required. 17 2. Altered source versions must be plainly marked as such, and must not be 18 misrepresented as being the original software. 19 3. This notice may not be removed or altered from any source distribution. 20 */ 21 #include "../../SDL_internal.h" 22 23 #ifdef __LINUX__ 24 25 #include "SDL_error.h" 26 #include "SDL_stdinc.h" 27 #include "SDL_thread.h" 28 29 #if !SDL_THREADS_DISABLED 30 #include <sys/time.h> 31 #include <sys/resource.h> 32 #include <pthread.h> 33 #include "SDL_system.h" 34 35 /* RLIMIT_RTTIME requires kernel >= 2.6.25 and is in glibc >= 2.14 */ 36 #ifndef RLIMIT_RTTIME 37 #define RLIMIT_RTTIME 15 38 #endif 39 /* SCHED_RESET_ON_FORK is in kernel >= 2.6.32. */ 40 #ifndef SCHED_RESET_ON_FORK 41 #define SCHED_RESET_ON_FORK 0x40000000 42 #endif 43 44 #include "SDL_dbus.h" 45 46 #if SDL_USE_LIBDBUS 47 #include <sched.h> 48 49 /* d-bus queries to org.freedesktop.RealtimeKit1. */ 50 #define RTKIT_DBUS_NODE "org.freedesktop.RealtimeKit1" 51 #define RTKIT_DBUS_PATH "/org/freedesktop/RealtimeKit1" 52 #define RTKIT_DBUS_INTERFACE "org.freedesktop.RealtimeKit1" 53 54 static pthread_once_t rtkit_initialize_once = PTHREAD_ONCE_INIT; 55 static Sint32 rtkit_min_nice_level = -20; 56 static Sint32 rtkit_max_realtime_priority = 99; 57 static Sint64 rtkit_max_rttime_usec = 200000; 58 59 static void 60 rtkit_initialize() 61 { 62 SDL_DBusContext *dbus = SDL_DBus_GetContext(); 63 64 /* Try getting minimum nice level: this is often greater than PRIO_MIN (-20). */ 65 if (!dbus || !SDL_DBus_QueryPropertyOnConnection(dbus->system_conn, RTKIT_DBUS_NODE, RTKIT_DBUS_PATH, RTKIT_DBUS_INTERFACE, "MinNiceLevel", 66 DBUS_TYPE_INT32, &rtkit_min_nice_level)) { 67 rtkit_min_nice_level = -20; 68 } 69 70 /* Try getting maximum realtime priority: this can be less than the POSIX default (99). */ 71 if (!dbus || !SDL_DBus_QueryPropertyOnConnection(dbus->system_conn, RTKIT_DBUS_NODE, RTKIT_DBUS_PATH, RTKIT_DBUS_INTERFACE, "MaxRealtimePriority", 72 DBUS_TYPE_INT32, &rtkit_max_realtime_priority)) { 73 rtkit_max_realtime_priority = 99; 74 } 75 76 /* Try getting maximum rttime allowed by rtkit: exceeding this value will result in SIGKILL */ 77 if (!dbus || !SDL_DBus_QueryPropertyOnConnection(dbus->system_conn, RTKIT_DBUS_NODE, RTKIT_DBUS_PATH, RTKIT_DBUS_INTERFACE, "RTTimeUSecMax", 78 DBUS_TYPE_INT64, &rtkit_max_rttime_usec)) { 79 rtkit_max_rttime_usec = 200000; 80 } 81 } 82 83 static SDL_bool 84 rtkit_initialize_realtime_thread() 85 { 86 // Following is an excerpt from rtkit README that outlines the requirements 87 // a thread must meet before making rtkit requests: 88 // 89 // * Only clients with RLIMIT_RTTIME set will get RT scheduling 90 // 91 // * RT scheduling will only be handed out to processes with 92 // SCHED_RESET_ON_FORK set to guarantee that the scheduling 93 // settings cannot 'leak' to child processes, thus making sure 94 // that 'RT fork bombs' cannot be used to bypass RLIMIT_RTTIME 95 // and take the system down. 96 // 97 // * Limits are enforced on all user controllable resources, only 98 // a maximum number of users, processes, threads can request RT 99 // scheduling at the same time. 100 // 101 // * Only a limited number of threads may be made RT in a 102 // specific time frame. 103 // 104 // * Client authorization is verified with PolicyKit 105 106 int err; 107 struct rlimit rlimit; 108 int nLimit = RLIMIT_RTTIME; 109 pid_t nPid = 0; //self 110 int nSchedPolicy = sched_getscheduler(nPid) | SCHED_RESET_ON_FORK; 111 struct sched_param schedParam = {}; 112 113 // Requirement #1: Set RLIMIT_RTTIME 114 err = getrlimit(nLimit, &rlimit); 115 if (err) 116 { 117 return SDL_FALSE; 118 } 119 120 // Current rtkit allows a max of 200ms right now 121 rlimit.rlim_max = rtkit_max_rttime_usec; 122 rlimit.rlim_cur = rlimit.rlim_max / 2; 123 err = setrlimit(nLimit, &rlimit); 124 if (err) 125 { 126 return SDL_FALSE; 127 } 128 129 // Requirement #2: Add SCHED_RESET_ON_FORK to the scheduler policy 130 err = sched_getparam(nPid, &schedParam); 131 if (err) 132 { 133 return SDL_FALSE; 134 } 135 136 err = sched_setscheduler(nPid, nSchedPolicy, &schedParam); 137 if (err) 138 { 139 return SDL_FALSE; 140 } 141 142 return SDL_TRUE; 143 } 144 145 static SDL_bool 146 rtkit_setpriority_nice(pid_t thread, int nice_level) 147 { 148 Uint64 ui64 = (Uint64)thread; 149 Sint32 si32 = (Sint32)nice_level; 150 SDL_DBusContext *dbus = SDL_DBus_GetContext(); 151 152 pthread_once(&rtkit_initialize_once, rtkit_initialize); 153 154 if (si32 < rtkit_min_nice_level) 155 si32 = rtkit_min_nice_level; 156 157 if (!dbus || !SDL_DBus_CallMethodOnConnection(dbus->system_conn, 158 RTKIT_DBUS_NODE, RTKIT_DBUS_PATH, RTKIT_DBUS_INTERFACE, "MakeThreadHighPriority", 159 DBUS_TYPE_UINT64, &ui64, DBUS_TYPE_INT32, &si32, DBUS_TYPE_INVALID, 160 DBUS_TYPE_INVALID)) { 161 return SDL_FALSE; 162 } 163 return SDL_TRUE; 164 } 165 166 static SDL_bool 167 rtkit_setpriority_realtime(pid_t thread, int rt_priority) 168 { 169 Uint64 ui64 = (Uint64)thread; 170 Uint32 ui32 = (Uint32)rt_priority; 171 SDL_DBusContext *dbus = SDL_DBus_GetContext(); 172 173 pthread_once(&rtkit_initialize_once, rtkit_initialize); 174 175 if (ui32 > rtkit_max_realtime_priority) 176 ui32 = rtkit_max_realtime_priority; 177 178 // We always perform the thread state changes necessary for rtkit. 179 // This wastes some system calls if the state is already set but 180 // typically code sets a thread priority and leaves it so it's 181 // not expected that this wasted effort will be an issue. 182 // We also do not quit if this fails, we let the rtkit request 183 // go through to determine whether it really needs to fail or not. 184 rtkit_initialize_realtime_thread(); 185 186 if (!dbus || !SDL_DBus_CallMethodOnConnection(dbus->system_conn, 187 RTKIT_DBUS_NODE, RTKIT_DBUS_PATH, RTKIT_DBUS_INTERFACE, "MakeThreadRealtime", 188 DBUS_TYPE_UINT64, &ui64, DBUS_TYPE_UINT32, &ui32, DBUS_TYPE_INVALID, 189 DBUS_TYPE_INVALID)) { 190 return SDL_FALSE; 191 } 192 return SDL_TRUE; 193 } 194 #else 195 196 #define rtkit_max_realtime_priority 99 197 198 #endif /* dbus */ 199 #endif /* threads */ 200 201 /* this is a public symbol, so it has to exist even if threads are disabled. */ 202 int 203 SDL_LinuxSetThreadPriority(Sint64 threadID, int priority) 204 { 205 #if SDL_THREADS_DISABLED 206 return SDL_Unsupported(); 207 #else 208 if (setpriority(PRIO_PROCESS, (id_t)threadID, priority) == 0) { 209 return 0; 210 } 211 212 #if SDL_USE_LIBDBUS 213 /* Note that this fails you most likely: 214 * Have your process's scheduler incorrectly configured. 215 See the requirements at: 216 http://git.0pointer.net/rtkit.git/tree/README#n16 217 * Encountered dbus/polkit security restrictions. Note 218 that the RealtimeKit1 dbus endpoint is inaccessible 219 over ssh connections for most common distro configs. 220 You might want to check your local config for details: 221 /usr/share/polkit-1/actions/org.freedesktop.RealtimeKit1.policy 222 223 README and sample code at: http://git.0pointer.net/rtkit.git 224 */ 225 if (rtkit_setpriority_nice((pid_t)threadID, priority)) { 226 return 0; 227 } 228 #endif 229 230 return SDL_SetError("setpriority() failed"); 231 #endif 232 } 233 234 /* this is a public symbol, so it has to exist even if threads are disabled. */ 235 int 236 SDL_LinuxSetThreadPriorityAndPolicy(Sint64 threadID, int sdlPriority, int schedPolicy) 237 { 238 #if SDL_THREADS_DISABLED 239 return SDL_Unsupported(); 240 #else 241 int osPriority; 242 243 if (schedPolicy == SCHED_RR || schedPolicy == SCHED_FIFO) { 244 if (sdlPriority == SDL_THREAD_PRIORITY_LOW) { 245 osPriority = 1; 246 } else if (sdlPriority == SDL_THREAD_PRIORITY_HIGH) { 247 osPriority = rtkit_max_realtime_priority * 3 / 4; 248 } else if (sdlPriority == SDL_THREAD_PRIORITY_TIME_CRITICAL) { 249 osPriority = rtkit_max_realtime_priority; 250 } else { 251 osPriority = rtkit_max_realtime_priority / 2; 252 } 253 } else { 254 if (sdlPriority == SDL_THREAD_PRIORITY_LOW) { 255 osPriority = 19; 256 } else if (sdlPriority == SDL_THREAD_PRIORITY_HIGH) { 257 osPriority = -10; 258 } else if (sdlPriority == SDL_THREAD_PRIORITY_TIME_CRITICAL) { 259 osPriority = -20; 260 } else { 261 osPriority = 0; 262 } 263 264 if (setpriority(PRIO_PROCESS, (id_t)threadID, osPriority) == 0) { 265 return 0; 266 } 267 } 268 269 #if SDL_USE_LIBDBUS 270 /* Note that this fails you most likely: 271 * Have your process's scheduler incorrectly configured. 272 See the requirements at: 273 http://git.0pointer.net/rtkit.git/tree/README#n16 274 * Encountered dbus/polkit security restrictions. Note 275 that the RealtimeKit1 dbus endpoint is inaccessible 276 over ssh connections for most common distro configs. 277 You might want to check your local config for details: 278 /usr/share/polkit-1/actions/org.freedesktop.RealtimeKit1.policy 279 280 README and sample code at: http://git.0pointer.net/rtkit.git 281 */ 282 if (schedPolicy == SCHED_RR || schedPolicy == SCHED_FIFO) { 283 if (rtkit_setpriority_realtime((pid_t)threadID, osPriority)) { 284 return 0; 285 } 286 } else { 287 if (rtkit_setpriority_nice((pid_t)threadID, osPriority)) { 288 return 0; 289 } 290 } 291 #endif 292 293 return SDL_SetError("setpriority() failed"); 294 #endif 295 } 296 297 #endif /* __LINUX__ */ 298 299 /* vi: set ts=4 sw=4 expandtab: */