|  | /* | 
|  | Copyright (c) 2011 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 <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) || 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; | 
|  | } | 
|  | #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 (DWORD); | 
|  |  | 
|  | #if defined(_MSC_VER) && !defined (USE_VEH_FOR_MSC_SETTHREADNAME) | 
|  | __try | 
|  | { | 
|  | RaiseException (EXCEPTION_SET_THREAD_NAME, 0, infosize, (DWORD *) &info); | 
|  | } | 
|  | __except (EXCEPTION_EXECUTE_HANDLER) | 
|  | { | 
|  | } | 
|  | #else | 
|  | /* Without a debugger we *must* have an exception handler, | 
|  | * otherwise raising an exception will crash the process. | 
|  | */ | 
|  | if ((!IsDebuggerPresent ()) && (SetThreadName_VEH_handle == NULL)) | 
|  | return; | 
|  |  | 
|  | RaiseException (EXCEPTION_SET_THREAD_NAME, 0, infosize, (DWORD *) &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; | 
|  | 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) | 
|  | { | 
|  | _pthread_v *t; | 
|  |  | 
|  | if (1) | 
|  | return; | 
|  | 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); | 
|  | } | 
|  |  | 
|  | /* Hook for TLS-based deregistration/registration of thread.  */ | 
|  | static BOOL 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(_MSC_VER) || defined (USE_VEH_FOR_MSC_SETTHREADNAME) | 
|  | if (lpreserved == NULL && SetThreadName_VEH_handle != NULL) | 
|  | { | 
|  | RemoveVectoredExceptionHandler (SetThreadName_VEH_handle); | 
|  | SetThreadName_VEH_handle = NULL; | 
|  | } | 
|  | #endif | 
|  | free_pthread_mem (); | 
|  | } | 
|  | else if (dwReason == DLL_PROCESS_ATTACH) | 
|  | { | 
|  | #if !defined(_MSC_VER) || defined (USE_VEH_FOR_MSC_SETTHREADNAME) | 
|  | SetThreadName_VEH_handle = AddVectoredExceptionHandler (1, &SetThreadName_VEH); | 
|  | /* 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); | 
|  | 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); | 
|  | t->spin_keys = new_spin_keys; | 
|  | push_pthread_mem (t); | 
|  | t = NULL; | 
|  | TlsSetValue (_pthread_tls, t); | 
|  | return TRUE; | 
|  | } | 
|  | pthread_mutex_destroy(&t->p_clock); | 
|  | t->spin_keys = new_spin_keys; | 
|  | } | 
|  | else if (t) | 
|  | { | 
|  | if (t->evStart) | 
|  | CloseHandle (t->evStart); | 
|  | t->evStart = NULL; | 
|  | pthread_mutex_destroy (&t->p_clock); | 
|  | t->spin_keys = new_spin_keys; | 
|  | } | 
|  | } | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | /* TLS-runtime section variable.  */ | 
|  | #ifdef _MSC_VER | 
|  | #pragma section(".CRT$XLF", shared) | 
|  | #endif | 
|  | PIMAGE_TLS_CALLBACK WINPTHREADS_ATTRIBUTE((WINPTHREADS_SECTION(".CRT$XLF"))) __xl_f  = (PIMAGE_TLS_CALLBACK) __dyn_tls_pthread; | 
|  | #ifdef _MSC_VER | 
|  | #pragma data_seg() | 
|  | #endif | 
|  |  | 
|  | #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 %d %s\n",NULL,(int)GetCurrentThreadId(),txt); | 
|  | else | 
|  | { | 
|  | printf("T%p %d V=%0X H=%p %s\n", | 
|  | __pth_gpointer_locked (t), | 
|  | (int)GetCurrentThreadId(), | 
|  | (int) (__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", 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 %d\n", o, (int) *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) | 
|  | WaitForSingleObject (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) | 
|  | WaitForSingleObject (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 %d\n", o, (int) *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; | 
|  | 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 () | 
|  | { | 
|  | _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; | 
|  | #else | 
|  | ctxt.Eip = (uintptr_t) _pthread_invoke_cancel; | 
|  | #endif | 
|  | SetThreadContext (tv->h, &ctxt); | 
|  |  | 
|  | /* 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_getstackaddr (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; | 
|  | } | 
|  |  | 
|  | int | 
|  | pthread_create_wrapper (void *args) | 
|  | { | 
|  | unsigned rslt = 0; | 
|  | struct _pthread_v *tv = (struct _pthread_v *)args; | 
|  |  | 
|  | 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" | 
|  | "\t.seh_handler __C_specific_handler, @except\n" | 
|  | "\t.seh_handlerdata\n" | 
|  | "\t.long 1\n" | 
|  | "\t.rva .tl_start, .tl_end, _gnu_exception_handler ,.tl_end\n" | 
|  | "\t.text" | 
|  | ); | 
|  | #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"); | 
|  | #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); | 
|  | 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; | 
|  | size_t ssize = 0; | 
|  | pthread_spinlock_t new_spin_keys = PTHREAD_SPINLOCK_INITIALIZER; | 
|  |  | 
|  | 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; | 
|  | 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 = 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, (unsigned int (__stdcall *)(void *))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); | 
|  | 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); | 
|  | 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); | 
|  | 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); | 
|  | 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; | 
|  | } |