SDL  2.0
SDL_threadprio.c
Go to the documentation of this file.
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: */
#define SDL_SetError
#define SDL_LinuxSetThreadPriority
#define SDL_Unsupported()
Definition: SDL_error.h:89
SDL_bool
Definition: SDL_stdinc.h:168
@ SDL_TRUE
Definition: SDL_stdinc.h:170
@ SDL_FALSE
Definition: SDL_stdinc.h:169
int32_t Sint32
Definition: SDL_stdinc.h:203
uint64_t Uint64
Definition: SDL_stdinc.h:222
uint32_t Uint32
Definition: SDL_stdinc.h:209
int64_t Sint64
Definition: SDL_stdinc.h:216
@ SDL_THREAD_PRIORITY_TIME_CRITICAL
Definition: SDL_thread.h:68
@ SDL_THREAD_PRIORITY_LOW
Definition: SDL_thread.h:65
@ SDL_THREAD_PRIORITY_HIGH
Definition: SDL_thread.h:67