diff options
| author | Nic Gaffney <gaffney_nic@protonmail.com> | 2024-06-12 21:15:52 -0500 |
|---|---|---|
| committer | Nic Gaffney <gaffney_nic@protonmail.com> | 2024-06-12 21:15:52 -0500 |
| commit | 963fae202108acd0498349e872e4811fa6c6aba0 (patch) | |
| tree | 1a7d5b6ee837700819d8f6f5a2484342a0ab6ec1 /vendor/zgui/libs/winpthreads/src/thread.c | |
| parent | 6084001df845815efd9c0eb712acf4fd9311ce36 (diff) | |
| download | particle-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.c | 1914 |
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; +} |
