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