1/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
2/* If you are missing that file, acquire a complete release at teeworlds.com. */
3
4#include "thread.h"
5
6#include "dbg.h"
7#include "windows.h"
8
9#if defined(CONF_FAMILY_UNIX)
10#include <pthread.h>
11#include <sched.h>
12
13#include <cstdlib>
14
15#if defined(CONF_PLATFORM_MACOS)
16#if defined(__MAC_10_10) && __MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_10_10
17#include <pthread/qos.h>
18#endif
19#endif
20
21#if defined(CONF_PLATFORM_EMSCRIPTEN)
22#include <emscripten/emscripten.h>
23#endif
24
25#elif defined(CONF_FAMILY_WINDOWS)
26#include <windows.h>
27#else
28#error NOT IMPLEMENTED
29#endif
30
31struct THREAD_RUN
32{
33 void (*threadfunc)(void *);
34 void *u;
35};
36
37#if defined(CONF_FAMILY_UNIX)
38static void *thread_run(void *user)
39#elif defined(CONF_FAMILY_WINDOWS)
40static DWORD __stdcall thread_run(void *user)
41#else
42#error not implemented
43#endif
44{
45#if defined(CONF_FAMILY_WINDOWS)
46 CWindowsComLifecycle WindowsComLifecycle(false);
47#endif
48 struct THREAD_RUN *data = (THREAD_RUN *)user;
49 void (*threadfunc)(void *) = data->threadfunc;
50 void *u = data->u;
51 free(ptr: data);
52 threadfunc(u);
53#if defined(CONF_FAMILY_UNIX)
54 return nullptr;
55#elif defined(CONF_FAMILY_WINDOWS)
56 return 0;
57#else
58#error not implemented
59#endif
60}
61
62void *thread_init(void (*threadfunc)(void *), void *u, const char *name)
63{
64 struct THREAD_RUN *data = (THREAD_RUN *)malloc(size: sizeof(*data));
65 data->threadfunc = threadfunc;
66 data->u = u;
67#if defined(CONF_FAMILY_UNIX)
68 {
69 pthread_attr_t attr;
70 dbg_assert(pthread_attr_init(&attr) == 0, "pthread_attr_init failure");
71#if defined(CONF_PLATFORM_MACOS) && defined(__MAC_10_10) && __MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_10_10
72 dbg_assert(pthread_attr_set_qos_class_np(&attr, QOS_CLASS_USER_INTERACTIVE, 0) == 0, "pthread_attr_set_qos_class_np failure");
73#endif
74 pthread_t id;
75 dbg_assert(pthread_create(&id, &attr, thread_run, data) == 0, "pthread_create failure");
76#if defined(CONF_PLATFORM_EMSCRIPTEN)
77 // Return control to the browser's main thread to allow the pthread to be started,
78 // otherwise we deadlock when waiting for a thread immediately after starting it.
79 emscripten_sleep(0);
80#endif
81 return (void *)id;
82 }
83#elif defined(CONF_FAMILY_WINDOWS)
84 HANDLE thread = CreateThread(nullptr, 0, thread_run, data, 0, nullptr);
85 dbg_assert(thread != nullptr, "CreateThread failure");
86 HMODULE kernel_base_handle;
87 if(GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_PIN, L"KernelBase.dll", &kernel_base_handle))
88 {
89 // Intentional
90#ifdef __MINGW32__
91#pragma GCC diagnostic push
92#pragma GCC diagnostic ignored "-Wcast-function-type"
93#endif
94 auto set_thread_description_function = reinterpret_cast<HRESULT(WINAPI *)(HANDLE, PCWSTR)>(GetProcAddress(kernel_base_handle, "SetThreadDescription"));
95#ifdef __MINGW32__
96#pragma GCC diagnostic pop
97#endif
98 if(set_thread_description_function)
99 set_thread_description_function(thread, windows_utf8_to_wide(name).c_str());
100 }
101 return thread;
102#else
103#error not implemented
104#endif
105}
106
107void thread_wait(void *thread)
108{
109#if defined(CONF_PLATFORM_EMSCRIPTEN)
110 // TODO: Remove this workaround when https://github.com/emscripten-core/emscripten/issues/9910 is fixed.
111 while(true)
112 {
113 const int join_result = pthread_tryjoin_np((pthread_t)thread, nullptr);
114 if(join_result == 0)
115 {
116 break;
117 }
118 dbg_assert(join_result == EBUSY, "pthread_tryjoin_np failure");
119 // Busy waiting so we can periodically yield control to browser's
120 // main thread because blocking on the main thread is very bad.
121 emscripten_sleep(10);
122 }
123#elif defined(CONF_FAMILY_UNIX)
124 dbg_assert(pthread_join((pthread_t)thread, nullptr) == 0, "pthread_join failure");
125#elif defined(CONF_FAMILY_WINDOWS)
126 dbg_assert(WaitForSingleObject((HANDLE)thread, INFINITE) == WAIT_OBJECT_0, "WaitForSingleObject failure");
127 dbg_assert(CloseHandle(thread), "CloseHandle failure");
128#else
129#error not implemented
130#endif
131}
132
133void thread_yield()
134{
135#if defined(CONF_FAMILY_UNIX)
136 dbg_assert(sched_yield() == 0, "sched_yield failure");
137#elif defined(CONF_FAMILY_WINDOWS)
138 Sleep(0);
139#else
140#error not implemented
141#endif
142}
143
144void thread_detach(void *thread)
145{
146#if defined(CONF_FAMILY_UNIX)
147 dbg_assert(pthread_detach((pthread_t)thread) == 0, "pthread_detach failure");
148#elif defined(CONF_FAMILY_WINDOWS)
149 dbg_assert(CloseHandle(thread), "CloseHandle failure");
150#else
151#error not implemented
152#endif
153}
154
155void thread_init_and_detach(void (*threadfunc)(void *), void *u, const char *name)
156{
157 void *thread = thread_init(threadfunc, u, name);
158 thread_detach(thread);
159}
160