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