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_FAMILY_UNIX)
104 dbg_assert(pthread_join((pthread_t)thread, nullptr) == 0, "pthread_join failure");
105#elif defined(CONF_FAMILY_WINDOWS)
106 dbg_assert(WaitForSingleObject((HANDLE)thread, INFINITE) == WAIT_OBJECT_0, "WaitForSingleObject failure");
107 dbg_assert(CloseHandle(thread), "CloseHandle failure");
108#else
109#error not implemented
110#endif
111}
112
113void thread_yield()
114{
115#if defined(CONF_FAMILY_UNIX)
116 dbg_assert(sched_yield() == 0, "sched_yield failure");
117#elif defined(CONF_FAMILY_WINDOWS)
118 Sleep(0);
119#else
120#error not implemented
121#endif
122}
123
124void thread_detach(void *thread)
125{
126#if defined(CONF_FAMILY_UNIX)
127 dbg_assert(pthread_detach((pthread_t)thread) == 0, "pthread_detach failure");
128#elif defined(CONF_FAMILY_WINDOWS)
129 dbg_assert(CloseHandle(thread), "CloseHandle failure");
130#else
131#error not implemented
132#endif
133}
134
135void thread_init_and_detach(void (*threadfunc)(void *), void *u, const char *name)
136{
137 void *thread = thread_init(threadfunc, u, name);
138 thread_detach(thread);
139}
140