| /* | 
 |    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 <stdio.h> | 
 | #include <malloc.h> | 
 | #include <signal.h> | 
 | #include "pthread.h" | 
 | #include "thread.h" | 
 | #include "misc.h" | 
 | #include "spinlock.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; | 
 |  | 
 | /* 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); | 
 |   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; | 
 |   spin_t new_spin_keys = {0, 0, LIFE_SPINLOCK, 1}; | 
 |  | 
 |   if (dwReason == DLL_PROCESS_DETACH) | 
 |     { | 
 |       free_pthread_mem (); | 
 |     } | 
 |   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 spin_t once_global = {0, 0, LIFE_SPINLOCK,1}; | 
 |  | 
 | static collect_once_t * | 
 | enterOnceObject (pthread_once_t *o) | 
 | { | 
 |   collect_once_t *c, *p = NULL; | 
 |   _spin_lite_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; | 
 |   _spin_lite_unlock (&once_global); | 
 |   return c; | 
 | } | 
 |  | 
 | static void | 
 | leaveOnceObject (collect_once_t *c) | 
 | { | 
 |   collect_once_t *h, *p = NULL; | 
 |   if (!c) | 
 |     return; | 
 |   _spin_lite_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); | 
 |   _spin_lite_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 (); | 
 |   _spin_lite_lock (&t->spin_keys); | 
 |   r = (key >= t->keymax || t->keyval_set[key] == 0 ? NULL : t->keyval[key]); | 
 |   _spin_lite_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 (); | 
 |    | 
 |   _spin_lite_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) | 
 |         { | 
 | 	  _spin_lite_unlock (&t->spin_keys); | 
 | 	  return ENOMEM; | 
 | 	} | 
 |       kv_set = (unsigned char *) realloc (t->keyval_set, keymax); | 
 |       if (!kv_set) | 
 |         { | 
 | 	  _spin_lite_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; | 
 |   _spin_lite_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; | 
 |  | 
 | 		_spin_lite_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; | 
 | 					_spin_lite_unlock (&tv->spin_keys); | 
 | 					_pthread_key_dest[i](val); | 
 | 					_spin_lite_lock (&tv->spin_keys); | 
 | 					flag = 1; | 
 | 				} | 
 | 				else | 
 | 				{ | 
 | 					tv->keyval[i] = NULL; | 
 | 					tv->keyval_set[i] = 0; | 
 | 				} | 
 | 				pthread_rwlock_unlock(&_pthread_key_lock); | 
 | 			} | 
 | 		} | 
 | 		_spin_lite_unlock (&tv->spin_keys); | 
 | 		/* Nothing to do? */ | 
 | 		if (!flag) | 
 | 			return; | 
 | 	} | 
 | } | 
 |  | 
 | static _pthread_v * | 
 | __pthread_self_lite (void) | 
 | { | 
 |   _pthread_v *t; | 
 |   spin_t new_spin_keys = {0, 0, LIFE_SPINLOCK,1}; | 
 |  | 
 |   _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_t t) | 
 | { | 
 |   struct _pthread_v *tv = __pth_gpointer_locked (t); | 
 |   return (!tv ? NULL : tv->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); | 
 |       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 | 
 |     { | 
 |       tv->ended = 1; | 
 |       pthread_mutex_unlock (&tv->p_clock); | 
 |       pthread_mutex_destroy (&tv->p_clock); | 
 |     } | 
 |   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; | 
 |   spin_t new_spin_keys = {0, 0, LIFE_SPINLOCK,1}; | 
 |  | 
 |   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; | 
 |       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); | 
 |   spin_t new_spin_keys = {0, 0, LIFE_SPINLOCK,1}; | 
 |  | 
 |   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) | 
 |     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; | 
 |   spin_t new_spin_keys = {0, 0, LIFE_SPINLOCK,1}; | 
 |  | 
 |   pthread_mutex_lock (&mtx_pthr_locked); | 
 |   tv = __pth_gpointer_locked (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; | 
 |   spin_t new_spin_keys = {0, 0, LIFE_SPINLOCK,1}; | 
 |  | 
 |   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; | 
 | } | 
 |  |