SDL_systhread.c 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  1. /*
  2. Simple DirectMedia Layer
  3. Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
  4. This software is provided 'as-is', without any express or implied
  5. warranty. In no event will the authors be held liable for any damages
  6. arising from the use of this software.
  7. Permission is granted to anyone to use this software for any purpose,
  8. including commercial applications, and to alter it and redistribute it
  9. freely, subject to the following restrictions:
  10. 1. The origin of this software must not be misrepresented; you must not
  11. claim that you wrote the original software. If you use this software
  12. in a product, an acknowledgment in the product documentation would be
  13. appreciated but is not required.
  14. 2. Altered source versions must be plainly marked as such, and must not be
  15. misrepresented as being the original software.
  16. 3. This notice may not be removed or altered from any source distribution.
  17. */
  18. #include "SDL_internal.h"
  19. #include <pthread.h>
  20. #ifdef HAVE_PTHREAD_NP_H
  21. #include <pthread_np.h>
  22. #endif
  23. #ifdef HAVE_SIGNAL_H
  24. #include <signal.h>
  25. #endif
  26. #include <errno.h>
  27. #ifdef SDL_PLATFORM_LINUX
  28. #include <sys/time.h>
  29. #include <sys/resource.h>
  30. #include <sys/syscall.h>
  31. #include <unistd.h>
  32. #include "../../core/linux/SDL_dbus.h"
  33. #endif // SDL_PLATFORM_LINUX
  34. #if (defined(SDL_PLATFORM_LINUX) || defined(SDL_PLATFORM_ANDROID) || defined(SDL_PLATFORM_MACOS) || defined(SDL_PLATFORM_IOS)) && defined(HAVE_DLOPEN)
  35. #include <dlfcn.h>
  36. #ifndef RTLD_DEFAULT
  37. #define RTLD_DEFAULT NULL
  38. #endif
  39. #endif
  40. #include "../SDL_thread_c.h"
  41. #include "../SDL_systhread.h"
  42. #ifdef SDL_PLATFORM_ANDROID
  43. #include "../../core/android/SDL_android.h"
  44. #endif
  45. #ifdef SDL_PLATFORM_HAIKU
  46. #include <kernel/OS.h>
  47. #endif
  48. #ifdef HAVE_SIGNAL_H
  49. // List of signals to mask in the subthreads
  50. static const int sig_list[] = {
  51. SIGHUP, SIGINT, SIGQUIT, SIGPIPE, SIGALRM, SIGTERM, SIGWINCH,
  52. SIGVTALRM, SIGPROF, 0
  53. };
  54. #endif
  55. static void *RunThread(void *data)
  56. {
  57. #ifdef SDL_PLATFORM_ANDROID
  58. Android_JNI_SetupThread();
  59. #endif
  60. SDL_RunThread((SDL_Thread *)data);
  61. return NULL;
  62. }
  63. #if (defined(SDL_PLATFORM_MACOS) || defined(SDL_PLATFORM_IOS)) && defined(HAVE_DLOPEN)
  64. static bool checked_setname = false;
  65. static int (*ppthread_setname_np)(const char *) = NULL;
  66. #elif (defined(SDL_PLATFORM_LINUX) || defined(SDL_PLATFORM_ANDROID)) && defined(HAVE_DLOPEN)
  67. static bool checked_setname = false;
  68. static int (*ppthread_setname_np)(pthread_t, const char *) = NULL;
  69. #endif
  70. bool SDL_SYS_CreateThread(SDL_Thread *thread,
  71. SDL_FunctionPointer pfnBeginThread,
  72. SDL_FunctionPointer pfnEndThread)
  73. {
  74. pthread_attr_t type;
  75. // do this here before any threads exist, so there's no race condition.
  76. #if (defined(SDL_PLATFORM_MACOS) || defined(SDL_PLATFORM_IOS) || defined(SDL_PLATFORM_LINUX) || defined(SDL_PLATFORM_ANDROID)) && defined(HAVE_DLOPEN)
  77. if (!checked_setname) {
  78. void *fn = dlsym(RTLD_DEFAULT, "pthread_setname_np");
  79. #if defined(SDL_PLATFORM_MACOS) || defined(SDL_PLATFORM_IOS)
  80. ppthread_setname_np = (int (*)(const char *))fn;
  81. #elif defined(SDL_PLATFORM_LINUX) || defined(SDL_PLATFORM_ANDROID)
  82. ppthread_setname_np = (int (*)(pthread_t, const char *))fn;
  83. #endif
  84. checked_setname = true;
  85. }
  86. #endif
  87. // Set the thread attributes
  88. if (pthread_attr_init(&type) != 0) {
  89. return SDL_SetError("Couldn't initialize pthread attributes");
  90. }
  91. pthread_attr_setdetachstate(&type, PTHREAD_CREATE_JOINABLE);
  92. // Set caller-requested stack size. Otherwise: use the system default.
  93. if (thread->stacksize) {
  94. pthread_attr_setstacksize(&type, thread->stacksize);
  95. }
  96. // Create the thread and go!
  97. if (pthread_create(&thread->handle, &type, RunThread, thread) != 0) {
  98. return SDL_SetError("Not enough resources to create thread");
  99. }
  100. thread->threadid = (SDL_ThreadID) thread->handle; // the SDL thread ID is just the pthread_t.
  101. return true;
  102. }
  103. void SDL_SYS_SetupThread(const char *name)
  104. {
  105. #ifdef HAVE_SIGNAL_H
  106. int i;
  107. sigset_t mask;
  108. #endif
  109. if (name) {
  110. #if (defined(SDL_PLATFORM_MACOS) || defined(SDL_PLATFORM_IOS) || defined(SDL_PLATFORM_LINUX) || defined(SDL_PLATFORM_ANDROID)) && defined(HAVE_DLOPEN)
  111. SDL_assert(checked_setname);
  112. if (ppthread_setname_np) {
  113. #if defined(SDL_PLATFORM_MACOS) || defined(SDL_PLATFORM_IOS)
  114. ppthread_setname_np(name);
  115. #elif defined(SDL_PLATFORM_LINUX) || defined(SDL_PLATFORM_ANDROID)
  116. if (ppthread_setname_np(pthread_self(), name) == ERANGE) {
  117. char namebuf[16]; // Limited to 16 char
  118. SDL_strlcpy(namebuf, name, sizeof(namebuf));
  119. ppthread_setname_np(pthread_self(), namebuf);
  120. }
  121. #endif
  122. }
  123. #elif defined(HAVE_PTHREAD_SETNAME_NP)
  124. #ifdef SDL_PLATFORM_NETBSD
  125. pthread_setname_np(pthread_self(), "%s", name);
  126. #else
  127. if (pthread_setname_np(pthread_self(), name) == ERANGE) {
  128. char namebuf[16]; // Limited to 16 char
  129. SDL_strlcpy(namebuf, name, sizeof(namebuf));
  130. pthread_setname_np(pthread_self(), namebuf);
  131. }
  132. #endif
  133. #elif defined(HAVE_PTHREAD_SET_NAME_NP)
  134. pthread_set_name_np(pthread_self(), name);
  135. #elif defined(SDL_PLATFORM_HAIKU)
  136. // The docs say the thread name can't be longer than B_OS_NAME_LENGTH.
  137. char namebuf[B_OS_NAME_LENGTH];
  138. SDL_strlcpy(namebuf, name, sizeof(namebuf));
  139. rename_thread(find_thread(NULL), namebuf);
  140. #endif
  141. }
  142. #ifdef HAVE_SIGNAL_H
  143. // Mask asynchronous signals for this thread
  144. sigemptyset(&mask);
  145. for (i = 0; sig_list[i]; ++i) {
  146. sigaddset(&mask, sig_list[i]);
  147. }
  148. pthread_sigmask(SIG_BLOCK, &mask, NULL);
  149. #endif
  150. #ifdef PTHREAD_CANCEL_ASYNCHRONOUS
  151. // Allow ourselves to be asynchronously cancelled
  152. {
  153. int oldstate;
  154. pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldstate);
  155. }
  156. #endif
  157. }
  158. SDL_ThreadID SDL_GetCurrentThreadID(void)
  159. {
  160. return (SDL_ThreadID)pthread_self();
  161. }
  162. bool SDL_SYS_SetThreadPriority(SDL_ThreadPriority priority)
  163. {
  164. #ifdef SDL_PLATFORM_RISCOS
  165. // FIXME: Setting thread priority does not seem to be supported
  166. return true;
  167. #else
  168. struct sched_param sched;
  169. int policy;
  170. int pri_policy;
  171. pthread_t thread = pthread_self();
  172. const char *policyhint = SDL_GetHint(SDL_HINT_THREAD_PRIORITY_POLICY);
  173. const bool timecritical_realtime_hint = SDL_GetHintBoolean(SDL_HINT_THREAD_FORCE_REALTIME_TIME_CRITICAL, false);
  174. if (pthread_getschedparam(thread, &policy, &sched) != 0) {
  175. return SDL_SetError("pthread_getschedparam() failed");
  176. }
  177. /* Higher priority levels may require changing the pthread scheduler policy
  178. * for the thread. SDL will make such changes by default but there is
  179. * also a hint allowing that behavior to be overridden. */
  180. switch (priority) {
  181. case SDL_THREAD_PRIORITY_LOW:
  182. case SDL_THREAD_PRIORITY_NORMAL:
  183. pri_policy = SCHED_OTHER;
  184. break;
  185. case SDL_THREAD_PRIORITY_HIGH:
  186. case SDL_THREAD_PRIORITY_TIME_CRITICAL:
  187. #if defined(SDL_PLATFORM_MACOS) || defined(SDL_PLATFORM_IOS) || defined(SDL_PLATFORM_TVOS)
  188. // Apple requires SCHED_RR for high priority threads
  189. pri_policy = SCHED_RR;
  190. break;
  191. #else
  192. pri_policy = SCHED_OTHER;
  193. break;
  194. #endif
  195. default:
  196. pri_policy = policy;
  197. break;
  198. }
  199. if (timecritical_realtime_hint && priority == SDL_THREAD_PRIORITY_TIME_CRITICAL) {
  200. pri_policy = SCHED_RR;
  201. }
  202. if (policyhint) {
  203. if (SDL_strcmp(policyhint, "current") == 0) {
  204. // Leave current thread scheduler policy unchanged
  205. } else if (SDL_strcmp(policyhint, "other") == 0) {
  206. policy = SCHED_OTHER;
  207. } else if (SDL_strcmp(policyhint, "rr") == 0) {
  208. policy = SCHED_RR;
  209. } else if (SDL_strcmp(policyhint, "fifo") == 0) {
  210. policy = SCHED_FIFO;
  211. } else {
  212. policy = pri_policy;
  213. }
  214. } else {
  215. policy = pri_policy;
  216. }
  217. #ifdef SDL_PLATFORM_LINUX
  218. {
  219. pid_t linuxTid = syscall(SYS_gettid);
  220. return SDL_SetLinuxThreadPriorityAndPolicy(linuxTid, priority, policy);
  221. }
  222. #else
  223. if (priority == SDL_THREAD_PRIORITY_LOW) {
  224. sched.sched_priority = sched_get_priority_min(policy);
  225. } else if (priority == SDL_THREAD_PRIORITY_TIME_CRITICAL) {
  226. #if defined(__QNX__)
  227. /* io_snd complains about a client thread having priority >= 49 */
  228. sched.sched_priority = 48;
  229. #else
  230. sched.sched_priority = sched_get_priority_max(policy);
  231. #endif
  232. } else {
  233. int min_priority = sched_get_priority_min(policy);
  234. int max_priority = sched_get_priority_max(policy);
  235. #if defined(SDL_PLATFORM_MACOS) || defined(SDL_PLATFORM_IOS) || defined(SDL_PLATFORM_TVOS)
  236. if (min_priority == 15 && max_priority == 47) {
  237. // Apple has a specific set of thread priorities
  238. if (priority == SDL_THREAD_PRIORITY_HIGH) {
  239. sched.sched_priority = 45;
  240. } else {
  241. sched.sched_priority = 37;
  242. }
  243. } else
  244. #endif // SDL_PLATFORM_MACOS || SDL_PLATFORM_IOS || SDL_PLATFORM_TVOS
  245. {
  246. sched.sched_priority = (min_priority + (max_priority - min_priority) / 2);
  247. if (priority == SDL_THREAD_PRIORITY_HIGH) {
  248. sched.sched_priority += ((max_priority - min_priority) / 4);
  249. }
  250. }
  251. }
  252. if (pthread_setschedparam(thread, policy, &sched) != 0) {
  253. return SDL_SetError("pthread_setschedparam() failed");
  254. }
  255. return true;
  256. #endif // linux
  257. #endif // #if SDL_PLATFORM_RISCOS
  258. }
  259. void SDL_SYS_WaitThread(SDL_Thread *thread)
  260. {
  261. pthread_join(thread->handle, NULL);
  262. }
  263. void SDL_SYS_DetachThread(SDL_Thread *thread)
  264. {
  265. pthread_detach(thread->handle);
  266. }