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
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/>.
29 #if !defined(HAVE__BEGINTHREADEX) || defined(__CYGWIN__)
33 #define WIN32_STACK_SIZE 65536
36 struct list wait_entry
;
38 #ifdef HAVE__BEGINTHREADEX
39 HANDLE thread_handle
; /* warning: not useable within the thread */
41 pthread_t pthread_handle
;
43 void (*function
)(void *);
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_
;
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) \
72 if (likely(fn_InitializeCriticalSectionAndSpinCount != NULL)) { \
73 r = fn_InitializeCriticalSectionAndSpinCount(m, 200); \
75 InitializeCriticalSection(m); \
77 InitializeCriticalSection(m); \
81 #define do_mutex_done(m) \
83 DeleteCriticalSection(m); \
86 #define do_mutex_lock(m) \
88 EnterCriticalSection(m); \
91 #define do_mutex_trylock(m) \
93 if (fn_TryEnterCriticalSection) \
94 return !!fn_TryEnterCriticalSection(m); \
98 #define do_mutex_unlock(m) \
100 LeaveCriticalSection(m); \
104 #define do_rwmutex_init(m) \
106 if (unlikely(!rwmutex_supported)) \
107 InitializeCriticalSection(m); \
109 fn_InitializeSRWLock(m); \
112 #define do_rwmutex_done(m) \
114 if (unlikely(!rwmutex_supported)) \
115 DeleteCriticalSection(m); \
118 #define do_rwmutex_lock_read(m) \
120 if (unlikely(!rwmutex_supported)) \
121 EnterCriticalSection(m); \
123 fn_AcquireSRWLockShared(m); \
126 #define do_rwmutex_unlock_read(m) \
128 if (unlikely(!rwmutex_supported)) \
129 LeaveCriticalSection(m); \
131 fn_ReleaseSRWLockShared(m); \
134 #define do_rwmutex_lock_write(m) \
136 if (unlikely(!rwmutex_supported)) \
137 EnterCriticalSection(m); \
139 fn_AcquireSRWLockExclusive(m); \
142 #define do_rwmutex_unlock_write(m) \
144 if (unlikely(!rwmutex_supported)) \
145 LeaveCriticalSection(m); \
147 fn_ReleaseSRWLockExclusive(m); \
151 #define do_cond_init(c) \
153 mutex_init_position(&(c)->mutex pass_position); \
154 list_init(&(c)->wait_list); \
157 #define do_cond_done(c) \
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"));\
163 #define do_cond_lock(c) \
165 mutex_lock_position(&(c)->mutex pass_position); \
168 #define do_cond_unlock(c) \
170 mutex_unlock_position(&(c)->mutex pass_position); \
173 #define do_cond_unlock_signal(c) \
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; \
184 mutex_unlock_position(&(c)->mutex pass_position); \
185 if (unlikely(tcb != NULL)) { \
186 r = SetEvent(tcb->wakeup); \
188 internal(caller_file_line, "cond_unlock_signal: SetEvent failed: %u", (unsigned)GetLastError());\
192 #define do_cond_unlock_broadcast(c) \
197 list_take(&list, &(c)->wait_list); \
198 for (l = list.next; l != &list; l = l->next) \
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); \
206 internal(caller_file_line, "cond_unlock_signal: SetEvent failed: %u", (unsigned)GetLastError());\
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());
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
)
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
);
234 r
= WaitForSingleObject(tcb
->wakeup
, INFINITE
);
235 if (unlikely(r
!= WAIT_OBJECT_0
))
236 win32_wait_failed("WaitForSingleObject", r pass_position
);
242 #define do_cond_wait(c) \
244 win32_cond_wait(c, INFINITE pass_position); \
247 #define do_cond_wait_us(c, us) \
249 return win32_cond_wait(c, (us + 999) / 1000 pass_position); \
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
);
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
);
286 #define do_thread_spawn(t, function, arg, priority, err) \
288 struct win32_thread *tcb; \
290 tcb = mem_alloc_mayfail(struct win32_thread *, sizeof(struct win32_thread), err);\
291 if (unlikely(!tcb)) \
293 win32_thread_init(tcb pass_position); \
294 tcb->function = function; \
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)) { \
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));\
308 #define do_thread_join(t) \
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());\
323 static void *pthread_thread_function(void *tcb_
)
325 struct win32_thread
*tcb
= cast_cpp(struct win32_thread
*, tcb_
);
326 win32_thread_common(tcb
);
330 #define do_thread_spawn(t, function, arg, priority, err) \
333 struct win32_thread *tcb; \
334 tcb = mem_alloc_mayfail(struct win32_thread *, sizeof(struct win32_thread), err);\
335 if (unlikely(!tcb)) \
337 win32_thread_init(tcb pass_position); \
338 tcb->function = function; \
340 tcb->priority = priority; \
341 r = pthread_create(&tcb->pthread_handle, NULL, pthread_thread_function, tcb);\
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));\
351 #define do_thread_join(t) \
354 struct win32_thread *tcb = *(t); \
355 r = pthread_join(tcb->pthread_handle, NULL); \
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); \
364 #ifndef HAVE___THREAD
366 #define do_tls_init(tl) \
368 DWORD r = TlsAlloc(); \
370 fatal("TlsAlloc failed at %s: %u", caller_file_line, (unsigned)GetLastError());\
374 #define do_tls_done(tl) \
376 if (unlikely(!TlsFree(*(tl)))) \
377 internal(caller_file_line, "TlsFree failed: %u", (unsigned)GetLastError());\
380 #define do_tls_get(tl, ret) \
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); \
391 #define do_tls_set(tl, val) \
393 if (unlikely(!TlsSetValue(*(tl), (void *)(val)))) \
394 internal(caller_file_line, "TlsSetValue(%u) failed: %u", (unsigned)*(tl), (unsigned)GetLastError());\
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();