SDL_systhread.c 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  1. /*
  2. Simple DirectMedia Layer
  3. Copyright (C) 1997-2024 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, SIGCHLD, 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. return true;
  101. }
  102. void SDL_SYS_SetupThread(const char *name)
  103. {
  104. #ifdef HAVE_SIGNAL_H
  105. int i;
  106. sigset_t mask;
  107. #endif
  108. if (name) {
  109. #if (defined(SDL_PLATFORM_MACOS) || defined(SDL_PLATFORM_IOS) || defined(SDL_PLATFORM_LINUX) || defined(SDL_PLATFORM_ANDROID)) && defined(HAVE_DLOPEN)
  110. SDL_assert(checked_setname);
  111. if (ppthread_setname_np) {
  112. #if defined(SDL_PLATFORM_MACOS) || defined(SDL_PLATFORM_IOS)
  113. ppthread_setname_np(name);
  114. #elif defined(SDL_PLATFORM_LINUX) || defined(SDL_PLATFORM_ANDROID)
  115. if (ppthread_setname_np(pthread_self(), name) == ERANGE) {
  116. char namebuf[16]; // Limited to 16 char
  117. SDL_strlcpy(namebuf, name, sizeof(namebuf));
  118. ppthread_setname_np(pthread_self(), namebuf);
  119. }
  120. #endif
  121. }
  122. #elif defined(HAVE_PTHREAD_SETNAME_NP)
  123. #ifdef SDL_PLATFORM_NETBSD
  124. pthread_setname_np(pthread_self(), "%s", name);
  125. #else
  126. if (pthread_setname_np(pthread_self(), name) == ERANGE) {
  127. char namebuf[16]; // Limited to 16 char
  128. SDL_strlcpy(namebuf, name, sizeof(namebuf));
  129. pthread_setname_np(pthread_self(), namebuf);
  130. }
  131. #endif
  132. #elif defined(HAVE_PTHREAD_SET_NAME_NP)
  133. pthread_set_name_np(pthread_self(), name);
  134. #elif defined(SDL_PLATFORM_HAIKU)
  135. // The docs say the thread name can't be longer than B_OS_NAME_LENGTH.
  136. char namebuf[B_OS_NAME_LENGTH];
  137. SDL_strlcpy(namebuf, name, sizeof(namebuf));
  138. rename_thread(find_thread(NULL), namebuf);
  139. #endif
  140. }
  141. #ifdef HAVE_SIGNAL_H
  142. // Mask asynchronous signals for this thread
  143. sigemptyset(&mask);
  144. for (i = 0; sig_list[i]; ++i) {
  145. sigaddset(&mask, sig_list[i]);
  146. }
  147. pthread_sigmask(SIG_BLOCK, &mask, 0);
  148. #endif
  149. #ifdef PTHREAD_CANCEL_ASYNCHRONOUS
  150. // Allow ourselves to be asynchronously cancelled
  151. {
  152. int oldstate;
  153. pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldstate);
  154. }
  155. #endif
  156. }
  157. SDL_ThreadID SDL_GetCurrentThreadID(void)
  158. {
  159. return (SDL_ThreadID)pthread_self();
  160. }
  161. bool SDL_SYS_SetThreadPriority(SDL_ThreadPriority priority)
  162. {
  163. #ifdef SDL_PLATFORM_RISCOS
  164. // FIXME: Setting thread priority does not seem to be supported
  165. return true;
  166. #else
  167. struct sched_param sched;
  168. int policy;
  169. int pri_policy;
  170. pthread_t thread = pthread_self();
  171. const char *policyhint = SDL_GetHint(SDL_HINT_THREAD_PRIORITY_POLICY);
  172. const bool timecritical_realtime_hint = SDL_GetHintBoolean(SDL_HINT_THREAD_FORCE_REALTIME_TIME_CRITICAL, false);
  173. if (pthread_getschedparam(thread, &policy, &sched) != 0) {
  174. return SDL_SetError("pthread_getschedparam() failed");
  175. }
  176. /* Higher priority levels may require changing the pthread scheduler policy
  177. * for the thread. SDL will make such changes by default but there is
  178. * also a hint allowing that behavior to be overridden. */
  179. switch (priority) {
  180. case SDL_THREAD_PRIORITY_LOW:
  181. case SDL_THREAD_PRIORITY_NORMAL:
  182. pri_policy = SCHED_OTHER;
  183. break;
  184. case SDL_THREAD_PRIORITY_HIGH:
  185. case SDL_THREAD_PRIORITY_TIME_CRITICAL:
  186. #if defined(SDL_PLATFORM_MACOS) || defined(SDL_PLATFORM_IOS) || defined(SDL_PLATFORM_TVOS)
  187. // Apple requires SCHED_RR for high priority threads
  188. pri_policy = SCHED_RR;
  189. break;
  190. #else
  191. pri_policy = SCHED_OTHER;
  192. break;
  193. #endif
  194. default:
  195. pri_policy = policy;
  196. break;
  197. }
  198. if (timecritical_realtime_hint && priority == SDL_THREAD_PRIORITY_TIME_CRITICAL) {
  199. pri_policy = SCHED_RR;
  200. }
  201. if (policyhint) {
  202. if (SDL_strcmp(policyhint, "current") == 0) {
  203. // Leave current thread scheduler policy unchanged
  204. } else if (SDL_strcmp(policyhint, "other") == 0) {
  205. policy = SCHED_OTHER;
  206. } else if (SDL_strcmp(policyhint, "rr") == 0) {
  207. policy = SCHED_RR;
  208. } else if (SDL_strcmp(policyhint, "fifo") == 0) {
  209. policy = SCHED_FIFO;
  210. } else {
  211. policy = pri_policy;
  212. }
  213. } else {
  214. policy = pri_policy;
  215. }
  216. #ifdef SDL_PLATFORM_LINUX
  217. {
  218. pid_t linuxTid = syscall(SYS_gettid);
  219. return SDL_SetLinuxThreadPriorityAndPolicy(linuxTid, priority, policy);
  220. }
  221. #else
  222. if (priority == SDL_THREAD_PRIORITY_LOW) {
  223. sched.sched_priority = sched_get_priority_min(policy);
  224. } else if (priority == SDL_THREAD_PRIORITY_TIME_CRITICAL) {
  225. sched.sched_priority = sched_get_priority_max(policy);
  226. } else {
  227. int min_priority = sched_get_priority_min(policy);
  228. int max_priority = sched_get_priority_max(policy);
  229. #if defined(SDL_PLATFORM_MACOS) || defined(SDL_PLATFORM_IOS) || defined(SDL_PLATFORM_TVOS)
  230. if (min_priority == 15 && max_priority == 47) {
  231. // Apple has a specific set of thread priorities
  232. if (priority == SDL_THREAD_PRIORITY_HIGH) {
  233. sched.sched_priority = 45;
  234. } else {
  235. sched.sched_priority = 37;
  236. }
  237. } else
  238. #endif // SDL_PLATFORM_MACOS || SDL_PLATFORM_IOS || SDL_PLATFORM_TVOS
  239. {
  240. sched.sched_priority = (min_priority + (max_priority - min_priority) / 2);
  241. if (priority == SDL_THREAD_PRIORITY_HIGH) {
  242. sched.sched_priority += ((max_priority - min_priority) / 4);
  243. }
  244. }
  245. }
  246. if (pthread_setschedparam(thread, policy, &sched) != 0) {
  247. return SDL_SetError("pthread_setschedparam() failed");
  248. }
  249. return true;
  250. #endif // linux
  251. #endif // #if SDL_PLATFORM_RISCOS
  252. }
  253. void SDL_SYS_WaitThread(SDL_Thread *thread)
  254. {
  255. pthread_join(thread->handle, 0);
  256. }
  257. void SDL_SYS_DetachThread(SDL_Thread *thread)
  258. {
  259. pthread_detach(thread->handle);
  260. }