diff options
author | sanine <sanine.not@pm.me> | 2022-08-27 23:52:56 -0500 |
---|---|---|
committer | sanine <sanine.not@pm.me> | 2022-08-27 23:52:56 -0500 |
commit | a4dd0ad63c00f4dee3b86dfd3075d1d61b2b3180 (patch) | |
tree | 13bd5bfa15e6fea2a12f176bae79adf9c6fd0933 /3rdparty/plibsys/src/puthread-win.c | |
parent | bde3e4f1bb7b8f8abca0884a7d994ee1c17a66b1 (diff) |
add plibsys
Diffstat (limited to '3rdparty/plibsys/src/puthread-win.c')
-rw-r--r-- | 3rdparty/plibsys/src/puthread-win.c | 511 |
1 files changed, 511 insertions, 0 deletions
diff --git a/3rdparty/plibsys/src/puthread-win.c b/3rdparty/plibsys/src/puthread-win.c new file mode 100644 index 0000000..57604fd --- /dev/null +++ b/3rdparty/plibsys/src/puthread-win.c @@ -0,0 +1,511 @@ +/* + * The MIT License + * + * Copyright (C) 2010-2019 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "pmem.h" +#include "pmutex.h" +#include "patomic.h" +#include "puthread.h" +#include "puthread-private.h" + +#include <stdlib.h> +#include <time.h> +#include <string.h> + +#include <process.h> + +typedef HRESULT (WINAPI * PWin32SetThreadDescription) (HANDLE hThread, PCWSTR lpThreadDescription); +typedef HANDLE puthread_hdl; + +struct PUThread_ { + PUThreadBase base; + puthread_hdl hdl; + PUThreadFunc proxy; +}; + +struct PUThreadKey_ { + DWORD key_idx; + PDestroyFunc free_func; +}; + +typedef struct PUThreadDestructor_ PUThreadDestructor; + +struct PUThreadDestructor_ { + DWORD key_idx; + PDestroyFunc free_func; + PUThreadDestructor *next; +}; + +/* + * For thread names: + * https://docs.microsoft.com/en-us/visualstudio/debugger/how-to-set-a-thread-name-in-native-code + */ + +const DWORD MS_VC_THREAD_NAME_EXCEPTION = 0x406D1388; + +#pragma pack(push, 8) +typedef struct tagTHREADNAME_INFO +{ + DWORD dwType; /* Must be 0x1000. */ + LPCSTR szName; /* Pointer to name (in user addr space). */ + DWORD dwThreadID; /* Thread ID (-1 = caller thread). */ + DWORD dwFlags; /* Reserved for future use, must be zero. */ +} THREADNAME_INFO; +#pragma pack(pop) + +#ifndef P_CC_MSVC +static void *pp_uthread_name_veh_handle = NULL; + +static LONG __stdcall +pp_uthread_set_thread_name_veh (PEXCEPTION_POINTERS except_info) +{ + if (except_info->ExceptionRecord != NULL && + except_info->ExceptionRecord->ExceptionCode == MS_VC_THREAD_NAME_EXCEPTION) + return EXCEPTION_CONTINUE_EXECUTION; + + return EXCEPTION_CONTINUE_SEARCH; +} +#endif + +/* Rest of definitions */ + +static PWin32SetThreadDescription pp_uthread_set_descr_func = NULL; +static PUThreadDestructor * volatile pp_uthread_tls_destructors = NULL; +static PMutex *pp_uthread_tls_mutex = NULL; + +static DWORD pp_uthread_get_tls_key (PUThreadKey *key); +static puint __stdcall pp_uthread_win32_proxy (ppointer data); + +static DWORD +pp_uthread_get_tls_key (PUThreadKey *key) +{ + DWORD tls_key = key->key_idx; + + if (P_LIKELY (tls_key != TLS_OUT_OF_INDEXES)) + return tls_key; + + p_mutex_lock (pp_uthread_tls_mutex); + + tls_key = key->key_idx; + + if (P_LIKELY (tls_key == TLS_OUT_OF_INDEXES)) { + PUThreadDestructor *destr = NULL; + + tls_key = TlsAlloc (); + + if (P_UNLIKELY (tls_key == TLS_OUT_OF_INDEXES)) { + P_ERROR ("PUThread::pp_uthread_get_tls_key: TlsAlloc() failed"); + p_mutex_unlock (pp_uthread_tls_mutex); + return TLS_OUT_OF_INDEXES; + } + + if (key->free_func != NULL) { + if (P_UNLIKELY ((destr = p_malloc0 (sizeof (PUThreadDestructor))) == NULL)) { + P_ERROR ("PUThread::pp_uthread_get_tls_key: failed to allocate memory"); + + if (P_UNLIKELY (TlsFree (tls_key) == 0)) + P_ERROR ("PUThread::pp_uthread_get_tls_key: TlsFree() failed(1)"); + + p_mutex_unlock (pp_uthread_tls_mutex); + return TLS_OUT_OF_INDEXES; + } + + destr->key_idx = tls_key; + destr->free_func = key->free_func; + destr->next = pp_uthread_tls_destructors; + + /* At the same time thread exit could be performed at there is no + * lock for the global destructor list */ + if (P_UNLIKELY (p_atomic_pointer_compare_and_exchange ((PVOID volatile *) &pp_uthread_tls_destructors, + (PVOID) destr->next, + (PVOID) destr) == FALSE)) { + P_ERROR ("PUThread::pp_uthread_get_tls_key: p_atomic_pointer_compare_and_exchange() failed"); + + if (P_UNLIKELY (TlsFree (tls_key) == 0)) + P_ERROR ("PUThread::pp_uthread_get_tls_key: TlsFree() failed(2)"); + + p_free (destr); + + p_mutex_unlock (pp_uthread_tls_mutex); + return TLS_OUT_OF_INDEXES; + } + } + + key->key_idx = tls_key; + } + + p_mutex_unlock (pp_uthread_tls_mutex); + + return tls_key; +} + +static puint __stdcall +pp_uthread_win32_proxy (ppointer data) +{ + PUThread *thread = data; + + thread->proxy (thread); + + _endthreadex (0); + + return 0; +} + +void +p_uthread_win32_thread_detach (void) +{ + pboolean was_called; + + do { + PUThreadDestructor *destr; + + was_called = FALSE; + + destr = (PUThreadDestructor *) p_atomic_pointer_get ((const PVOID volatile *) &pp_uthread_tls_destructors); + + while (destr != NULL) { + ppointer value; + + value = TlsGetValue (destr->key_idx); + + if (value != NULL && destr->free_func != NULL) { + TlsSetValue (destr->key_idx, NULL); + destr->free_func (value); + was_called = TRUE; + } + + destr = destr->next; + } + } while (was_called); +} + +void +p_uthread_init_internal (void) +{ + HMODULE hmodule; + + if (P_LIKELY (pp_uthread_tls_mutex == NULL)) + pp_uthread_tls_mutex = p_mutex_new (); + + hmodule = GetModuleHandleA ("kernel32.dll"); + + if (P_UNLIKELY (hmodule == NULL)) { + P_ERROR ("PUThread::p_uthread_init_internal: failed to load kernel32.dll module"); + return; + } + + pp_uthread_set_descr_func = (PWin32SetThreadDescription) GetProcAddress (hmodule, "SetThreadDescription"); + +#ifndef P_CC_MSVC + pp_uthread_name_veh_handle = AddVectoredExceptionHandler (1, &pp_uthread_set_thread_name_veh); +#endif +} + +void +p_uthread_shutdown_internal (void) +{ + PUThreadDestructor *destr; + + p_uthread_win32_thread_detach (); + + destr = pp_uthread_tls_destructors; + + while (destr != NULL) { + PUThreadDestructor *next_destr = destr->next; + + TlsFree (destr->key_idx); + p_free (destr); + + destr = next_destr; + } + + pp_uthread_tls_destructors = NULL; + + if (P_LIKELY (pp_uthread_tls_mutex != NULL)) { + p_mutex_free (pp_uthread_tls_mutex); + pp_uthread_tls_mutex = NULL; + } + + pp_uthread_set_descr_func = NULL; + +#ifndef P_CC_MSVC + if (pp_uthread_name_veh_handle != NULL) { + RemoveVectoredExceptionHandler (pp_uthread_name_veh_handle); + pp_uthread_name_veh_handle = NULL; + } +#endif +} + +PUThread * +p_uthread_create_internal (PUThreadFunc func, + pboolean joinable, + PUThreadPriority prio, + psize stack_size) +{ + PUThread *ret; + + if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PUThread))) == NULL)) { + P_ERROR ("PUThread::p_uthread_create_internal: failed to allocate memory"); + return NULL; + } + + ret->proxy = func; + + if (P_UNLIKELY ((ret->hdl = (HANDLE) _beginthreadex (NULL, + (puint) stack_size, + pp_uthread_win32_proxy, + ret, + CREATE_SUSPENDED, + NULL)) == NULL)) { + P_ERROR ("PUThread::p_uthread_create_internal: _beginthreadex() failed"); + p_free (ret); + return NULL; + } + + ret->base.joinable = joinable; + + p_uthread_set_priority (ret, prio); + + if (P_UNLIKELY (ResumeThread (ret->hdl) == (DWORD) -1)) { + P_ERROR ("PUThread::p_uthread_create_internal: ResumeThread() failed"); + CloseHandle (ret->hdl); + p_free (ret); + } + + return ret; +} + +void +p_uthread_exit_internal (void) +{ + _endthreadex (0); +} + +void +p_uthread_wait_internal (PUThread *thread) +{ + if (P_UNLIKELY ((WaitForSingleObject (thread->hdl, INFINITE)) != WAIT_OBJECT_0)) + P_ERROR ("PUThread::p_uthread_wait_internal: WaitForSingleObject() failed"); +} + +void +p_uthread_set_name_internal (PUThread *thread) +{ + wchar_t *thr_wname = NULL; + psize namelen = 0; + HRESULT hres; + THREADNAME_INFO thr_info; + + if (pp_uthread_set_descr_func != NULL) { + namelen = strlen (thread->base.name); + + if (P_UNLIKELY ((thr_wname = p_malloc0 (sizeof (wchar_t) * (namelen + 1))) == NULL)) { + P_ERROR ("PUThread::p_uthread_set_name_internal: failed to allocate memory"); + return; + } + + mbstowcs (thr_wname, thread->base.name, namelen + 1); + + hres = pp_uthread_set_descr_func (thread->hdl, thr_wname); + + p_free (thr_wname); + + if (P_UNLIKELY (FAILED (hres))) { + P_ERROR ("PUThread::p_uthread_set_name_internal: failed to set thread description"); + return; + } + } + + if (!IsDebuggerPresent ()) + return; + + thr_info.dwType = 0x1000; + thr_info.szName = thread->base.name; + thr_info.dwThreadID = -1; + thr_info.dwFlags = 0; + +#ifdef P_CC_MSVC +# pragma warning(push) +# pragma warning(disable: 6320 6322) + __try { + RaiseException (MS_VC_THREAD_NAME_EXCEPTION, + 0, + sizeof (thr_info) / sizeof (ULONG_PTR), + (ULONG_PTR *) &thr_info); + } + __except (EXCEPTION_EXECUTE_HANDLER) {} +# pragma warning(pop) +#else + if (pp_uthread_name_veh_handle != NULL) + RaiseException (MS_VC_THREAD_NAME_EXCEPTION, + 0, + sizeof (thr_info) / sizeof (ULONG_PTR), + (ULONG_PTR *) &thr_info); +#endif +} + +void +p_uthread_free_internal (PUThread *thread) +{ + CloseHandle (thread->hdl); + p_free (thread); +} + +P_LIB_API void +p_uthread_yield (void) +{ + Sleep (0); +} + +P_LIB_API pboolean +p_uthread_set_priority (PUThread *thread, + PUThreadPriority prio) +{ + pint native_prio; + + if (P_UNLIKELY (thread == NULL)) + return FALSE; + + switch (prio) { + case P_UTHREAD_PRIORITY_IDLE: + native_prio = THREAD_PRIORITY_IDLE; + break; + case P_UTHREAD_PRIORITY_LOWEST: + native_prio = THREAD_PRIORITY_LOWEST; + break; + case P_UTHREAD_PRIORITY_LOW: + native_prio = THREAD_PRIORITY_BELOW_NORMAL; + break; + case P_UTHREAD_PRIORITY_NORMAL: + native_prio = THREAD_PRIORITY_NORMAL; + break; + case P_UTHREAD_PRIORITY_HIGH: + native_prio = THREAD_PRIORITY_ABOVE_NORMAL; + break; + case P_UTHREAD_PRIORITY_HIGHEST: + native_prio = THREAD_PRIORITY_HIGHEST; + break; + case P_UTHREAD_PRIORITY_TIMECRITICAL: + native_prio = THREAD_PRIORITY_TIME_CRITICAL; + break; + case P_UTHREAD_PRIORITY_INHERIT: + default: + native_prio = GetThreadPriority (GetCurrentThread ()); + break; + } + + if (P_UNLIKELY (SetThreadPriority (thread->hdl, native_prio) == 0)) { + P_ERROR ("PUThread::p_uthread_set_priority: SetThreadPriority() failed"); + return FALSE; + } + + thread->base.prio = prio; + + return TRUE; +} + +P_LIB_API P_HANDLE +p_uthread_current_id (void) +{ + return (P_HANDLE) ((psize) GetCurrentThreadId ()); +} + +P_LIB_API PUThreadKey * +p_uthread_local_new (PDestroyFunc free_func) +{ + PUThreadKey *ret; + + if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PUThreadKey))) == NULL)) { + P_ERROR ("PUThread::p_uthread_local_new: failed to allocate memory"); + return NULL; + } + + ret->key_idx = TLS_OUT_OF_INDEXES; + ret->free_func = free_func; + + return ret; +} + +P_LIB_API void +p_uthread_local_free (PUThreadKey *key) +{ + if (P_UNLIKELY (key == NULL)) + return; + + p_free (key); +} + +P_LIB_API ppointer +p_uthread_get_local (PUThreadKey *key) +{ + DWORD tls_idx; + + if (P_UNLIKELY (key == NULL)) + return NULL; + + tls_idx = pp_uthread_get_tls_key (key); + + return tls_idx == TLS_OUT_OF_INDEXES ? NULL : TlsGetValue (tls_idx); +} + +P_LIB_API void +p_uthread_set_local (PUThreadKey *key, + ppointer value) +{ + DWORD tls_idx; + + if (P_UNLIKELY (key == NULL)) + return; + + tls_idx = pp_uthread_get_tls_key (key); + + if (P_LIKELY (tls_idx != TLS_OUT_OF_INDEXES)) { + if (P_UNLIKELY (TlsSetValue (tls_idx, value) == 0)) + P_ERROR ("PUThread::p_uthread_set_local: TlsSetValue() failed"); + } +} + +P_LIB_API void +p_uthread_replace_local (PUThreadKey *key, + ppointer value) +{ + DWORD tls_idx; + ppointer old_value; + + if (P_UNLIKELY (key == NULL)) + return; + + tls_idx = pp_uthread_get_tls_key (key); + + if (P_UNLIKELY (tls_idx == TLS_OUT_OF_INDEXES)) + return; + + old_value = TlsGetValue (tls_idx); + + if (old_value != NULL && key->free_func != NULL) + key->free_func (old_value); + + if (P_UNLIKELY (TlsSetValue (tls_idx, value) == 0)) + P_ERROR ("PUThread::p_uthread_replace_local: TlsSetValue() failed"); +} |