sdl

FORK: Simple Directmedia Layer
git clone https://git.neptards.moe/neptards/sdl.git
Log | Files | Refs

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: */