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 unsigned long __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 return 0;
54}
55
56void *thread_init(void (*threadfunc)(void *), void *u, const char *name)
57{
58 struct THREAD_RUN *data = (THREAD_RUN *)malloc(size: sizeof(*data));
59 data->threadfunc = threadfunc;
60 data->u = u;
61#if defined(CONF_FAMILY_UNIX)
62 {
63 pthread_attr_t attr;
64 dbg_assert(pthread_attr_init(&attr) == 0, "pthread_attr_init failure");
65#if defined(CONF_PLATFORM_MACOS) && defined(__MAC_10_10) && __MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_10_10
66 dbg_assert(pthread_attr_set_qos_class_np(&attr, QOS_CLASS_USER_INTERACTIVE, 0) == 0, "pthread_attr_set_qos_class_np failure");
67#endif
68 pthread_t id;
69 dbg_assert(pthread_create(&id, &attr, thread_run, data) == 0, "pthread_create failure");
70#if defined(CONF_PLATFORM_EMSCRIPTEN)
71 // Return control to the browser's main thread to allow the pthread to be started,
72 // otherwise we deadlock when waiting for a thread immediately after starting it.
73 emscripten_sleep(0);
74#endif
75 return (void *)id;
76 }
77#elif defined(CONF_FAMILY_WINDOWS)
78 HANDLE thread = CreateThread(nullptr, 0, thread_run, data, 0, nullptr);
79 dbg_assert(thread != nullptr, "CreateThread failure");
80 HMODULE kernel_base_handle;
81 if(GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_PIN, L"KernelBase.dll", &kernel_base_handle))
82 {
83 // Intentional
84#ifdef __MINGW32__
85#pragma GCC diagnostic push
86#pragma GCC diagnostic ignored "-Wcast-function-type"
87#endif
88 auto set_thread_description_function = reinterpret_cast<HRESULT(WINAPI *)(HANDLE, PCWSTR)>(GetProcAddress(kernel_base_handle, "SetThreadDescription"));
89#ifdef __MINGW32__
90#pragma GCC diagnostic pop
91#endif
92 if(set_thread_description_function)
93 set_thread_description_function(thread, windows_utf8_to_wide(name).c_str());
94 }
95 return thread;
96#else
97#error not implemented
98#endif
99}
100
101void thread_wait(void *thread)
102{
103#if defined(CONF_PLATFORM_EMSCRIPTEN)
104 // TODO: Remove this workaround when https://github.com/emscripten-core/emscripten/issues/9910 is fixed.
105 while(true)
106 {
107 const int join_result = pthread_tryjoin_np((pthread_t)thread, nullptr);
108 if(join_result == 0)
109 {
110 break;
111 }
112 dbg_assert(join_result == EBUSY, "pthread_tryjoin_np failure");
113 // Busy waiting so we can periodically yield control to browser's
114 // main thread because blocking on the main thread is very bad.
115 emscripten_sleep(10);
116 }
117#elif defined(CONF_FAMILY_UNIX)
118 dbg_assert(pthread_join((pthread_t)thread, nullptr) == 0, "pthread_join failure");
119#elif defined(CONF_FAMILY_WINDOWS)
120 dbg_assert(WaitForSingleObject((HANDLE)thread, INFINITE) == WAIT_OBJECT_0, "WaitForSingleObject failure");
121 dbg_assert(CloseHandle(thread), "CloseHandle failure");
122#else
123#error not implemented
124#endif
125}
126
127void thread_yield()
128{
129#if defined(CONF_FAMILY_UNIX)
130 dbg_assert(sched_yield() == 0, "sched_yield failure");
131#elif defined(CONF_FAMILY_WINDOWS)
132 Sleep(0);
133#else
134#error not implemented
135#endif
136}
137
138void thread_detach(void *thread)
139{
140#if defined(CONF_FAMILY_UNIX)
141 dbg_assert(pthread_detach((pthread_t)thread) == 0, "pthread_detach failure");
142#elif defined(CONF_FAMILY_WINDOWS)
143 dbg_assert(CloseHandle(thread), "CloseHandle failure");
144#else
145#error not implemented
146#endif
147}
148
149void thread_init_and_detach(void (*threadfunc)(void *), void *u, const char *name)
150{
151 void *thread = thread_init(threadfunc, u, name);
152 thread_detach(thread);
153}
154