x86-64: hack the ABI of cg_upcall_ipret_copy_variable_to_pointer
[ajla.git] / th_win32.c
blob208b12e20dc33c737f52dca1329676186c8e3b30
1 /*
2 * Copyright (C) 2024 Mikulas Patocka
4 * This file is part of Ajla.
6 * Ajla is free software: you can redistribute it and/or modify it under the
7 * terms of the GNU General Public License as published by the Free Software
8 * Foundation, either version 3 of the License, or (at your option) any later
9 * version.
11 * Ajla is distributed in the hope that it will be useful, but WITHOUT ANY
12 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
13 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License along with
16 * Ajla. If not, see <https://www.gnu.org/licenses/>.
19 #include "ajla.h"
21 #include "obj_reg.h"
22 #include "mem_al.h"
23 #include "os.h"
25 #include "thread.h"
27 #ifdef THREAD_WIN32
29 #if !defined(HAVE__BEGINTHREADEX) || defined(__CYGWIN__)
30 #include <pthread.h>
31 #endif
33 #define WIN32_STACK_SIZE 65536
35 struct win32_thread {
36 struct list wait_entry;
37 HANDLE wakeup;
38 #ifdef HAVE__BEGINTHREADEX
39 HANDLE thread_handle; /* warning: not useable within the thread */
40 #else
41 pthread_t pthread_handle;
42 #endif
43 void (*function)(void *);
44 void *arg;
45 thread_priority_t priority;
48 bool rwmutex_supported;
50 static struct win32_thread thread_1;
52 static tls_decl(struct win32_thread *, current_tcb);
54 unsigned thread_concurrency(void)
56 thread_concurrency_win32_;
57 return 1;
60 static BOOL(WINAPI *fn_TryEnterCriticalSection)(LPCRITICAL_SECTION);
61 static BOOL(WINAPI *fn_InitializeCriticalSectionAndSpinCount)(LPCRITICAL_SECTION, DWORD);
62 static VOID (WINAPI *fn_InitializeSRWLock)(void *);
63 static VOID (WINAPI *fn_AcquireSRWLockShared)(void *);
64 static VOID (WINAPI *fn_ReleaseSRWLockShared)(void *);
65 static VOID (WINAPI *fn_AcquireSRWLockExclusive)(void *);
66 static VOID (WINAPI *fn_ReleaseSRWLockExclusive)(void *);
69 #define do_mutex_init(m) \
70 do { \
71 BOOL r; \
72 if (likely(fn_InitializeCriticalSectionAndSpinCount != NULL)) { \
73 r = fn_InitializeCriticalSectionAndSpinCount(m, 200); \
74 if (unlikely(!r)) \
75 InitializeCriticalSection(m); \
76 } else { \
77 InitializeCriticalSection(m); \
78 } \
79 } while (0)
81 #define do_mutex_done(m) \
82 do { \
83 DeleteCriticalSection(m); \
84 } while (0)
86 #define do_mutex_lock(m) \
87 do { \
88 EnterCriticalSection(m); \
89 } while (0)
91 #define do_mutex_trylock(m) \
92 do { \
93 if (fn_TryEnterCriticalSection) \
94 return !!fn_TryEnterCriticalSection(m); \
95 return false; \
96 } while (0)
98 #define do_mutex_unlock(m) \
99 do { \
100 LeaveCriticalSection(m); \
101 } while (0)
104 #define do_rwmutex_init(m) \
105 do { \
106 if (unlikely(!rwmutex_supported)) \
107 InitializeCriticalSection(m); \
108 else \
109 fn_InitializeSRWLock(m); \
110 } while (0)
112 #define do_rwmutex_done(m) \
113 do { \
114 if (unlikely(!rwmutex_supported)) \
115 DeleteCriticalSection(m); \
116 } while (0)
118 #define do_rwmutex_lock_read(m) \
119 do { \
120 if (unlikely(!rwmutex_supported)) \
121 EnterCriticalSection(m); \
122 else \
123 fn_AcquireSRWLockShared(m); \
124 } while (0)
126 #define do_rwmutex_unlock_read(m) \
127 do { \
128 if (unlikely(!rwmutex_supported)) \
129 LeaveCriticalSection(m); \
130 else \
131 fn_ReleaseSRWLockShared(m); \
132 } while (0)
134 #define do_rwmutex_lock_write(m) \
135 do { \
136 if (unlikely(!rwmutex_supported)) \
137 EnterCriticalSection(m); \
138 else \
139 fn_AcquireSRWLockExclusive(m); \
140 } while (0)
142 #define do_rwmutex_unlock_write(m) \
143 do { \
144 if (unlikely(!rwmutex_supported)) \
145 LeaveCriticalSection(m); \
146 else \
147 fn_ReleaseSRWLockExclusive(m); \
148 } while (0)
151 #define do_cond_init(c) \
152 do { \
153 mutex_init_position(&(c)->mutex pass_position); \
154 list_init(&(c)->wait_list); \
155 } while (0)
157 #define do_cond_done(c) \
158 do { \
159 mutex_done_position(&(c)->mutex pass_position); \
160 ajla_assert_lo(list_is_empty(&(c)->wait_list), (caller_file_line, "cond_done: wait list is not empty"));\
161 } while (0)
163 #define do_cond_lock(c) \
164 do { \
165 mutex_lock_position(&(c)->mutex pass_position); \
166 } while (0)
168 #define do_cond_unlock(c) \
169 do { \
170 mutex_unlock_position(&(c)->mutex pass_position); \
171 } while (0)
173 #define do_cond_unlock_signal(c) \
174 do { \
175 BOOL r; \
176 struct win32_thread *tcb; \
177 if (unlikely(!list_is_empty(&(c)->wait_list))) { \
178 tcb = get_struct((c)->wait_list.next, struct win32_thread, wait_entry);\
179 list_del(&tcb->wait_entry); \
180 tcb->wait_entry.prev = NULL; \
181 } else { \
182 tcb = NULL; \
184 mutex_unlock_position(&(c)->mutex pass_position); \
185 if (unlikely(tcb != NULL)) { \
186 r = SetEvent(tcb->wakeup); \
187 if (unlikely(!r)) \
188 internal(caller_file_line, "cond_unlock_signal: SetEvent failed: %u", (unsigned)GetLastError());\
190 } while (0)
192 #define do_cond_unlock_broadcast(c) \
193 do { \
194 BOOL r; \
195 struct list list; \
196 struct list *l; \
197 list_take(&list, &(c)->wait_list); \
198 for (l = list.next; l != &list; l = l->next) \
199 l->prev = NULL; \
200 mutex_unlock_position(&(c)->mutex pass_position); \
201 while (list.next != &list) { \
202 struct win32_thread *tcb = get_struct(list.next, struct win32_thread, wait_entry);\
203 list.next = tcb->wait_entry.next; \
204 r = SetEvent(tcb->wakeup); \
205 if (unlikely(!r)) \
206 internal(caller_file_line, "cond_unlock_signal: SetEvent failed: %u", (unsigned)GetLastError());\
208 } while (0)
210 static attr_noreturn attr_cold win32_wait_failed(const char *str, DWORD r argument_position)
212 if (r == WAIT_FAILED)
213 internal(caller_file_line, "win32_cond_wait_failed: %s failed: error %u", str, (unsigned)GetLastError());
214 else
215 internal(caller_file_line, "win32_cond_wait_failed: %s failed: status %u", str, (unsigned)r);
218 static bool win32_cond_wait(cond_t *c, DWORD timeout argument_position)
220 DWORD r;
221 struct win32_thread *tcb;
222 tcb = tls_get(struct win32_thread *, current_tcb);
223 list_add(&(c)->wait_list, &tcb->wait_entry);
224 mutex_unlock_position(&(c)->mutex pass_position);
225 r = WaitForSingleObjectEx(tcb->wakeup, timeout, TRUE);
226 mutex_lock_position(&(c)->mutex pass_position);
227 if (r != WAIT_OBJECT_0) {
228 if (r != WAIT_IO_COMPLETION && unlikely(r != WAIT_TIMEOUT))
229 win32_wait_failed("WaitForSingleObjectEx", r pass_position);
230 if (likely(tcb->wait_entry.prev != NULL)) {
231 list_del(&tcb->wait_entry);
232 return false;
233 } else {
234 r = WaitForSingleObject(tcb->wakeup, INFINITE);
235 if (unlikely(r != WAIT_OBJECT_0))
236 win32_wait_failed("WaitForSingleObject", r pass_position);
239 return true;
242 #define do_cond_wait(c) \
243 do { \
244 win32_cond_wait(c, INFINITE pass_position); \
245 } while (0)
247 #define do_cond_wait_us(c, us) \
248 do { \
249 return win32_cond_wait(c, (us + 999) / 1000 pass_position); \
250 } while (0)
252 static void win32_thread_init(struct win32_thread *tcb argument_position)
254 tcb->wakeup = CreateEventA(NULL, FALSE, FALSE, NULL);
255 if (unlikely(!tcb->wakeup))
256 fatal("CreateEventA failed at %s: %u", caller_file_line, (unsigned)GetLastError());
259 static void win32_thread_done(struct win32_thread *tcb argument_position)
261 if (unlikely(!CloseHandle(tcb->wakeup)))
262 internal(caller_file_line, "win32_thread_done: CloseHandle failed: %u", (unsigned)GetLastError());
265 static inline void win32_thread_common(struct win32_thread *tcb)
267 if (tcb->priority == PRIORITY_TIMER) {
268 if (unlikely(!SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST)))
269 warning("SetThreadPriority(THREAD_PRIORITY_HIGHEST) failed: %u", (unsigned)GetLastError());
271 tls_set(struct win32_thread *, current_tcb, tcb);
272 asm_setup_thread();
273 tcb->function(tcb->arg);
274 tls_destructor_call();
277 #ifdef HAVE__BEGINTHREADEX
279 static unsigned __stdcall win32_thread_function(void *tcb_)
281 struct win32_thread *tcb = cast_cpp(struct win32_thread *, tcb_);
282 win32_thread_common(tcb);
283 return 0;
286 #define do_thread_spawn(t, function, arg, priority, err) \
287 do { \
288 struct win32_thread *tcb; \
289 unsigned thrd; \
290 tcb = mem_alloc_mayfail(struct win32_thread *, sizeof(struct win32_thread), err);\
291 if (unlikely(!tcb)) \
292 return false; \
293 win32_thread_init(tcb pass_position); \
294 tcb->function = function; \
295 tcb->arg = arg; \
296 tcb->priority = priority; \
297 tcb->thread_handle = (HANDLE)_beginthreadex(NULL, WIN32_STACK_SIZE, win32_thread_function, tcb, 0, &thrd);\
298 if (unlikely(!tcb->thread_handle)) { \
299 int er = errno; \
300 ajla_error_t e = error_from_errno(EC_SYSCALL, er); \
301 fatal_mayfail(e, err, "_beginthreadex failed at %s: %u, %d, %s", position_string(position_arg), GetLastError(), er, error_decode(e));\
302 mem_free(tcb); \
303 return false; \
305 *(t) = tcb; \
306 } while (0)
308 #define do_thread_join(t) \
309 do { \
310 DWORD r; \
311 struct win32_thread *tcb = *(t); \
312 r = WaitForSingleObject(tcb->thread_handle, INFINITE); \
313 if (unlikely(r != WAIT_OBJECT_0)) \
314 win32_wait_failed("WaitForSingleObject", r pass_position);\
315 win32_thread_done(tcb pass_position); \
316 if (unlikely(!CloseHandle(tcb->thread_handle))) \
317 internal(caller_file_line, "thread_join: CloseHandle failed: %u", (unsigned)GetLastError());\
318 mem_free(tcb); \
319 } while (0)
321 #else
323 static void *pthread_thread_function(void *tcb_)
325 struct win32_thread *tcb = cast_cpp(struct win32_thread *, tcb_);
326 win32_thread_common(tcb);
327 return NULL;
330 #define do_thread_spawn(t, function, arg, priority, err) \
331 do { \
332 int r; \
333 struct win32_thread *tcb; \
334 tcb = mem_alloc_mayfail(struct win32_thread *, sizeof(struct win32_thread), err);\
335 if (unlikely(!tcb)) \
336 return false; \
337 win32_thread_init(tcb pass_position); \
338 tcb->function = function; \
339 tcb->arg = arg; \
340 tcb->priority = priority; \
341 r = pthread_create(&tcb->pthread_handle, NULL, pthread_thread_function, tcb);\
342 if (unlikely(r)) { \
343 ajla_error_t e = error_from_errno(EC_SYSCALL, r); \
344 fatal_mayfail(e, err, "pthread_create failed at %s: %d, %s", position_string(position_arg), r, error_decode(e));\
345 mem_free(tcb); \
346 return false; \
348 *(t) = tcb; \
349 } while (0)
351 #define do_thread_join(t) \
352 do { \
353 int r; \
354 struct win32_thread *tcb = *(t); \
355 r = pthread_join(tcb->pthread_handle, NULL); \
356 if (unlikely(r)) \
357 internal(caller_file_line, "thread_join: pthread_join failed: %d, %s", r, error_decode(error_from_errno(EC_SYSCALL, r)));\
358 win32_thread_done(tcb pass_position); \
359 mem_free(tcb); \
360 } while (0)
362 #endif
364 #ifndef HAVE___THREAD
366 #define do_tls_init(tl) \
367 do { \
368 DWORD r = TlsAlloc(); \
369 if (unlikely(!r)) \
370 fatal("TlsAlloc failed at %s: %u", caller_file_line, (unsigned)GetLastError());\
371 *(tl) = r; \
372 } while (0)
374 #define do_tls_done(tl) \
375 do { \
376 if (unlikely(!TlsFree(*(tl)))) \
377 internal(caller_file_line, "TlsFree failed: %u", (unsigned)GetLastError());\
378 } while (0)
380 #define do_tls_get(tl, ret) \
381 do { \
382 void *r = TlsGetValue(*(tl)); \
383 if (unlikely(!r)) { \
384 DWORD le = GetLastError(); \
385 if (unlikely(le != 0)) \
386 internal(caller_file_line, "TlsGetValue(%u) failed: %u", (unsigned)*(tl), (unsigned)GetLastError());\
388 *(ret) = ptr_to_num(r); \
389 } while (0)
391 #define do_tls_set(tl, val) \
392 do { \
393 if (unlikely(!TlsSetValue(*(tl), (void *)(val)))) \
394 internal(caller_file_line, "TlsSetValue(%u) failed: %u", (unsigned)*(tl), (unsigned)GetLastError());\
395 } while (0)
397 #endif
400 #include "th_com.inc"
403 #include "th_sig.inc"
406 void thread_init(void)
408 fn_TryEnterCriticalSection = (void *)GetProcAddress(GetModuleHandleA("kernel32.dll"), "TryEnterCriticalSection");
409 fn_InitializeCriticalSectionAndSpinCount = (void *)GetProcAddress(GetModuleHandleA("kernel32.dll"), "InitializeCriticalSectionAndSpinCount");
410 fn_InitializeSRWLock = (void *)GetProcAddress(GetModuleHandleA("kernel32.dll"), "InitializeSRWLock");
411 fn_AcquireSRWLockShared = (void *)GetProcAddress(GetModuleHandleA("kernel32.dll"), "AcquireSRWLockShared");
412 fn_ReleaseSRWLockShared = (void *)GetProcAddress(GetModuleHandleA("kernel32.dll"), "ReleaseSRWLockShared");
413 fn_AcquireSRWLockExclusive = (void *)GetProcAddress(GetModuleHandleA("kernel32.dll"), "AcquireSRWLockExclusive");
414 fn_ReleaseSRWLockExclusive = (void *)GetProcAddress(GetModuleHandleA("kernel32.dll"), "ReleaseSRWLockExclusive");
415 rwmutex_supported = fn_InitializeSRWLock && fn_AcquireSRWLockShared && fn_ReleaseSRWLockShared && fn_AcquireSRWLockExclusive && fn_ReleaseSRWLockExclusive;
417 thread_signal_init();
418 win32_thread_init(&thread_1 pass_file_line);
419 tls_init(struct win32_thread *, current_tcb);
420 tls_set(struct win32_thread *, current_tcb, &thread_1);
421 thread_common_init();
424 void thread_done(void)
426 thread_common_done();
427 win32_thread_done(&thread_1 pass_file_line);
428 tls_done(struct win32_thread *, current_tcb);
429 thread_signal_done();
432 #endif