SDL_systhread.c (9435B)
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 #include "SDL_system.h" 24 #include "SDL_hints.h" 25 26 #include <pthread.h> 27 28 #if HAVE_PTHREAD_NP_H 29 #include <pthread_np.h> 30 #endif 31 32 #include <signal.h> 33 34 #ifdef __LINUX__ 35 #include <sys/time.h> 36 #include <sys/resource.h> 37 #include <sys/syscall.h> 38 #include <unistd.h> 39 #include <errno.h> 40 41 #include "../../core/linux/SDL_dbus.h" 42 #endif /* __LINUX__ */ 43 44 #if defined(__LINUX__) || defined(__MACOSX__) || defined(__IPHONEOS__) 45 #include <dlfcn.h> 46 #ifndef RTLD_DEFAULT 47 #define RTLD_DEFAULT NULL 48 #endif 49 #endif 50 51 #include "SDL_platform.h" 52 #include "SDL_thread.h" 53 #include "../SDL_thread_c.h" 54 #include "../SDL_systhread.h" 55 #ifdef __ANDROID__ 56 #include "../../core/android/SDL_android.h" 57 #endif 58 59 #ifdef __HAIKU__ 60 #include <kernel/OS.h> 61 #endif 62 63 64 #ifndef __NACL__ 65 /* List of signals to mask in the subthreads */ 66 static const int sig_list[] = { 67 SIGHUP, SIGINT, SIGQUIT, SIGPIPE, SIGALRM, SIGTERM, SIGCHLD, SIGWINCH, 68 SIGVTALRM, SIGPROF, 0 69 }; 70 #endif 71 72 static void * 73 RunThread(void *data) 74 { 75 #ifdef __ANDROID__ 76 Android_JNI_SetupThread(); 77 #endif 78 SDL_RunThread((SDL_Thread *) data); 79 return NULL; 80 } 81 82 #if defined(__MACOSX__) || defined(__IPHONEOS__) 83 static SDL_bool checked_setname = SDL_FALSE; 84 static int (*ppthread_setname_np)(const char*) = NULL; 85 #elif defined(__LINUX__) 86 static SDL_bool checked_setname = SDL_FALSE; 87 static int (*ppthread_setname_np)(pthread_t, const char*) = NULL; 88 #endif 89 int 90 SDL_SYS_CreateThread(SDL_Thread * thread) 91 { 92 pthread_attr_t type; 93 94 /* do this here before any threads exist, so there's no race condition. */ 95 #if defined(__MACOSX__) || defined(__IPHONEOS__) || defined(__LINUX__) 96 if (!checked_setname) { 97 void *fn = dlsym(RTLD_DEFAULT, "pthread_setname_np"); 98 #if defined(__MACOSX__) || defined(__IPHONEOS__) 99 ppthread_setname_np = (int(*)(const char*)) fn; 100 #elif defined(__LINUX__) 101 ppthread_setname_np = (int(*)(pthread_t, const char*)) fn; 102 #endif 103 checked_setname = SDL_TRUE; 104 } 105 #endif 106 107 /* Set the thread attributes */ 108 if (pthread_attr_init(&type) != 0) { 109 return SDL_SetError("Couldn't initialize pthread attributes"); 110 } 111 pthread_attr_setdetachstate(&type, PTHREAD_CREATE_JOINABLE); 112 113 /* Set caller-requested stack size. Otherwise: use the system default. */ 114 if (thread->stacksize) { 115 pthread_attr_setstacksize(&type, thread->stacksize); 116 } 117 118 /* Create the thread and go! */ 119 if (pthread_create(&thread->handle, &type, RunThread, thread) != 0) { 120 return SDL_SetError("Not enough resources to create thread"); 121 } 122 123 return 0; 124 } 125 126 void 127 SDL_SYS_SetupThread(const char *name) 128 { 129 #if !defined(__NACL__) 130 int i; 131 sigset_t mask; 132 #endif /* !__NACL__ */ 133 134 if (name != NULL) { 135 #if defined(__MACOSX__) || defined(__IPHONEOS__) || defined(__LINUX__) 136 SDL_assert(checked_setname); 137 if (ppthread_setname_np != NULL) { 138 #if defined(__MACOSX__) || defined(__IPHONEOS__) 139 ppthread_setname_np(name); 140 #elif defined(__LINUX__) 141 ppthread_setname_np(pthread_self(), name); 142 #endif 143 } 144 #elif HAVE_PTHREAD_SETNAME_NP 145 #if defined(__NETBSD__) 146 pthread_setname_np(pthread_self(), "%s", name); 147 #else 148 pthread_setname_np(pthread_self(), name); 149 #endif 150 #elif HAVE_PTHREAD_SET_NAME_NP 151 pthread_set_name_np(pthread_self(), name); 152 #elif defined(__HAIKU__) 153 /* The docs say the thread name can't be longer than B_OS_NAME_LENGTH. */ 154 char namebuf[B_OS_NAME_LENGTH]; 155 SDL_snprintf(namebuf, sizeof (namebuf), "%s", name); 156 namebuf[sizeof (namebuf) - 1] = '\0'; 157 rename_thread(find_thread(NULL), namebuf); 158 #endif 159 } 160 161 /* NativeClient does not yet support signals.*/ 162 #if !defined(__NACL__) 163 /* Mask asynchronous signals for this thread */ 164 sigemptyset(&mask); 165 for (i = 0; sig_list[i]; ++i) { 166 sigaddset(&mask, sig_list[i]); 167 } 168 pthread_sigmask(SIG_BLOCK, &mask, 0); 169 #endif /* !__NACL__ */ 170 171 172 #ifdef PTHREAD_CANCEL_ASYNCHRONOUS 173 /* Allow ourselves to be asynchronously cancelled */ 174 { 175 int oldstate; 176 pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldstate); 177 } 178 #endif 179 } 180 181 SDL_threadID 182 SDL_ThreadID(void) 183 { 184 return ((SDL_threadID) pthread_self()); 185 } 186 187 #if __LINUX__ 188 /** 189 \brief Sets the SDL priority (not nice level) for a thread, using setpriority() if appropriate, and RealtimeKit if available. 190 Differs from SDL_LinuxSetThreadPriority in also taking the desired scheduler policy, 191 such as SCHED_OTHER or SCHED_RR. 192 193 \return 0 on success, or -1 on error. 194 */ 195 extern DECLSPEC int SDLCALL SDL_LinuxSetThreadPriorityAndPolicy(Sint64 threadID, int sdlPriority, int schedPolicy); 196 #endif 197 198 int 199 SDL_SYS_SetThreadPriority(SDL_ThreadPriority priority) 200 { 201 #if __NACL__ || __RISCOS__ 202 /* FIXME: Setting thread priority does not seem to be supported in NACL */ 203 return 0; 204 #else 205 struct sched_param sched; 206 int policy; 207 int pri_policy; 208 pthread_t thread = pthread_self(); 209 const char *policyhint = SDL_GetHint(SDL_HINT_THREAD_PRIORITY_POLICY); 210 const SDL_bool timecritical_realtime_hint = SDL_GetHintBoolean(SDL_HINT_THREAD_FORCE_REALTIME_TIME_CRITICAL, SDL_FALSE); 211 212 if (pthread_getschedparam(thread, &policy, &sched) != 0) { 213 return SDL_SetError("pthread_getschedparam() failed"); 214 } 215 216 /* Higher priority levels may require changing the pthread scheduler policy 217 * for the thread. SDL will make such changes by default but there is 218 * also a hint allowing that behavior to be overridden. */ 219 switch (priority) { 220 case SDL_THREAD_PRIORITY_LOW: 221 case SDL_THREAD_PRIORITY_NORMAL: 222 pri_policy = SCHED_OTHER; 223 break; 224 case SDL_THREAD_PRIORITY_HIGH: 225 case SDL_THREAD_PRIORITY_TIME_CRITICAL: 226 #if defined(__MACOSX__) || defined(__IPHONEOS__) || defined(__TVOS__) 227 /* Apple requires SCHED_RR for high priority threads */ 228 pri_policy = SCHED_RR; 229 break; 230 #else 231 pri_policy = SCHED_OTHER; 232 break; 233 #endif 234 default: 235 pri_policy = policy; 236 break; 237 } 238 239 if (timecritical_realtime_hint && priority == SDL_THREAD_PRIORITY_TIME_CRITICAL) { 240 pri_policy = SCHED_RR; 241 } 242 243 if (policyhint) { 244 if (SDL_strcmp(policyhint, "current") == 0) { 245 /* Leave current thread scheduler policy unchanged */ 246 } else if (SDL_strcmp(policyhint, "other") == 0) { 247 policy = SCHED_OTHER; 248 } else if (SDL_strcmp(policyhint, "rr") == 0) { 249 policy = SCHED_RR; 250 } else if (SDL_strcmp(policyhint, "fifo") == 0) { 251 policy = SCHED_FIFO; 252 } else { 253 policy = pri_policy; 254 } 255 } else { 256 policy = pri_policy; 257 } 258 259 #if __LINUX__ 260 { 261 pid_t linuxTid = syscall(SYS_gettid); 262 return SDL_LinuxSetThreadPriorityAndPolicy(linuxTid, priority, policy); 263 } 264 #else 265 if (priority == SDL_THREAD_PRIORITY_LOW) { 266 sched.sched_priority = sched_get_priority_min(policy); 267 } else if (priority == SDL_THREAD_PRIORITY_TIME_CRITICAL) { 268 sched.sched_priority = sched_get_priority_max(policy); 269 } else { 270 int min_priority = sched_get_priority_min(policy); 271 int max_priority = sched_get_priority_max(policy); 272 273 #if defined(__MACOSX__) || defined(__IPHONEOS__) || defined(__TVOS__) 274 if (min_priority == 15 && max_priority == 47) { 275 /* Apple has a specific set of thread priorities */ 276 if (priority == SDL_THREAD_PRIORITY_HIGH) { 277 sched.sched_priority = 45; 278 } else { 279 sched.sched_priority = 37; 280 } 281 } else 282 #endif /* __MACOSX__ || __IPHONEOS__ || __TVOS__ */ 283 { 284 sched.sched_priority = (min_priority + (max_priority - min_priority) / 2); 285 if (priority == SDL_THREAD_PRIORITY_HIGH) { 286 sched.sched_priority += ((max_priority - min_priority) / 4); 287 } 288 } 289 } 290 if (pthread_setschedparam(thread, policy, &sched) != 0) { 291 return SDL_SetError("pthread_setschedparam() failed"); 292 } 293 return 0; 294 #endif /* linux */ 295 #endif /* #if __NACL__ || __RISCOS__ */ 296 } 297 298 void 299 SDL_SYS_WaitThread(SDL_Thread * thread) 300 { 301 pthread_join(thread->handle, 0); 302 } 303 304 void 305 SDL_SYS_DetachThread(SDL_Thread * thread) 306 { 307 pthread_detach(thread->handle); 308 } 309 310 /* vi: set ts=4 sw=4 expandtab: */