aboutsummaryrefslogtreecommitdiff
path: root/vendor/zgui/libs/winpthreads/src/thread.c
diff options
context:
space:
mode:
authorNic Gaffney <gaffney_nic@protonmail.com>2024-06-12 21:15:52 -0500
committerNic Gaffney <gaffney_nic@protonmail.com>2024-06-12 21:15:52 -0500
commit963fae202108acd0498349e872e4811fa6c6aba0 (patch)
tree1a7d5b6ee837700819d8f6f5a2484342a0ab6ec1 /vendor/zgui/libs/winpthreads/src/thread.c
parent6084001df845815efd9c0eb712acf4fd9311ce36 (diff)
downloadparticle-sim-963fae202108acd0498349e872e4811fa6c6aba0.tar.gz
Added imgui for configuration
Diffstat (limited to 'vendor/zgui/libs/winpthreads/src/thread.c')
-rw-r--r--vendor/zgui/libs/winpthreads/src/thread.c1914
1 files changed, 1914 insertions, 0 deletions
diff --git a/vendor/zgui/libs/winpthreads/src/thread.c b/vendor/zgui/libs/winpthreads/src/thread.c
new file mode 100644
index 0000000..36ee665
--- /dev/null
+++ b/vendor/zgui/libs/winpthreads/src/thread.c
@@ -0,0 +1,1914 @@
+/*
+ Copyright (c) 2011-2016 mingw-w64 project
+
+ 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 <windows.h>
+#include <strsafe.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <malloc.h>
+#include <signal.h>
+#include "pthread.h"
+#include "thread.h"
+#include "misc.h"
+#include "winpthread_internal.h"
+
+static _pthread_v *__pthread_self_lite (void);
+
+void (**_pthread_key_dest)(void *) = NULL;
+
+static volatile long _pthread_cancelling;
+static int _pthread_concur;
+
+/* FIXME Will default to zero as needed */
+static pthread_once_t _pthread_tls_once;
+static DWORD _pthread_tls = 0xffffffff;
+
+static pthread_rwlock_t _pthread_key_lock = PTHREAD_RWLOCK_INITIALIZER;
+static unsigned long _pthread_key_max=0L;
+static unsigned long _pthread_key_sch=0L;
+
+static _pthread_v *pthr_root = NULL, *pthr_last = NULL;
+static pthread_mutex_t mtx_pthr_locked = PTHREAD_RECURSIVE_MUTEX_INITIALIZER;
+
+static __pthread_idlist *idList = NULL;
+static size_t idListCnt = 0;
+static size_t idListMax = 0;
+static pthread_t idListNextId = 0;
+
+#if !defined(_MSC_VER)
+#define USE_VEH_FOR_MSC_SETTHREADNAME
+#endif
+#if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
+/* forbidden RemoveVectoredExceptionHandler/AddVectoredExceptionHandler APIs */
+#undef USE_VEH_FOR_MSC_SETTHREADNAME
+#endif
+
+#if defined(USE_VEH_FOR_MSC_SETTHREADNAME)
+static void *SetThreadName_VEH_handle = NULL;
+
+static LONG __stdcall
+SetThreadName_VEH (PEXCEPTION_POINTERS ExceptionInfo)
+{
+ if (ExceptionInfo->ExceptionRecord != NULL &&
+ ExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_SET_THREAD_NAME)
+ return EXCEPTION_CONTINUE_EXECUTION;
+
+ return EXCEPTION_CONTINUE_SEARCH;
+}
+
+static PVOID (*AddVectoredExceptionHandlerFuncPtr) (ULONG, PVECTORED_EXCEPTION_HANDLER);
+static ULONG (*RemoveVectoredExceptionHandlerFuncPtr) (PVOID);
+
+static void __attribute__((constructor))
+ctor (void)
+{
+ HMODULE module = GetModuleHandleA("kernel32.dll");
+ if (module) {
+ AddVectoredExceptionHandlerFuncPtr = (__typeof__(AddVectoredExceptionHandlerFuncPtr)) GetProcAddress(module, "AddVectoredExceptionHandler");
+ RemoveVectoredExceptionHandlerFuncPtr = (__typeof__(RemoveVectoredExceptionHandlerFuncPtr)) GetProcAddress(module, "RemoveVectoredExceptionHandler");
+ }
+}
+#endif
+
+typedef struct _THREADNAME_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;
+
+static void
+SetThreadName (DWORD dwThreadID, LPCSTR szThreadName)
+{
+ THREADNAME_INFO info;
+ DWORD infosize;
+
+ info.dwType = 0x1000;
+ info.szName = szThreadName;
+ info.dwThreadID = dwThreadID;
+ info.dwFlags = 0;
+
+ infosize = sizeof (info) / sizeof (ULONG_PTR);
+
+#if defined(_MSC_VER) && !defined (USE_VEH_FOR_MSC_SETTHREADNAME)
+ __try
+ {
+ RaiseException (EXCEPTION_SET_THREAD_NAME, 0, infosize, (ULONG_PTR *)&info);
+ }
+ __except (EXCEPTION_EXECUTE_HANDLER)
+ {
+ }
+#else
+ /* Without a debugger we *must* have an exception handler,
+ * otherwise raising an exception will crash the process.
+ */
+#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
+ if ((!IsDebuggerPresent ()) && (SetThreadName_VEH_handle == NULL))
+#else
+ if (!IsDebuggerPresent ())
+#endif
+ return;
+
+ RaiseException (EXCEPTION_SET_THREAD_NAME, 0, infosize, (ULONG_PTR *) &info);
+#endif
+}
+
+/* Search the list idList for an element with identifier ID. If
+ found, its associated _pthread_v pointer is returned, otherwise
+ NULL.
+ NOTE: This method is not locked. */
+static struct _pthread_v *
+__pthread_get_pointer (pthread_t id)
+{
+ size_t l, r, p;
+ if (!idListCnt)
+ return NULL;
+ if (idListCnt == 1)
+ return (idList[0].id == id ? idList[0].ptr : NULL);
+ l = 0; r = idListCnt - 1;
+ while (l <= r)
+ {
+ p = (l + r) >> 1;
+ if (idList[p].id == id)
+ return idList[p].ptr;
+ else if (idList[p].id > id)
+ {
+ if (p == l)
+ return NULL;
+ r = p - 1;
+ }
+ else
+ {
+ l = p + 1;
+ }
+ }
+
+ return NULL;
+}
+
+static void
+__pth_remove_use_for_key (pthread_key_t key)
+{
+ int i;
+
+ pthread_mutex_lock (&mtx_pthr_locked);
+ for (i = 0; i < idListCnt; i++)
+ {
+ if (idList[i].ptr != NULL
+ && idList[i].ptr->keyval != NULL
+ && key < idList[i].ptr->keymax)
+ {
+ idList[i].ptr->keyval[key] = NULL;
+ idList[i].ptr->keyval_set[key] = 0;
+ }
+ }
+ pthread_mutex_unlock (&mtx_pthr_locked);
+}
+
+/* Search the list idList for an element with identifier ID. If
+ found, its associated _pthread_v pointer is returned, otherwise
+ NULL.
+ NOTE: This method uses lock mtx_pthr_locked. */
+struct _pthread_v *
+__pth_gpointer_locked (pthread_t id)
+{
+ struct _pthread_v *ret;
+ if (!id)
+ return NULL;
+ pthread_mutex_lock (&mtx_pthr_locked);
+ ret = __pthread_get_pointer (id);
+ pthread_mutex_unlock (&mtx_pthr_locked);
+ return ret;
+}
+
+/* Registers in the list idList an element with _pthread_v pointer
+ and creates and unique identifier ID. If successful created the
+ ID of this element is returned, otherwise on failure zero ID gets
+ returned.
+ NOTE: This method is not locked. */
+static pthread_t
+__pthread_register_pointer (struct _pthread_v *ptr)
+{
+ __pthread_idlist *e;
+ size_t i;
+
+ if (!ptr)
+ return 0;
+ /* Check if a resize of list is necessary. */
+ if (idListCnt >= idListMax)
+ {
+ if (!idListCnt)
+ {
+ e = (__pthread_idlist *) malloc (sizeof (__pthread_idlist) * 16);
+ if (!e)
+ return 0;
+ idListMax = 16;
+ idList = e;
+ }
+ else
+ {
+ e = (__pthread_idlist *) realloc (idList, sizeof (__pthread_idlist) * (idListMax + 16));
+ if (!e)
+ return 0;
+ idListMax += 16;
+ idList = e;
+ }
+ }
+ do
+ {
+ ++idListNextId;
+ /* If two MSB are set we reset to id 1. We need to check here bits
+ to avoid gcc's no-overflow issue on increment. Additionally we
+ need to handle different size of pthread_t on 32-bit/64-bit. */
+ if ((idListNextId & ( ((pthread_t) 1) << ((sizeof (pthread_t) * 8) - 2))) != 0)
+ idListNextId = 1;
+ }
+ while (idListNextId == 0 || __pthread_get_pointer (idListNextId));
+ /* We assume insert at end of list. */
+ i = idListCnt;
+ if (i != 0)
+ {
+ /* Find position we can actual insert sorted. */
+ while (i > 0 && idList[i - 1].id > idListNextId)
+ --i;
+ if (i != idListCnt)
+ memmove (&idList[i + 1], &idList[i], sizeof (__pthread_idlist) * (idListCnt - i));
+ }
+ idList[i].id = idListNextId;
+ idList[i].ptr = ptr;
+ ++idListCnt;
+ return idListNextId;
+}
+
+/* Deregisters in the list idList an element with identifier ID and
+ returns its _pthread_v pointer on success. Otherwise NULL is returned.
+ NOTE: This method is not locked. */
+static struct _pthread_v *
+__pthread_deregister_pointer (pthread_t id)
+{
+ size_t l, r, p;
+ if (!idListCnt)
+ return NULL;
+ l = 0; r = idListCnt - 1;
+ while (l <= r)
+ {
+ p = (l + r) >> 1;
+ if (idList[p].id == id)
+ {
+ struct _pthread_v *ret = idList[p].ptr;
+ p++;
+ if (p < idListCnt)
+ memmove (&idList[p - 1], &idList[p], sizeof (__pthread_idlist) * (idListCnt - p));
+ --idListCnt;
+ /* Is this last element in list then free list. */
+ if (idListCnt == 0)
+ {
+ free (idList);
+ idListCnt = idListMax = 0;
+ }
+ return ret;
+ }
+ else if (idList[p].id > id)
+ {
+ if (p == l)
+ return NULL;
+ r = p - 1;
+ }
+ else
+ {
+ l = p + 1;
+ }
+ }
+ return NULL;
+}
+
+/* Save a _pthread_v element for reuse in pool. */
+static void
+push_pthread_mem (_pthread_v *sv)
+{
+ if (!sv || sv->next != NULL)
+ return;
+ pthread_mutex_lock (&mtx_pthr_locked);
+ if (sv->x != 0)
+ __pthread_deregister_pointer (sv->x);
+ if (sv->keyval)
+ free (sv->keyval);
+ if (sv->keyval_set)
+ free (sv->keyval_set);
+ if (sv->thread_name)
+ free (sv->thread_name);
+ memset (sv, 0, sizeof(struct _pthread_v));
+ if (pthr_last == NULL)
+ pthr_root = pthr_last = sv;
+ else
+ {
+ pthr_last->next = sv;
+ pthr_last = sv;
+ }
+ pthread_mutex_unlock (&mtx_pthr_locked);
+}
+
+/* Get a _pthread_v element from pool, or allocate it.
+ Note the unique identifier is created for the element here, too. */
+static _pthread_v *
+pop_pthread_mem (void)
+{
+ _pthread_v *r = NULL;
+
+ pthread_mutex_lock (&mtx_pthr_locked);
+ if ((r = pthr_root) == NULL)
+ {
+ if ((r = (_pthread_v *)calloc (1,sizeof(struct _pthread_v))) != NULL)
+ {
+ r->x = __pthread_register_pointer (r);
+ if (r->x == 0)
+ {
+ free (r);
+ r = NULL;
+ }
+ }
+ pthread_mutex_unlock (&mtx_pthr_locked);
+ return r;
+ }
+ r->x = __pthread_register_pointer (r);
+ if (r->x == 0)
+ r = NULL;
+ else
+ {
+ if((pthr_root = r->next) == NULL)
+ pthr_last = NULL;
+
+ r->next = NULL;
+ }
+ pthread_mutex_unlock (&mtx_pthr_locked);
+ return r;
+}
+
+/* Free memory consumed in _pthread_v pointer pool. */
+static void
+free_pthread_mem (void)
+{
+#if 0
+ _pthread_v *t;
+
+ pthread_mutex_lock (&mtx_pthr_locked);
+ t = pthr_root;
+ while (t != NULL)
+ {
+ _pthread_v *sv = t;
+ t = t->next;
+ if (sv->x != 0 && sv->ended == 0 && sv->valid != DEAD_THREAD)
+ {
+ pthread_mutex_unlock (&mtx_pthr_locked);
+ pthread_cancel (t->x);
+ Sleep (0);
+ pthread_mutex_lock (&mtx_pthr_locked);
+ t = pthr_root;
+ continue;
+ }
+ else if (sv->x != 0 && sv->valid != DEAD_THREAD)
+ {
+ pthread_mutex_unlock (&mtx_pthr_locked);
+ Sleep (0);
+ pthread_mutex_lock (&mtx_pthr_locked);
+ continue;
+ }
+ if (sv->x != 0)
+ __pthread_deregister_pointer (sv->x);
+ sv->x = 0;
+ free (sv);
+ pthr_root = t;
+ }
+ pthread_mutex_unlock (&mtx_pthr_locked);
+#endif
+ return;
+}
+
+static void
+replace_spin_keys (pthread_spinlock_t *old, pthread_spinlock_t new)
+{
+ if (old == NULL)
+ return;
+
+ if (EPERM == pthread_spin_destroy (old))
+ {
+#define THREADERR "Error cleaning up spin_keys for thread %lu.\n"
+ char threaderr[sizeof(THREADERR) + 8] = { 0 };
+ snprintf(threaderr, sizeof(threaderr), THREADERR, GetCurrentThreadId());
+#undef THREADERR
+ OutputDebugStringA (threaderr);
+ abort ();
+ }
+
+ *old = new;
+}
+
+/* Hook for TLS-based deregistration/registration of thread. */
+static void WINAPI
+__dyn_tls_pthread (HANDLE hDllHandle, DWORD dwReason, LPVOID lpreserved)
+{
+ _pthread_v *t = NULL;
+ pthread_spinlock_t new_spin_keys = PTHREAD_SPINLOCK_INITIALIZER;
+
+ if (dwReason == DLL_PROCESS_DETACH)
+ {
+#if defined(USE_VEH_FOR_MSC_SETTHREADNAME)
+ if (lpreserved == NULL && SetThreadName_VEH_handle != NULL)
+ {
+ if (RemoveVectoredExceptionHandlerFuncPtr != NULL)
+ RemoveVectoredExceptionHandlerFuncPtr (SetThreadName_VEH_handle);
+ SetThreadName_VEH_handle = NULL;
+ }
+#endif
+ free_pthread_mem ();
+ }
+ else if (dwReason == DLL_PROCESS_ATTACH)
+ {
+#if defined(USE_VEH_FOR_MSC_SETTHREADNAME)
+ if (AddVectoredExceptionHandlerFuncPtr != NULL)
+ SetThreadName_VEH_handle = AddVectoredExceptionHandlerFuncPtr (1, &SetThreadName_VEH);
+ else
+ SetThreadName_VEH_handle = NULL;
+ /* Can't do anything on error anyway, check for NULL later */
+#endif
+ }
+ else if (dwReason == DLL_THREAD_DETACH)
+ {
+ if (_pthread_tls != 0xffffffff)
+ t = (_pthread_v *)TlsGetValue(_pthread_tls);
+ if (t && t->thread_noposix != 0)
+ {
+ _pthread_cleanup_dest (t->x);
+ if (t->h != NULL)
+ {
+ CloseHandle (t->h);
+ if (t->evStart)
+ CloseHandle (t->evStart);
+ t->evStart = NULL;
+ t->h = NULL;
+ }
+ pthread_mutex_destroy (&t->p_clock);
+ replace_spin_keys (&t->spin_keys, new_spin_keys);
+ push_pthread_mem (t);
+ t = NULL;
+ TlsSetValue (_pthread_tls, t);
+ }
+ else if (t && t->ended == 0)
+ {
+ if (t->evStart)
+ CloseHandle(t->evStart);
+ t->evStart = NULL;
+ t->ended = 1;
+ _pthread_cleanup_dest (t->x);
+ if ((t->p_state & PTHREAD_CREATE_DETACHED) == PTHREAD_CREATE_DETACHED)
+ {
+ t->valid = DEAD_THREAD;
+ if (t->h != NULL)
+ CloseHandle (t->h);
+ t->h = NULL;
+ pthread_mutex_destroy(&t->p_clock);
+ replace_spin_keys (&t->spin_keys, new_spin_keys);
+ push_pthread_mem (t);
+ t = NULL;
+ TlsSetValue (_pthread_tls, t);
+ return;
+ }
+ pthread_mutex_destroy(&t->p_clock);
+ replace_spin_keys (&t->spin_keys, new_spin_keys);
+ }
+ else if (t)
+ {
+ if (t->evStart)
+ CloseHandle (t->evStart);
+ t->evStart = NULL;
+ pthread_mutex_destroy (&t->p_clock);
+ replace_spin_keys (&t->spin_keys, new_spin_keys);
+ }
+ }
+}
+
+/* TLS-runtime section variable. */
+
+#if defined(_MSC_VER)
+/* Force a reference to _tls_used to make the linker create the TLS
+ * directory if it's not already there. (e.g. if __declspec(thread)
+ * is not used).
+ * Force a reference to __xl_f to prevent whole program optimization
+ * from discarding the variable. */
+
+/* On x86, symbols are prefixed with an underscore. */
+# if defined(_M_IX86)
+# pragma comment(linker, "/include:__tls_used")
+# pragma comment(linker, "/include:___xl_f")
+# else
+# pragma comment(linker, "/include:_tls_used")
+# pragma comment(linker, "/include:__xl_f")
+# endif
+
+/* .CRT$XLA to .CRT$XLZ is an array of PIMAGE_TLS_CALLBACK
+ * pointers. Pick an arbitrary location for our callback.
+ *
+ * See VC\...\crt\src\vcruntime\tlssup.cpp for reference. */
+
+# pragma section(".CRT$XLF", long, read)
+#endif
+
+WINPTHREADS_ATTRIBUTE((WINPTHREADS_SECTION(".CRT$XLF")))
+extern const PIMAGE_TLS_CALLBACK __xl_f;
+const PIMAGE_TLS_CALLBACK __xl_f = __dyn_tls_pthread;
+
+
+#ifdef WINPTHREAD_DBG
+static int print_state = 0;
+void thread_print_set (int state)
+{
+ print_state = state;
+}
+
+void
+thread_print (volatile pthread_t t, char *txt)
+{
+ if (!print_state)
+ return;
+ if (!t)
+ printf("T%p %lu %s\n",NULL,GetCurrentThreadId(),txt);
+ else
+ {
+ printf("T%p %lu V=%0X H=%p %s\n",
+ (void *) __pth_gpointer_locked (t),
+ GetCurrentThreadId(),
+ (__pth_gpointer_locked (t))->valid,
+ (__pth_gpointer_locked (t))->h,
+ txt
+ );
+ }
+}
+#endif
+
+/* Internal collect-once structure. */
+typedef struct collect_once_t {
+ pthread_once_t *o;
+ pthread_mutex_t m;
+ int count;
+ struct collect_once_t *next;
+} collect_once_t;
+
+static collect_once_t *once_obj = NULL;
+
+static pthread_spinlock_t once_global = PTHREAD_SPINLOCK_INITIALIZER;
+
+static collect_once_t *
+enterOnceObject (pthread_once_t *o)
+{
+ collect_once_t *c, *p = NULL;
+ pthread_spin_lock (&once_global);
+ c = once_obj;
+ while (c != NULL && c->o != o)
+ {
+ c = (p = c)->next;
+ }
+ if (!c)
+ {
+ c = (collect_once_t *) calloc(1,sizeof(collect_once_t));
+ c->o = o;
+ c->count = 1;
+ if (!p)
+ once_obj = c;
+ else
+ p->next = c;
+ pthread_mutex_init(&c->m, NULL);
+ }
+ else
+ c->count += 1;
+ pthread_spin_unlock (&once_global);
+ return c;
+}
+
+static void
+leaveOnceObject (collect_once_t *c)
+{
+ collect_once_t *h, *p = NULL;
+ if (!c)
+ return;
+ pthread_spin_lock (&once_global);
+ h = once_obj;
+ while (h != NULL && c != h)
+ h = (p = h)->next;
+
+ if (h)
+ {
+ c->count -= 1;
+ if (c->count == 0)
+ {
+ pthread_mutex_destroy(&c->m);
+ if (!p)
+ once_obj = c->next;
+ else
+ p->next = c->next;
+ free (c);
+ }
+ }
+ else
+ fprintf(stderr, "%p not found?!?!\n", (void *) c);
+ pthread_spin_unlock (&once_global);
+}
+
+static void
+_pthread_once_cleanup (void *o)
+{
+ collect_once_t *co = (collect_once_t *) o;
+ pthread_mutex_unlock (&co->m);
+ leaveOnceObject (co);
+}
+
+static int
+_pthread_once_raw (pthread_once_t *o, void (*func)(void))
+{
+ collect_once_t *co;
+ long state = *o;
+
+ CHECK_PTR(o);
+ CHECK_PTR(func);
+
+ if (state == 1)
+ return 0;
+ co = enterOnceObject(o);
+ pthread_mutex_lock(&co->m);
+ if (*o == 0)
+ {
+ func();
+ *o = 1;
+ }
+ else if (*o != 1)
+ fprintf (stderr," once %p is %ld\n", (void *) o, (long) *o);
+ pthread_mutex_unlock(&co->m);
+ leaveOnceObject(co);
+
+ /* Done */
+ return 0;
+}
+
+/* Unimplemented. */
+void *
+pthread_timechange_handler_np(void *dummy)
+{
+ return NULL;
+}
+
+/* Compatibility routine for pthread-win32. It waits for ellapse of
+ interval and additionally checks for possible thread-cancelation. */
+int
+pthread_delay_np (const struct timespec *interval)
+{
+ DWORD to = (!interval ? 0 : dwMilliSecs (_pthread_time_in_ms_from_timespec (interval)));
+ struct _pthread_v *s = __pthread_self_lite ();
+
+ if (!to)
+ {
+ pthread_testcancel ();
+ Sleep (0);
+ pthread_testcancel ();
+ return 0;
+ }
+ pthread_testcancel ();
+ if (s->evStart)
+ _pthread_wait_for_single_object (s->evStart, to);
+ else
+ Sleep (to);
+ pthread_testcancel ();
+ return 0;
+}
+
+int pthread_delay_np_ms (DWORD to);
+
+int
+pthread_delay_np_ms (DWORD to)
+{
+ struct _pthread_v *s = __pthread_self_lite ();
+
+ if (!to)
+ {
+ pthread_testcancel ();
+ Sleep (0);
+ pthread_testcancel ();
+ return 0;
+ }
+ pthread_testcancel ();
+ if (s->evStart)
+ _pthread_wait_for_single_object (s->evStart, to);
+ else
+ Sleep (to);
+ pthread_testcancel ();
+ return 0;
+}
+
+/* Compatibility routine for pthread-win32. It returns the
+ amount of available CPUs on system. */
+int
+pthread_num_processors_np(void)
+{
+ int r = 0;
+ DWORD_PTR ProcessAffinityMask, SystemAffinityMask;
+
+ if (GetProcessAffinityMask(GetCurrentProcess(), &ProcessAffinityMask, &SystemAffinityMask))
+ {
+ for(; ProcessAffinityMask != 0; ProcessAffinityMask >>= 1)
+ r += (ProcessAffinityMask & 1) != 0;
+ }
+ /* assume at least 1 */
+ return r ? r : 1;
+}
+
+/* Compatiblity routine for pthread-win32. Allows to set amount of used
+ CPUs for process. */
+int
+pthread_set_num_processors_np(int n)
+{
+ DWORD_PTR ProcessAffinityMask, ProcessNewAffinityMask = 0, SystemAffinityMask;
+ int r = 0;
+ /* need at least 1 */
+ n = n ? n : 1;
+ if (GetProcessAffinityMask (GetCurrentProcess (), &ProcessAffinityMask, &SystemAffinityMask))
+ {
+ for (; ProcessAffinityMask != 0; ProcessAffinityMask >>= 1)
+ {
+ ProcessNewAffinityMask <<= 1;
+ if ((ProcessAffinityMask & 1) != 0 && r < n)
+ {
+ ProcessNewAffinityMask |= 1;
+ r++;
+ }
+ }
+ SetProcessAffinityMask (GetCurrentProcess (),ProcessNewAffinityMask);
+ }
+ return r;
+}
+
+int
+pthread_once (pthread_once_t *o, void (*func)(void))
+{
+ collect_once_t *co;
+ long state = *o;
+
+ CHECK_PTR(o);
+ CHECK_PTR(func);
+
+ if (state == 1)
+ return 0;
+ co = enterOnceObject(o);
+ pthread_mutex_lock(&co->m);
+ if (*o == 0)
+ {
+ pthread_cleanup_push(_pthread_once_cleanup, co);
+ func();
+ pthread_cleanup_pop(0);
+ *o = 1;
+ }
+ else if (*o != 1)
+ fprintf (stderr," once %p is %ld\n", (void *) o, (long) *o);
+ pthread_mutex_unlock(&co->m);
+ leaveOnceObject(co);
+
+ return 0;
+}
+
+int
+pthread_key_create (pthread_key_t *key, void (* dest)(void *))
+{
+ unsigned int i;
+ long nmax;
+ void (**d)(void *);
+
+ if (!key)
+ return EINVAL;
+
+ pthread_rwlock_wrlock (&_pthread_key_lock);
+
+ for (i = _pthread_key_sch; i < _pthread_key_max; i++)
+ {
+ if (!_pthread_key_dest[i])
+ {
+ *key = i;
+ if (dest)
+ _pthread_key_dest[i] = dest;
+ else
+ _pthread_key_dest[i] = (void(*)(void *))1;
+ pthread_rwlock_unlock (&_pthread_key_lock);
+ return 0;
+ }
+ }
+
+ for (i = 0; i < _pthread_key_sch; i++)
+ {
+ if (!_pthread_key_dest[i])
+ {
+ *key = i;
+ if (dest)
+ _pthread_key_dest[i] = dest;
+ else
+ _pthread_key_dest[i] = (void(*)(void *))1;
+ pthread_rwlock_unlock (&_pthread_key_lock);
+
+ return 0;
+ }
+ }
+
+ if (_pthread_key_max == PTHREAD_KEYS_MAX)
+ {
+ pthread_rwlock_unlock(&_pthread_key_lock);
+ return ENOMEM;
+ }
+
+ nmax = _pthread_key_max * 2;
+ if (nmax == 0)
+ nmax = _pthread_key_max + 1;
+ if (nmax > PTHREAD_KEYS_MAX)
+ nmax = PTHREAD_KEYS_MAX;
+
+ /* No spare room anywhere */
+ d = (void (__cdecl **)(void *))realloc(_pthread_key_dest, nmax * sizeof(*d));
+ if (!d)
+ {
+ pthread_rwlock_unlock (&_pthread_key_lock);
+ return ENOMEM;
+ }
+
+ /* Clear new region */
+ memset ((void *) &d[_pthread_key_max], 0, (nmax-_pthread_key_max)*sizeof(void *));
+
+ /* Use new region */
+ _pthread_key_dest = d;
+ _pthread_key_sch = _pthread_key_max + 1;
+ *key = _pthread_key_max;
+ _pthread_key_max = nmax;
+
+ if (dest)
+ _pthread_key_dest[*key] = dest;
+ else
+ _pthread_key_dest[*key] = (void(*)(void *))1;
+
+ pthread_rwlock_unlock (&_pthread_key_lock);
+ return 0;
+}
+
+int
+pthread_key_delete (pthread_key_t key)
+{
+ if (key >= _pthread_key_max || !_pthread_key_dest)
+ return EINVAL;
+
+ pthread_rwlock_wrlock (&_pthread_key_lock);
+
+ _pthread_key_dest[key] = NULL;
+
+ /* Start next search from our location */
+ if (_pthread_key_sch > key)
+ _pthread_key_sch = key;
+ /* So now we need to walk the complete list of threads
+ and remove key's reference for it. */
+ __pth_remove_use_for_key (key);
+
+ pthread_rwlock_unlock (&_pthread_key_lock);
+ return 0;
+}
+
+void *
+pthread_getspecific (pthread_key_t key)
+{
+ DWORD lasterr = GetLastError ();
+ void *r;
+ _pthread_v *t = __pthread_self_lite ();
+ pthread_spin_lock (&t->spin_keys);
+ r = (key >= t->keymax || t->keyval_set[key] == 0 ? NULL : t->keyval[key]);
+ pthread_spin_unlock (&t->spin_keys);
+ SetLastError (lasterr);
+ return r;
+}
+
+int
+pthread_setspecific (pthread_key_t key, const void *value)
+{
+ DWORD lasterr = GetLastError ();
+ _pthread_v *t = __pthread_self_lite ();
+
+ pthread_spin_lock (&t->spin_keys);
+
+ if (key >= t->keymax)
+ {
+ int keymax = (key + 1);
+ void **kv;
+ unsigned char *kv_set;
+
+ kv = (void **) realloc (t->keyval, keymax * sizeof (void *));
+
+ if (!kv)
+ {
+ pthread_spin_unlock (&t->spin_keys);
+ return ENOMEM;
+ }
+ kv_set = (unsigned char *) realloc (t->keyval_set, keymax);
+ if (!kv_set)
+ {
+ pthread_spin_unlock (&t->spin_keys);
+ return ENOMEM;
+ }
+
+ /* Clear new region */
+ memset (&kv[t->keymax], 0, (keymax - t->keymax)*sizeof(void *));
+ memset (&kv_set[t->keymax], 0, (keymax - t->keymax));
+
+ t->keyval = kv;
+ t->keyval_set = kv_set;
+ t->keymax = keymax;
+ }
+
+ t->keyval[key] = (void *) value;
+ t->keyval_set[key] = 1;
+ pthread_spin_unlock (&t->spin_keys);
+ SetLastError (lasterr);
+
+ return 0;
+}
+
+int
+pthread_equal (pthread_t t1, pthread_t t2)
+{
+ return (t1 == t2);
+}
+
+void
+pthread_tls_init (void)
+{
+ _pthread_tls = TlsAlloc();
+
+ /* Cannot continue if out of indexes */
+ if (_pthread_tls == TLS_OUT_OF_INDEXES)
+ abort();
+}
+
+void
+_pthread_cleanup_dest (pthread_t t)
+{
+ _pthread_v *tv;
+ unsigned int i, j;
+
+ if (!t)
+ return;
+ tv = __pth_gpointer_locked (t);
+ if (!tv)
+ return;
+
+ for (j = 0; j < PTHREAD_DESTRUCTOR_ITERATIONS; j++)
+ {
+ int flag = 0;
+
+ pthread_spin_lock (&tv->spin_keys);
+ for (i = 0; i < tv->keymax; i++)
+ {
+ void *val = tv->keyval[i];
+
+ if (tv->keyval_set[i])
+ {
+ pthread_rwlock_rdlock (&_pthread_key_lock);
+ if ((uintptr_t) _pthread_key_dest[i] > 1)
+ {
+ /* Call destructor */
+ tv->keyval[i] = NULL;
+ tv->keyval_set[i] = 0;
+ pthread_spin_unlock (&tv->spin_keys);
+ _pthread_key_dest[i](val);
+ pthread_spin_lock (&tv->spin_keys);
+ flag = 1;
+ }
+ else
+ {
+ tv->keyval[i] = NULL;
+ tv->keyval_set[i] = 0;
+ }
+ pthread_rwlock_unlock(&_pthread_key_lock);
+ }
+ }
+ pthread_spin_unlock (&tv->spin_keys);
+ /* Nothing to do? */
+ if (!flag)
+ return;
+ }
+}
+
+static _pthread_v *
+__pthread_self_lite (void)
+{
+ _pthread_v *t;
+ pthread_spinlock_t new_spin_keys = PTHREAD_SPINLOCK_INITIALIZER;
+
+ _pthread_once_raw (&_pthread_tls_once, pthread_tls_init);
+
+ t = (_pthread_v *) TlsGetValue (_pthread_tls);
+ if (t)
+ return t;
+ /* Main thread? */
+ t = (struct _pthread_v *) pop_pthread_mem ();
+
+ /* If cannot initialize main thread, then the only thing we can do is return null pthread_t */
+ if (!__xl_f || !t)
+ return 0;
+
+ t->p_state = PTHREAD_DEFAULT_ATTR /*| PTHREAD_CREATE_DETACHED*/;
+ t->tid = GetCurrentThreadId();
+ t->evStart = CreateEvent (NULL, 1, 0, NULL);
+ t->p_clock = PTHREAD_MUTEX_INITIALIZER;
+ replace_spin_keys (&t->spin_keys, new_spin_keys);
+ t->sched_pol = SCHED_OTHER;
+ t->h = NULL; //GetCurrentThread();
+ if (!DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(), &t->h, 0, FALSE, DUPLICATE_SAME_ACCESS))
+ abort ();
+ t->sched.sched_priority = GetThreadPriority(t->h);
+ t->ended = 0;
+ t->thread_noposix = 1;
+
+ /* Save for later */
+ if (!TlsSetValue(_pthread_tls, t))
+ abort ();
+ return t;
+}
+
+pthread_t
+pthread_self (void)
+{
+ _pthread_v *t = __pthread_self_lite ();
+
+ if (!t)
+ return 0;
+ return t->x;
+}
+
+/* Internal helper for getting event handle of thread T. */
+void *
+pthread_getevent (void)
+{
+ _pthread_v *t = __pthread_self_lite ();
+ return (!t ? NULL : t->evStart);
+}
+
+/* Internal helper for getting thread handle of thread T. */
+void *
+pthread_gethandle (pthread_t t)
+{
+ struct _pthread_v *tv = __pth_gpointer_locked (t);
+ return (!tv ? NULL : tv->h);
+}
+
+/* Internal helper for getting pointer of clean of current thread. */
+struct _pthread_cleanup **
+pthread_getclean (void)
+{
+ struct _pthread_v *t = __pthread_self_lite ();
+ if (!t) return NULL;
+ return &t->clean;
+}
+
+int
+pthread_get_concurrency (int *val)
+{
+ *val = _pthread_concur;
+ return 0;
+}
+
+int
+pthread_set_concurrency (int val)
+{
+ _pthread_concur = val;
+ return 0;
+}
+
+void
+pthread_exit (void *res)
+{
+ _pthread_v *t = NULL;
+ unsigned rslt = (unsigned) ((intptr_t) res);
+ struct _pthread_v *id = __pthread_self_lite ();
+
+ id->ret_arg = res;
+
+ _pthread_cleanup_dest (id->x);
+ if (id->thread_noposix == 0)
+ longjmp(id->jb, 1);
+
+ /* Make sure we free ourselves if we are detached */
+ if ((t = (_pthread_v *)TlsGetValue(_pthread_tls)) != NULL)
+ {
+ if (!t->h)
+ {
+ t->valid = DEAD_THREAD;
+ if (t->evStart)
+ CloseHandle (t->evStart);
+ t->evStart = NULL;
+ rslt = (unsigned) (size_t) t->ret_arg;
+ push_pthread_mem(t);
+ t = NULL;
+ TlsSetValue (_pthread_tls, t);
+ }
+ else
+ {
+ rslt = (unsigned) (size_t) t->ret_arg;
+ t->ended = 1;
+ if (t->evStart)
+ CloseHandle (t->evStart);
+ t->evStart = NULL;
+ if ((t->p_state & PTHREAD_CREATE_DETACHED) == PTHREAD_CREATE_DETACHED)
+ {
+ t->valid = DEAD_THREAD;
+ CloseHandle (t->h);
+ t->h = NULL;
+ push_pthread_mem(t);
+ t = NULL;
+ TlsSetValue(_pthread_tls, t);
+ }
+ }
+ }
+ /* Time to die */
+ _endthreadex(rslt);
+}
+
+void
+_pthread_invoke_cancel (void)
+{
+ _pthread_cleanup *pcup;
+ struct _pthread_v *se = __pthread_self_lite ();
+ se->in_cancel = 1;
+ _pthread_setnobreak (1);
+ InterlockedDecrement(&_pthread_cancelling);
+
+ /* Call cancel queue */
+ for (pcup = se->clean; pcup; pcup = pcup->next)
+ {
+ pcup->func((pthread_once_t *)pcup->arg);
+ }
+
+ _pthread_setnobreak (0);
+ pthread_exit(PTHREAD_CANCELED);
+}
+
+int
+__pthread_shallcancel (void)
+{
+ struct _pthread_v *t;
+ if (!_pthread_cancelling)
+ return 0;
+ t = __pthread_self_lite ();
+ if (t == NULL)
+ return 0;
+ if (t->nobreak <= 0 && t->cancelled && (t->p_state & PTHREAD_CANCEL_ENABLE))
+ return 1;
+ return 0;
+}
+
+void
+_pthread_setnobreak (int v)
+{
+ struct _pthread_v *t = __pthread_self_lite ();
+ if (t == NULL)
+ return;
+ if (v > 0)
+ InterlockedIncrement ((long*)&t->nobreak);
+ else
+ InterlockedDecrement((long*)&t->nobreak);
+}
+
+void
+pthread_testcancel (void)
+{
+ struct _pthread_v *self = __pthread_self_lite ();
+
+ if (!self || self->in_cancel)
+ return;
+ if (!_pthread_cancelling)
+ return;
+ pthread_mutex_lock (&self->p_clock);
+
+ if (self->cancelled && (self->p_state & PTHREAD_CANCEL_ENABLE) && self->nobreak <= 0)
+ {
+ self->in_cancel = 1;
+ self->p_state &= ~PTHREAD_CANCEL_ENABLE;
+ if (self->evStart)
+ ResetEvent (self->evStart);
+ pthread_mutex_unlock (&self->p_clock);
+ _pthread_invoke_cancel ();
+ }
+ pthread_mutex_unlock (&self->p_clock);
+}
+
+int
+pthread_cancel (pthread_t t)
+{
+ struct _pthread_v *tv = __pth_gpointer_locked (t);
+
+ if (tv == NULL)
+ return ESRCH;
+ CHECK_OBJECT(tv, ESRCH);
+ /*if (tv->ended) return ESRCH;*/
+ pthread_mutex_lock(&tv->p_clock);
+ if (pthread_equal(pthread_self(), t))
+ {
+ if(tv->cancelled)
+ {
+ pthread_mutex_unlock(&tv->p_clock);
+ return (tv->in_cancel ? ESRCH : 0);
+ }
+ tv->cancelled = 1;
+ InterlockedIncrement(&_pthread_cancelling);
+ if(tv->evStart) SetEvent(tv->evStart);
+ if ((tv->p_state & PTHREAD_CANCEL_ASYNCHRONOUS) != 0 && (tv->p_state & PTHREAD_CANCEL_ENABLE) != 0)
+ {
+ tv->p_state &= ~PTHREAD_CANCEL_ENABLE;
+ tv->in_cancel = 1;
+ pthread_mutex_unlock(&tv->p_clock);
+ _pthread_invoke_cancel();
+ }
+ else
+ pthread_mutex_unlock(&tv->p_clock);
+ return 0;
+ }
+
+ if ((tv->p_state & PTHREAD_CANCEL_ASYNCHRONOUS) != 0 && (tv->p_state & PTHREAD_CANCEL_ENABLE) != 0)
+ {
+ /* Dangerous asynchronous cancelling */
+ CONTEXT ctxt;
+
+ if(tv->in_cancel)
+ {
+ pthread_mutex_unlock(&tv->p_clock);
+ return (tv->in_cancel ? ESRCH : 0);
+ }
+ /* Already done? */
+ if(tv->cancelled || tv->in_cancel)
+ {
+ /* ??? pthread_mutex_unlock (&tv->p_clock); */
+ return ESRCH;
+ }
+
+ ctxt.ContextFlags = CONTEXT_CONTROL;
+
+ SuspendThread (tv->h);
+ if (WaitForSingleObject (tv->h, 0) == WAIT_TIMEOUT)
+ {
+ GetThreadContext(tv->h, &ctxt);
+#ifdef _M_X64
+ ctxt.Rip = (uintptr_t) _pthread_invoke_cancel;
+#elif defined(_M_IX86)
+ ctxt.Eip = (uintptr_t) _pthread_invoke_cancel;
+#elif defined(_M_ARM) || defined(_M_ARM64)
+ ctxt.Pc = (uintptr_t) _pthread_invoke_cancel;
+#else
+#error Unsupported architecture
+#endif
+#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
+ SetThreadContext (tv->h, &ctxt);
+#endif
+
+ /* Also try deferred Cancelling */
+ tv->cancelled = 1;
+ tv->p_state &= ~PTHREAD_CANCEL_ENABLE;
+ tv->in_cancel = 1;
+
+ /* Notify everyone to look */
+ InterlockedIncrement (&_pthread_cancelling);
+ if (tv->evStart)
+ SetEvent (tv->evStart);
+ pthread_mutex_unlock (&tv->p_clock);
+
+ ResumeThread (tv->h);
+ }
+ }
+ else
+ {
+ if (tv->cancelled == 0)
+ {
+ /* Safe deferred Cancelling */
+ tv->cancelled = 1;
+
+ /* Notify everyone to look */
+ InterlockedIncrement (&_pthread_cancelling);
+ if (tv->evStart)
+ SetEvent (tv->evStart);
+ }
+ else
+ {
+ pthread_mutex_unlock (&tv->p_clock);
+ return (tv->in_cancel ? ESRCH : 0);
+ }
+ }
+ pthread_mutex_unlock (&tv->p_clock);
+ return 0;
+}
+
+/* half-stubbed version as we don't really well support signals */
+int
+pthread_kill (pthread_t t, int sig)
+{
+ struct _pthread_v *tv;
+
+ pthread_mutex_lock (&mtx_pthr_locked);
+ tv = __pthread_get_pointer (t);
+ if (!tv || t != tv->x || tv->in_cancel || tv->ended || tv->h == NULL
+ || tv->h == INVALID_HANDLE_VALUE)
+ {
+ pthread_mutex_unlock (&mtx_pthr_locked);
+ return ESRCH;
+ }
+ pthread_mutex_unlock (&mtx_pthr_locked);
+ if (!sig)
+ return 0;
+ if (sig < SIGINT || sig > NSIG)
+ return EINVAL;
+ return pthread_cancel(t);
+}
+
+unsigned
+_pthread_get_state (const pthread_attr_t *attr, unsigned flag)
+{
+ return (attr->p_state & flag);
+}
+
+int
+_pthread_set_state (pthread_attr_t *attr, unsigned flag, unsigned val)
+{
+ if (~flag & val)
+ return EINVAL;
+ attr->p_state &= ~flag;
+ attr->p_state |= val;
+
+ return 0;
+}
+
+int
+pthread_attr_init (pthread_attr_t *attr)
+{
+ memset (attr, 0, sizeof (pthread_attr_t));
+ attr->p_state = PTHREAD_DEFAULT_ATTR;
+ attr->stack = NULL;
+ attr->s_size = 0;
+ return 0;
+}
+
+int
+pthread_attr_destroy (pthread_attr_t *attr)
+{
+ /* No need to do anything */
+ memset (attr, 0, sizeof(pthread_attr_t));
+ return 0;
+}
+
+int
+pthread_attr_setdetachstate (pthread_attr_t *a, int flag)
+{
+ return _pthread_set_state(a, PTHREAD_CREATE_DETACHED, flag);
+}
+
+int
+pthread_attr_getdetachstate (const pthread_attr_t *a, int *flag)
+{
+ *flag = _pthread_get_state(a, PTHREAD_CREATE_DETACHED);
+ return 0;
+}
+
+int
+pthread_attr_setinheritsched (pthread_attr_t *a, int flag)
+{
+ if (!a || (flag != PTHREAD_INHERIT_SCHED && flag != PTHREAD_EXPLICIT_SCHED))
+ return EINVAL;
+ return _pthread_set_state(a, PTHREAD_INHERIT_SCHED, flag);
+}
+
+int
+pthread_attr_getinheritsched (const pthread_attr_t *a, int *flag)
+{
+ *flag = _pthread_get_state(a, PTHREAD_INHERIT_SCHED);
+ return 0;
+}
+
+int
+pthread_attr_setscope (pthread_attr_t *a, int flag)
+{
+ return _pthread_set_state(a, PTHREAD_SCOPE_SYSTEM, flag);
+}
+
+int
+pthread_attr_getscope (const pthread_attr_t *a, int *flag)
+{
+ *flag = _pthread_get_state(a, PTHREAD_SCOPE_SYSTEM);
+ return 0;
+}
+
+int
+pthread_attr_getstack (const pthread_attr_t *attr, void **stack, size_t *size)
+{
+ *stack = (char *) attr->stack - attr->s_size;
+ *size = attr->s_size;
+ return 0;
+}
+
+int
+pthread_attr_setstack (pthread_attr_t *attr, void *stack, size_t size)
+{
+ attr->s_size = size;
+ attr->stack = (char *) stack + size;
+ return 0;
+}
+
+int
+pthread_attr_getstackaddr (const pthread_attr_t *attr, void **stack)
+{
+ *stack = attr->stack;
+ return 0;
+}
+
+int
+pthread_attr_setstackaddr (pthread_attr_t *attr, void *stack)
+{
+ attr->stack = stack;
+ return 0;
+}
+
+int
+pthread_attr_getstacksize (const pthread_attr_t *attr, size_t *size)
+{
+ *size = attr->s_size;
+ return 0;
+}
+
+int
+pthread_attr_setstacksize (pthread_attr_t *attr, size_t size)
+{
+ attr->s_size = size;
+ return 0;
+}
+
+static void
+test_cancel_locked (pthread_t t)
+{
+ struct _pthread_v *tv = __pth_gpointer_locked (t);
+
+ if (!tv || tv->in_cancel || tv->ended != 0 || (tv->p_state & PTHREAD_CANCEL_ENABLE) == 0)
+ return;
+ if ((tv->p_state & PTHREAD_CANCEL_ASYNCHRONOUS) == 0)
+ return;
+ if (WaitForSingleObject(tv->evStart, 0) != WAIT_OBJECT_0)
+ return;
+ pthread_mutex_unlock (&tv->p_clock);
+ _pthread_invoke_cancel();
+}
+
+int
+pthread_setcancelstate (int state, int *oldstate)
+{
+ _pthread_v *t = __pthread_self_lite ();
+
+ if (!t || (state & PTHREAD_CANCEL_ENABLE) != state)
+ return EINVAL;
+
+ pthread_mutex_lock (&t->p_clock);
+ if (oldstate)
+ *oldstate = t->p_state & PTHREAD_CANCEL_ENABLE;
+ t->p_state &= ~PTHREAD_CANCEL_ENABLE;
+ t->p_state |= state;
+ test_cancel_locked (t->x);
+ pthread_mutex_unlock (&t->p_clock);
+
+ return 0;
+}
+
+int
+pthread_setcanceltype (int type, int *oldtype)
+{
+ _pthread_v *t = __pthread_self_lite ();
+
+ if (!t || (type & PTHREAD_CANCEL_ASYNCHRONOUS) != type)
+ return EINVAL;
+
+ pthread_mutex_lock (&t->p_clock);
+ if (oldtype)
+ *oldtype = t->p_state & PTHREAD_CANCEL_ASYNCHRONOUS;
+ t->p_state &= ~PTHREAD_CANCEL_ASYNCHRONOUS;
+ t->p_state |= type;
+ test_cancel_locked (t->x);
+ pthread_mutex_unlock (&t->p_clock);
+
+ return 0;
+}
+
+void _fpreset (void);
+
+#if defined(__i386__)
+/* Align ESP on 16-byte boundaries. */
+# if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 2)
+__attribute__((force_align_arg_pointer))
+# endif
+#endif
+unsigned __stdcall
+pthread_create_wrapper (void *args)
+{
+ unsigned rslt = 0;
+ struct _pthread_v *tv = (struct _pthread_v *)args;
+
+ _fpreset();
+
+ pthread_mutex_lock (&mtx_pthr_locked);
+ pthread_mutex_lock (&tv->p_clock);
+ _pthread_once_raw(&_pthread_tls_once, pthread_tls_init);
+ TlsSetValue(_pthread_tls, tv);
+ tv->tid = GetCurrentThreadId();
+ pthread_mutex_unlock (&tv->p_clock);
+
+
+ if (!setjmp(tv->jb))
+ {
+ intptr_t trslt = (intptr_t) 128;
+ /* Provide to this thread a default exception handler. */
+ #ifdef __SEH__
+ asm ("\t.tl_start:\n");
+ #endif /* Call function and save return value */
+ pthread_mutex_unlock (&mtx_pthr_locked);
+ if (tv->func)
+ trslt = (intptr_t) tv->func(tv->ret_arg);
+ #ifdef __SEH__
+ asm ("\tnop\n\t.tl_end: nop\n"
+#ifdef __arm__
+ "\t.seh_handler __C_specific_handler, %except\n"
+#else
+ "\t.seh_handler __C_specific_handler, @except\n"
+#endif
+ "\t.seh_handlerdata\n"
+ "\t.long 1\n"
+ "\t.rva .tl_start, .tl_end, _gnu_exception_handler ,.tl_end\n"
+ "\t.text"
+ );
+ #endif
+ pthread_mutex_lock (&mtx_pthr_locked);
+ tv->ret_arg = (void*) trslt;
+ /* Clean up destructors */
+ _pthread_cleanup_dest(tv->x);
+ }
+ else
+ pthread_mutex_lock (&mtx_pthr_locked);
+
+ pthread_mutex_lock (&tv->p_clock);
+ rslt = (unsigned) (size_t) tv->ret_arg;
+ /* Make sure we free ourselves if we are detached */
+ if (tv->evStart)
+ CloseHandle (tv->evStart);
+ tv->evStart = NULL;
+ if (!tv->h)
+ {
+ tv->valid = DEAD_THREAD;
+ pthread_mutex_unlock (&tv->p_clock);
+ pthread_mutex_destroy (&tv->p_clock);
+ push_pthread_mem (tv);
+ tv = NULL;
+ TlsSetValue (_pthread_tls, tv);
+ }
+ else
+ {
+ pthread_mutex_unlock (&tv->p_clock);
+ pthread_mutex_destroy (&tv->p_clock);
+ /* Reinitialise p_clock, since there may be attempts at
+ destroying it again in __dyn_tls_thread later on. */
+ tv->p_clock = PTHREAD_MUTEX_INITIALIZER;
+ tv->ended = 1;
+ }
+ while (pthread_mutex_unlock (&mtx_pthr_locked) == 0)
+ Sleep (0);
+ _endthreadex (rslt);
+ return rslt;
+}
+
+int
+pthread_create (pthread_t *th, const pthread_attr_t *attr, void *(* func)(void *), void *arg)
+{
+ HANDLE thrd = NULL;
+ int redo = 0;
+ struct _pthread_v *tv;
+ unsigned int ssize = 0;
+ pthread_spinlock_t new_spin_keys = PTHREAD_SPINLOCK_INITIALIZER;
+
+ if (attr && attr->s_size > UINT_MAX)
+ return EINVAL;
+
+ if ((tv = pop_pthread_mem ()) == NULL)
+ return EAGAIN;
+
+ if (th)
+ *th = tv->x;
+
+ /* Save data in pthread_t */
+ tv->ended = 0;
+ tv->ret_arg = arg;
+ tv->func = func;
+ tv->p_state = PTHREAD_DEFAULT_ATTR;
+ tv->h = INVALID_HANDLE_VALUE;
+ /* We retry it here a few times, as events are a limited resource ... */
+ do
+ {
+ tv->evStart = CreateEvent (NULL, 1, 0, NULL);
+ if (tv->evStart != NULL)
+ break;
+ Sleep ((!redo ? 0 : 20));
+ }
+ while (++redo <= 4);
+
+ tv->p_clock = PTHREAD_MUTEX_INITIALIZER;
+ replace_spin_keys (&tv->spin_keys, new_spin_keys);
+ tv->valid = LIFE_THREAD;
+ tv->sched.sched_priority = THREAD_PRIORITY_NORMAL;
+ tv->sched_pol = SCHED_OTHER;
+ if (tv->evStart == NULL)
+ {
+ if (th)
+ memset (th, 0, sizeof (pthread_t));
+ push_pthread_mem (tv);
+ return EAGAIN;
+ }
+
+ if (attr)
+ {
+ int inh = 0;
+ tv->p_state = attr->p_state;
+ ssize = (unsigned int)attr->s_size;
+ pthread_attr_getinheritsched (attr, &inh);
+ if (inh)
+ {
+ tv->sched.sched_priority = __pthread_self_lite ()->sched.sched_priority;
+ }
+ else
+ tv->sched.sched_priority = attr->param.sched_priority;
+ }
+
+ /* Make sure tv->h has value of INVALID_HANDLE_VALUE */
+ _ReadWriteBarrier();
+
+ thrd = (HANDLE) _beginthreadex(NULL, ssize, pthread_create_wrapper, tv, 0x4/*CREATE_SUSPEND*/, NULL);
+ if (thrd == INVALID_HANDLE_VALUE)
+ thrd = 0;
+ /* Failed */
+ if (!thrd)
+ {
+ if (tv->evStart)
+ CloseHandle (tv->evStart);
+ pthread_mutex_destroy (&tv->p_clock);
+ replace_spin_keys (&tv->spin_keys, new_spin_keys);
+ tv->evStart = NULL;
+ tv->h = 0;
+ if (th)
+ memset (th, 0, sizeof (pthread_t));
+ push_pthread_mem (tv);
+ return EAGAIN;
+ }
+ {
+ int pr = tv->sched.sched_priority;
+ if (pr <= THREAD_PRIORITY_IDLE) {
+ pr = THREAD_PRIORITY_IDLE;
+ } else if (pr <= THREAD_PRIORITY_LOWEST) {
+ pr = THREAD_PRIORITY_LOWEST;
+ } else if (pr >= THREAD_PRIORITY_TIME_CRITICAL) {
+ pr = THREAD_PRIORITY_TIME_CRITICAL;
+ } else if (pr >= THREAD_PRIORITY_HIGHEST) {
+ pr = THREAD_PRIORITY_HIGHEST;
+ }
+ SetThreadPriority (thrd, pr);
+ }
+ ResetEvent (tv->evStart);
+ if ((tv->p_state & PTHREAD_CREATE_DETACHED) != 0)
+ {
+ tv->h = 0;
+ ResumeThread (thrd);
+ CloseHandle (thrd);
+ }
+ else
+ {
+ tv->h = thrd;
+ ResumeThread (thrd);
+ }
+ Sleep (0);
+ return 0;
+}
+
+int
+pthread_join (pthread_t t, void **res)
+{
+ DWORD dwFlags;
+ struct _pthread_v *tv = __pth_gpointer_locked (t);
+ pthread_spinlock_t new_spin_keys = PTHREAD_SPINLOCK_INITIALIZER;
+
+ if (!tv || tv->h == NULL || !GetHandleInformation(tv->h, &dwFlags))
+ return ESRCH;
+ if ((tv->p_state & PTHREAD_CREATE_DETACHED) != 0)
+ return EINVAL;
+ if (pthread_equal(pthread_self(), t))
+ return EDEADLK;
+
+ /* pthread_testcancel (); */
+ if (tv->ended == 0 || (tv->h != NULL && tv->h != INVALID_HANDLE_VALUE))
+ WaitForSingleObject (tv->h, INFINITE);
+ CloseHandle (tv->h);
+ if (tv->evStart)
+ CloseHandle (tv->evStart);
+ tv->evStart = NULL;
+ /* Obtain return value */
+ if (res)
+ *res = tv->ret_arg;
+ pthread_mutex_destroy (&tv->p_clock);
+ replace_spin_keys (&tv->spin_keys, new_spin_keys);
+ push_pthread_mem (tv);
+
+ return 0;
+}
+
+int
+_pthread_tryjoin (pthread_t t, void **res)
+{
+ DWORD dwFlags;
+ struct _pthread_v *tv;
+ pthread_spinlock_t new_spin_keys = PTHREAD_SPINLOCK_INITIALIZER;
+
+ pthread_mutex_lock (&mtx_pthr_locked);
+ tv = __pthread_get_pointer (t);
+
+ if (!tv || tv->h == NULL || !GetHandleInformation(tv->h, &dwFlags))
+ {
+ pthread_mutex_unlock (&mtx_pthr_locked);
+ return ESRCH;
+ }
+
+ if ((tv->p_state & PTHREAD_CREATE_DETACHED) != 0)
+ {
+ pthread_mutex_unlock (&mtx_pthr_locked);
+ return EINVAL;
+ }
+ if (pthread_equal(pthread_self(), t))
+ {
+ pthread_mutex_unlock (&mtx_pthr_locked);
+ return EDEADLK;
+ }
+ if(tv->ended == 0 && WaitForSingleObject(tv->h, 0))
+ {
+ if (tv->ended == 0)
+ {
+ pthread_mutex_unlock (&mtx_pthr_locked);
+ /* pthread_testcancel (); */
+ return EBUSY;
+ }
+ }
+ CloseHandle (tv->h);
+ if (tv->evStart)
+ CloseHandle (tv->evStart);
+ tv->evStart = NULL;
+
+ /* Obtain return value */
+ if (res)
+ *res = tv->ret_arg;
+ pthread_mutex_destroy (&tv->p_clock);
+ replace_spin_keys (&tv->spin_keys, new_spin_keys);
+
+ push_pthread_mem (tv);
+
+ pthread_mutex_unlock (&mtx_pthr_locked);
+ /* pthread_testcancel (); */
+ return 0;
+}
+
+int
+pthread_detach (pthread_t t)
+{
+ int r = 0;
+ DWORD dwFlags;
+ struct _pthread_v *tv = __pth_gpointer_locked (t);
+ HANDLE dw;
+ pthread_spinlock_t new_spin_keys = PTHREAD_SPINLOCK_INITIALIZER;
+
+ pthread_mutex_lock (&mtx_pthr_locked);
+ if (!tv || tv->h == NULL || !GetHandleInformation(tv->h, &dwFlags))
+ {
+ pthread_mutex_unlock (&mtx_pthr_locked);
+ return ESRCH;
+ }
+ if ((tv->p_state & PTHREAD_CREATE_DETACHED) != 0)
+ {
+ pthread_mutex_unlock (&mtx_pthr_locked);
+ return EINVAL;
+ }
+ /* if (tv->ended) r = ESRCH; */
+ dw = tv->h;
+ tv->h = 0;
+ tv->p_state |= PTHREAD_CREATE_DETACHED;
+ _ReadWriteBarrier();
+ if (dw)
+ {
+ CloseHandle (dw);
+ if (tv->ended)
+ {
+ if (tv->evStart)
+ CloseHandle (tv->evStart);
+ tv->evStart = NULL;
+ pthread_mutex_destroy (&tv->p_clock);
+ replace_spin_keys (&tv->spin_keys, new_spin_keys);
+ push_pthread_mem (tv);
+ }
+ }
+ pthread_mutex_unlock (&mtx_pthr_locked);
+
+ return r;
+}
+
+static int dummy_concurrency_level = 0;
+
+int
+pthread_getconcurrency (void)
+{
+ return dummy_concurrency_level;
+}
+
+int
+pthread_setconcurrency (int new_level)
+{
+ dummy_concurrency_level = new_level;
+ return 0;
+}
+
+int
+pthread_setname_np (pthread_t thread, const char *name)
+{
+ struct _pthread_v *tv;
+ char *stored_name;
+
+ if (name == NULL)
+ return EINVAL;
+
+ tv = __pth_gpointer_locked (thread);
+ if (!tv || thread != tv->x || tv->in_cancel || tv->ended || tv->h == NULL
+ || tv->h == INVALID_HANDLE_VALUE)
+ return ESRCH;
+
+ stored_name = strdup (name);
+ if (stored_name == NULL)
+ return ENOMEM;
+
+ if (tv->thread_name != NULL)
+ free (tv->thread_name);
+
+ tv->thread_name = stored_name;
+ SetThreadName (tv->tid, name);
+ return 0;
+}
+
+int
+pthread_getname_np (pthread_t thread, char *name, size_t len)
+{
+ HRESULT result;
+ struct _pthread_v *tv;
+
+ if (name == NULL)
+ return EINVAL;
+
+ tv = __pth_gpointer_locked (thread);
+ if (!tv || thread != tv->x || tv->in_cancel || tv->ended || tv->h == NULL
+ || tv->h == INVALID_HANDLE_VALUE)
+ return ESRCH;
+
+ if (len < 1)
+ return ERANGE;
+
+ if (tv->thread_name == NULL)
+ {
+ name[0] = '\0';
+ return 0;
+ }
+
+ if (strlen (tv->thread_name) >= len)
+ return ERANGE;
+
+ result = StringCchCopyNA (name, len, tv->thread_name, len - 1);
+ if (SUCCEEDED (result))
+ return 0;
+
+ return ERANGE;
+}