summaryrefslogtreecommitdiff
path: root/3rdparty/plibsys/src/puthread-win.c
diff options
context:
space:
mode:
Diffstat (limited to '3rdparty/plibsys/src/puthread-win.c')
-rw-r--r--3rdparty/plibsys/src/puthread-win.c511
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");
+}