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