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