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 #include "../../SDL_internal.h"
22 
23 /* Thread management routines for SDL */
24 
25 extern "C" {
26 #include "SDL_thread.h"
27 #include "../SDL_thread_c.h"
28 #include "../SDL_systhread.h"
29 }
30 
31 #include <mutex>
32 #include <thread>
33 #include <system_error>
34 
35 #ifdef __WINRT__
36 #include <Windows.h>
37 #endif
38 
39 static void
RunThread(void * args)40 RunThread(void *args)
41 {
42     SDL_RunThread((SDL_Thread *) args);
43 }
44 
45 extern "C"
46 int
SDL_SYS_CreateThread(SDL_Thread * thread)47 SDL_SYS_CreateThread(SDL_Thread * thread)
48 {
49     try {
50         // !!! FIXME: no way to set a thread stack size here.
51         std::thread cpp_thread(RunThread, thread);
52         thread->handle = (void *) new std::thread(std::move(cpp_thread));
53         return 0;
54     } catch (std::system_error & ex) {
55         SDL_SetError("unable to start a C++ thread: code=%d; %s", ex.code(), ex.what());
56         return -1;
57     } catch (std::bad_alloc &) {
58         SDL_OutOfMemory();
59         return -1;
60     }
61 }
62 
63 extern "C"
64 void
SDL_SYS_SetupThread(const char * name)65 SDL_SYS_SetupThread(const char *name)
66 {
67     // Make sure a thread ID gets assigned ASAP, for debugging purposes:
68     SDL_ThreadID();
69     return;
70 }
71 
72 extern "C"
73 SDL_threadID
SDL_ThreadID(void)74 SDL_ThreadID(void)
75 {
76 #ifdef __WINRT__
77     return GetCurrentThreadId();
78 #else
79     // HACK: Mimick a thread ID, if one isn't otherwise available.
80     static thread_local SDL_threadID current_thread_id = 0;
81     static SDL_threadID next_thread_id = 1;
82     static std::mutex next_thread_id_mutex;
83 
84     if (current_thread_id == 0) {
85         std::lock_guard<std::mutex> lock(next_thread_id_mutex);
86         current_thread_id = next_thread_id;
87         ++next_thread_id;
88     }
89 
90     return current_thread_id;
91 #endif
92 }
93 
94 extern "C"
95 int
SDL_SYS_SetThreadPriority(SDL_ThreadPriority priority)96 SDL_SYS_SetThreadPriority(SDL_ThreadPriority priority)
97 {
98 #ifdef __WINRT__
99     int value;
100 
101     if (priority == SDL_THREAD_PRIORITY_LOW) {
102         value = THREAD_PRIORITY_LOWEST;
103     }
104     else if (priority == SDL_THREAD_PRIORITY_HIGH) {
105         value = THREAD_PRIORITY_HIGHEST;
106     }
107     else if (priority == SDL_THREAD_PRIORITY_TIME_CRITICAL) {
108         // FIXME: WinRT does not support TIME_CRITICAL! -flibit
109         SDL_LogWarn(SDL_LOG_CATEGORY_SYSTEM, "TIME_CRITICAL unsupported, falling back to HIGHEST");
110         value = THREAD_PRIORITY_HIGHEST;
111     }
112     else {
113         value = THREAD_PRIORITY_NORMAL;
114     }
115     if (!SetThreadPriority(GetCurrentThread(), value)) {
116         return WIN_SetError("SetThreadPriority()");
117     }
118     return 0;
119 #else
120     return SDL_Unsupported();
121 #endif
122 }
123 
124 extern "C"
125 void
SDL_SYS_WaitThread(SDL_Thread * thread)126 SDL_SYS_WaitThread(SDL_Thread * thread)
127 {
128     if ( ! thread) {
129         return;
130     }
131 
132     try {
133         std::thread * cpp_thread = (std::thread *) thread->handle;
134         if (cpp_thread->joinable()) {
135             cpp_thread->join();
136         }
137     } catch (std::system_error &) {
138         // An error occurred when joining the thread.  SDL_WaitThread does not,
139         // however, seem to provide a means to report errors to its callers
140         // though!
141     }
142 }
143 
144 extern "C"
145 void
SDL_SYS_DetachThread(SDL_Thread * thread)146 SDL_SYS_DetachThread(SDL_Thread * thread)
147 {
148     if ( ! thread) {
149         return;
150     }
151 
152     try {
153         std::thread * cpp_thread = (std::thread *) thread->handle;
154         if (cpp_thread->joinable()) {
155             cpp_thread->detach();
156         }
157     } catch (std::system_error &) {
158         // An error occurred when detaching the thread.  SDL_DetachThread does not,
159         // however, seem to provide a means to report errors to its callers
160         // though!
161     }
162 }
163 
164 extern "C"
165 SDL_TLSData *
SDL_SYS_GetTLSData(void)166 SDL_SYS_GetTLSData(void)
167 {
168     return SDL_Generic_GetTLSData();
169 }
170 
171 extern "C"
172 int
SDL_SYS_SetTLSData(SDL_TLSData * data)173 SDL_SYS_SetTLSData(SDL_TLSData *data)
174 {
175     return SDL_Generic_SetTLSData(data);
176 }
177 
178 /* vi: set ts=4 sw=4 expandtab: */
179