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 */
65static const int sig_list[] = {
66 SIGHUP, SIGINT, SIGQUIT, SIGPIPE, SIGALRM, SIGTERM, SIGCHLD, SIGWINCH,
67 SIGVTALRM, SIGPROF, 0
68};
69#endif
70
71static void *
72RunThread(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__)
82static SDL_bool checked_setname = SDL_FALSE;
83static int (*ppthread_setname_np)(const char*) = NULL;
84#elif defined(__LINUX__)
85static SDL_bool checked_setname = SDL_FALSE;
86static int (*ppthread_setname_np)(pthread_t, const char*) = NULL;
87#endif
88int
89SDL_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
125void
126SDL_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
180SDL_threadID
181SDL_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 */
194extern DECLSPEC int SDLCALL SDL_LinuxSetThreadPriorityAndPolicy(Sint64 threadID, int sdlPriority, int schedPolicy);
195#endif
196
197int
198SDL_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
297void
298SDL_SYS_WaitThread(SDL_Thread * thread)
299{
300 pthread_join(thread->handle, 0);
301}
302
303void
304SDL_SYS_DetachThread(SDL_Thread * thread)
305{
306 pthread_detach(thread->handle);
307}
308
309/* vi: set ts=4 sw=4 expandtab: */
310