(pthreads) Cleanups

This commit is contained in:
twinaphex 2017-12-27 22:08:59 +01:00
parent 4c0c842451
commit dbaa237c71

443
deps/pthreads/pte.c vendored
View File

@ -74,9 +74,8 @@ static int pte_thread_detach_common (unsigned char threadShouldExit)
*/ */
pte_thread_t * sp = (pte_thread_t *) pthread_getspecific (pte_selfThreadKey); pte_thread_t * sp = (pte_thread_t *) pthread_getspecific (pte_selfThreadKey);
if (sp != NULL) // otherwise OS thread with no implicit POSIX handle. if (sp) // otherwise OS thread with no implicit POSIX handle.
{ {
pte_callUserDestroyRoutines (sp); pte_callUserDestroyRoutines (sp);
(void) pthread_mutex_lock (&sp->cancelLock); (void) pthread_mutex_lock (&sp->cancelLock);
@ -91,22 +90,18 @@ static int pte_thread_detach_common (unsigned char threadShouldExit)
if (sp->detachState == PTHREAD_CREATE_DETACHED) if (sp->detachState == PTHREAD_CREATE_DETACHED)
{ {
if (threadShouldExit) if (threadShouldExit)
{
pte_threadExitAndDestroy (sp); pte_threadExitAndDestroy (sp);
}
else else
{
pte_threadDestroy (sp); pte_threadDestroy (sp);
}
// pte_osTlsSetValue (pte_selfThreadKey->key, NULL); #if 0
pte_osTlsSetValue (pte_selfThreadKey->key, NULL);
#endif
} }
else else
{ {
if (threadShouldExit) if (threadShouldExit)
{
pte_osThreadExit(); pte_osThreadExit();
}
} }
} }
} }
@ -116,41 +111,34 @@ static int pte_thread_detach_common (unsigned char threadShouldExit)
static void pte_threadDestroyCommon (pthread_t thread, unsigned char shouldThreadExit) static void pte_threadDestroyCommon (pthread_t thread, unsigned char shouldThreadExit)
{ {
pte_thread_t * tp = (pte_thread_t *) thread;
pte_thread_t threadCopy; pte_thread_t threadCopy;
pte_thread_t * tp = (pte_thread_t *) thread;
if (tp != NULL) if (!tp)
return;
/*
* Copy thread state so that the thread can be atomically NULLed.
*/
memcpy (&threadCopy, tp, sizeof (threadCopy));
/*
* Thread ID structs are never freed. They're NULLed and reused.
* This also sets the thread to PThreadStateInitial (invalid).
*/
pte_threadReusePush (thread);
(void) pthread_mutex_destroy(&threadCopy.cancelLock);
(void) pthread_mutex_destroy(&threadCopy.threadLock);
if (threadCopy.threadId != 0)
{ {
/* if (shouldThreadExit)
* Copy thread state so that the thread can be atomically NULLed. pte_osThreadExitAndDelete(threadCopy.threadId);
*/ else
memcpy (&threadCopy, tp, sizeof (threadCopy)); pte_osThreadDelete(threadCopy.threadId);
/*
* Thread ID structs are never freed. They're NULLed and reused.
* This also sets the thread to PThreadStateInitial (invalid).
*/
pte_threadReusePush (thread);
(void) pthread_mutex_destroy(&threadCopy.cancelLock);
(void) pthread_mutex_destroy(&threadCopy.threadLock);
if (threadCopy.threadId != 0)
{
if (shouldThreadExit)
{
pte_osThreadExitAndDelete(threadCopy.threadId);
}
else
{
pte_osThreadDelete(threadCopy.threadId);
}
}
} }
} /* pte_threadDestroy */ }
void void
pte_callUserDestroyRoutines (pthread_t thread) pte_callUserDestroyRoutines (pthread_t thread)
@ -171,159 +159,130 @@ pte_callUserDestroyRoutines (pthread_t thread)
* ------------------------------------------------------------------- * -------------------------------------------------------------------
*/ */
{ {
ThreadKeyAssoc * assoc; int assocsRemaining;
int iterations = 0;
ThreadKeyAssoc *assoc = NULL;
pte_thread_t *sp = (pte_thread_t *) thread;
if (thread != NULL) if (!thread)
return;
/*
* Run through all Thread<-->Key associations
* for the current thread.
*
* Do this process at most PTHREAD_DESTRUCTOR_ITERATIONS times.
*/
do
{ {
int assocsRemaining; assocsRemaining = 0;
int iterations = 0; iterations++;
pte_thread_t * sp = (pte_thread_t *) thread;
(void) pthread_mutex_lock(&(sp->threadLock));
/* /*
* Run through all Thread<-->Key associations * The pointer to the next assoc is stored in the thread struct so that
* for the current thread. * the assoc destructor in pthread_key_delete can adjust it
* * if it deletes this assoc. This can happen if we fail to acquire
* Do this process at most PTHREAD_DESTRUCTOR_ITERATIONS times. * both locks below, and are forced to release all of our locks,
* leaving open the opportunity for pthread_key_delete to get in
* before us.
*/ */
do sp->nextAssoc = sp->keys;
(void) pthread_mutex_unlock(&(sp->threadLock));
for (;;)
{ {
assocsRemaining = 0; void * value;
iterations++; pthread_key_t k;
void (*destructor) (void *);
(void) pthread_mutex_lock(&(sp->threadLock));
/* /*
* The pointer to the next assoc is stored in the thread struct so that * First we need to serialise with pthread_key_delete by locking
* the assoc destructor in pthread_key_delete can adjust it * both assoc guards, but in the reverse order to our convention,
* if it deletes this assoc. This can happen if we fail to acquire * so we must be careful to avoid deadlock.
* both locks below, and are forced to release all of our locks,
* leaving open the opportunity for pthread_key_delete to get in
* before us.
*/ */
sp->nextAssoc = sp->keys; (void) pthread_mutex_lock(&(sp->threadLock));
(void) pthread_mutex_unlock(&(sp->threadLock));
for (;;) if ((assoc = (ThreadKeyAssoc *)sp->nextAssoc) == NULL)
{
/* Finished */
pthread_mutex_unlock(&(sp->threadLock));
break;
}
else
{ {
void * value;
pthread_key_t k;
void (*destructor) (void *);
/* /*
* First we need to serialise with pthread_key_delete by locking * assoc->key must be valid because assoc can't change or be
* both assoc guards, but in the reverse order to our convention, * removed from our chain while we hold at least one lock. If
* so we must be careful to avoid deadlock. * the assoc was on our key chain then the key has not been
* deleted yet.
*
* Now try to acquire the second lock without deadlocking.
* If we fail, we need to relinquish the first lock and the
* processor and then try to acquire them all again.
*/ */
(void) pthread_mutex_lock(&(sp->threadLock)); if (pthread_mutex_trylock(&(assoc->key->keyLock)) == EBUSY)
if ((assoc = (ThreadKeyAssoc *)sp->nextAssoc) == NULL)
{ {
/* Finished */
pthread_mutex_unlock(&(sp->threadLock)); pthread_mutex_unlock(&(sp->threadLock));
break; pte_osThreadSleep(1); // Ugly but necessary to avoid priority effects.
}
else
{
/* /*
* assoc->key must be valid because assoc can't change or be * Go around again.
* removed from our chain while we hold at least one lock. If * If pthread_key_delete has removed this assoc in the meantime,
* the assoc was on our key chain then the key has not been * sp->nextAssoc will point to a new assoc.
* deleted yet.
*
* Now try to acquire the second lock without deadlocking.
* If we fail, we need to relinquish the first lock and the
* processor and then try to acquire them all again.
*/ */
if (pthread_mutex_trylock(&(assoc->key->keyLock)) == EBUSY) continue;
{
pthread_mutex_unlock(&(sp->threadLock));
pte_osThreadSleep(1); // Ugly but necessary to avoid priority effects.
/*
* Go around again.
* If pthread_key_delete has removed this assoc in the meantime,
* sp->nextAssoc will point to a new assoc.
*/
continue;
}
}
/* We now hold both locks */
sp->nextAssoc = assoc->nextKey;
/*
* Key still active; pthread_key_delete
* will block on these same mutexes before
* it can release actual key; therefore,
* key is valid and we can call the destroy
* routine;
*/
k = assoc->key;
destructor = k->destructor;
value = pte_osTlsGetValue(k->key);
pte_osTlsSetValue (k->key, NULL);
// Every assoc->key exists and has a destructor
if (value != NULL && iterations <= PTHREAD_DESTRUCTOR_ITERATIONS)
{
/*
* Unlock both locks before the destructor runs.
* POSIX says pthread_key_delete can be run from destructors,
* and that probably includes with this key as target.
* pthread_setspecific can also be run from destructors and
* also needs to be able to access the assocs.
*/
(void) pthread_mutex_unlock(&(sp->threadLock));
(void) pthread_mutex_unlock(&(k->keyLock));
assocsRemaining++;
#ifdef __cplusplus
try
{
/*
* Run the caller's cleanup routine.
*/
destructor (value);
}
catch (...)
{
/*
* A system unexpected exception has occurred
* running the user's destructor.
* We get control back within this block in case
* the application has set up it's own terminate
* handler. Since we are leaving the thread we
* should not get any internal pthreads
* exceptions.
*/
terminate ();
}
#else /* __cplusplus */
/*
* Run the caller's cleanup routine.
*/
destructor (value);
#endif /* __cplusplus */
}
else
{
/*
* Remove association from both the key and thread chains
* and reclaim it's memory resources.
*/
pte_tkAssocDestroy (assoc);
(void) pthread_mutex_unlock(&(sp->threadLock));
(void) pthread_mutex_unlock(&(k->keyLock));
} }
} }
/* We now hold both locks */
sp->nextAssoc = assoc->nextKey;
/*
* Key still active; pthread_key_delete
* will block on these same mutexes before
* it can release actual key; therefore,
* key is valid and we can call the destroy
* routine;
*/
k = assoc->key;
destructor = k->destructor;
value = pte_osTlsGetValue(k->key);
pte_osTlsSetValue (k->key, NULL);
// Every assoc->key exists and has a destructor
if (value && iterations <= PTHREAD_DESTRUCTOR_ITERATIONS)
{
/*
* Unlock both locks before the destructor runs.
* POSIX says pthread_key_delete can be run from destructors,
* and that probably includes with this key as target.
* pthread_setspecific can also be run from destructors and
* also needs to be able to access the assocs.
*/
(void) pthread_mutex_unlock(&(sp->threadLock));
(void) pthread_mutex_unlock(&(k->keyLock));
assocsRemaining++;
/*
* Run the caller's cleanup routine.
*/
destructor (value);
}
else
{
/*
* Remove association from both the key and thread chains
* and reclaim it's memory resources.
*/
pte_tkAssocDestroy (assoc);
(void) pthread_mutex_unlock(&(sp->threadLock));
(void) pthread_mutex_unlock(&(k->keyLock));
}
} }
while (assocsRemaining); }while (assocsRemaining);
}
} }
int pte_cancellable_wait (pte_osSemaphoreHandle semHandle, unsigned int* timeout) int pte_cancellable_wait (pte_osSemaphoreHandle semHandle, unsigned int* timeout)
@ -334,7 +293,7 @@ int pte_cancellable_wait (pte_osSemaphoreHandle semHandle, unsigned int* timeout
pthread_t self = pthread_self(); pthread_t self = pthread_self();
pte_thread_t *sp = (pte_thread_t *) self; pte_thread_t *sp = (pte_thread_t *) self;
if (sp != NULL) if (sp)
{ {
/* /*
* Get cancelEvent handle * Get cancelEvent handle
@ -360,7 +319,7 @@ int pte_cancellable_wait (pte_osSemaphoreHandle semHandle, unsigned int* timeout
break; break;
case PTE_OS_INTERRUPTED: case PTE_OS_INTERRUPTED:
if (sp != NULL) if (sp)
{ {
/* /*
* Should handle POSIX and implicit POSIX threads.. * Should handle POSIX and implicit POSIX threads..
@ -519,26 +478,18 @@ int pte_mutex_check_need_init (pthread_mutex_t * mutex)
mtx = *mutex; mtx = *mutex;
if (mtx == PTHREAD_MUTEX_INITIALIZER) if (mtx == PTHREAD_MUTEX_INITIALIZER)
{
result = pthread_mutex_init (mutex, NULL); result = pthread_mutex_init (mutex, NULL);
}
else if (mtx == PTHREAD_RECURSIVE_MUTEX_INITIALIZER) else if (mtx == PTHREAD_RECURSIVE_MUTEX_INITIALIZER)
{
result = pthread_mutex_init (mutex, &pte_recursive_mutexattr); result = pthread_mutex_init (mutex, &pte_recursive_mutexattr);
}
else if (mtx == PTHREAD_ERRORCHECK_MUTEX_INITIALIZER) else if (mtx == PTHREAD_ERRORCHECK_MUTEX_INITIALIZER)
{
result = pthread_mutex_init (mutex, &pte_errorcheck_mutexattr); result = pthread_mutex_init (mutex, &pte_errorcheck_mutexattr);
} /*
* The mutex has been destroyed while we were waiting to
* initialise it, so the operation that caused the
* auto-initialisation should fail.
*/
else if (mtx == NULL) else if (mtx == NULL)
{
/*
* The mutex has been destroyed while we were waiting to
* initialise it, so the operation that caused the
* auto-initialisation should fail.
*/
result = EINVAL; result = EINVAL;
}
pte_osMutexUnlock(pte_mutex_test_init_lock); pte_osMutexUnlock(pte_mutex_test_init_lock);
@ -547,8 +498,8 @@ int pte_mutex_check_need_init (pthread_mutex_t * mutex)
pthread_t pte_new (void) pthread_t pte_new (void)
{ {
pthread_t nil = NULL; pthread_t nil = NULL;
pte_thread_t * tp; pte_thread_t * tp = NULL;
/* /*
* If there's a reusable pthread_t then use it. * If there's a reusable pthread_t then use it.
@ -588,7 +539,6 @@ unsigned int pte_relmillisecs (const struct timespec * abstime)
const long long NANOSEC_PER_MILLISEC = 1000000; const long long NANOSEC_PER_MILLISEC = 1000000;
const long long MILLISEC_PER_SEC = 1000; const long long MILLISEC_PER_SEC = 1000;
unsigned int milliseconds; unsigned int milliseconds;
long long tmpAbsMilliseconds;
long tmpCurrMilliseconds; long tmpCurrMilliseconds;
struct timeb currSysTime; struct timeb currSysTime;
@ -603,7 +553,7 @@ unsigned int pte_relmillisecs (const struct timespec * abstime)
* *
* Assume all integers are unsigned, i.e. cannot test if less than 0. * Assume all integers are unsigned, i.e. cannot test if less than 0.
*/ */
tmpAbsMilliseconds = (int64_t)abstime->tv_sec * MILLISEC_PER_SEC; long long tmpAbsMilliseconds = (int64_t)abstime->tv_sec * MILLISEC_PER_SEC;
tmpAbsMilliseconds += ((int64_t)abstime->tv_nsec + (NANOSEC_PER_MILLISEC/2)) / NANOSEC_PER_MILLISEC; tmpAbsMilliseconds += ((int64_t)abstime->tv_nsec + (NANOSEC_PER_MILLISEC/2)) / NANOSEC_PER_MILLISEC;
/* get current system time */ /* get current system time */
@ -665,7 +615,6 @@ pte_threadReusePop (void)
{ {
pthread_t t = NULL; pthread_t t = NULL;
pte_osMutexLock (pte_thread_reuse_lock); pte_osMutexLock (pte_thread_reuse_lock);
if (PTE_THREAD_REUSE_EMPTY != pte_threadReuseTop) if (PTE_THREAD_REUSE_EMPTY != pte_threadReuseTop)
@ -677,9 +626,7 @@ pte_threadReusePop (void)
pte_threadReuseTop = tp->prevReuse; pte_threadReuseTop = tp->prevReuse;
if (PTE_THREAD_REUSE_EMPTY == pte_threadReuseTop) if (PTE_THREAD_REUSE_EMPTY == pte_threadReuseTop)
{
pte_threadReuseBottom = PTE_THREAD_REUSE_EMPTY; pte_threadReuseBottom = PTE_THREAD_REUSE_EMPTY;
}
tp->prevReuse = NULL; tp->prevReuse = NULL;
@ -859,7 +806,10 @@ int pte_threadStart (void *vthreadParms)
pte_thread_t *sp = (pte_thread_t *) self; pte_thread_t *sp = (pte_thread_t *) self;
start = threadParms->start; start = threadParms->start;
arg = threadParms->arg; arg = threadParms->arg;
// free (threadParms);
#if 0
free (threadParms);
#endif
pthread_setspecific (pte_selfThreadKey, sp); pthread_setspecific (pte_selfThreadKey, sp);
@ -868,14 +818,11 @@ int pte_threadStart (void *vthreadParms)
setjmp_rc = setjmp (sp->start_mark); setjmp_rc = setjmp (sp->start_mark);
/*
* Run the caller's routine;
*/
if (0 == setjmp_rc) if (0 == setjmp_rc)
{
/*
* Run the caller's routine;
*/
sp->exitStatus = status = (*start) (arg); sp->exitStatus = status = (*start) (arg);
}
else else
{ {
switch (setjmp_rc) switch (setjmp_rc)
@ -934,11 +881,9 @@ void pte_throw (unsigned int exception)
pte_thread_t * sp = (pte_thread_t *) pthread_getspecific (pte_selfThreadKey); pte_thread_t * sp = (pte_thread_t *) pthread_getspecific (pte_selfThreadKey);
/* Should never enter here */
if (exception != PTE_EPS_CANCEL && exception != PTE_EPS_EXIT) if (exception != PTE_EPS_CANCEL && exception != PTE_EPS_EXIT)
{
/* Should never enter here */
exit (1); exit (1);
}
if (NULL == sp || sp->implicit) if (NULL == sp || sp->implicit)
{ {
@ -962,8 +907,9 @@ void pte_throw (unsigned int exception)
pte_thread_detach_and_exit_np (); pte_thread_detach_and_exit_np ();
// pte_osThreadExit((void*)exitCode); #if 0
pte_osThreadExit((void*)exitCode);
#endif
} }
pte_pop_cleanup_all (1); pte_pop_cleanup_all (1);
@ -1026,19 +972,17 @@ int pte_tkAssocCreate (pte_thread_t * sp, pthread_key_t key)
assoc = (ThreadKeyAssoc *) calloc (1, sizeof (*assoc)); assoc = (ThreadKeyAssoc *) calloc (1, sizeof (*assoc));
if (assoc == NULL) if (assoc == NULL)
{
return ENOMEM; return ENOMEM;
}
assoc->thread = sp; assoc->thread = sp;
assoc->key = key; assoc->key = key;
/* /*
* Register assoc with key * Register assoc with key
*/ */
assoc->prevThread = NULL; assoc->prevThread = NULL;
assoc->nextThread = (ThreadKeyAssoc *) key->threads; assoc->nextThread = (ThreadKeyAssoc *) key->threads;
if (assoc->nextThread != NULL) if (assoc->nextThread)
assoc->nextThread->prevThread = assoc; assoc->nextThread->prevThread = assoc;
key->threads = (void *) assoc; key->threads = (void *) assoc;
@ -1047,7 +991,7 @@ int pte_tkAssocCreate (pte_thread_t * sp, pthread_key_t key)
*/ */
assoc->prevKey = NULL; assoc->prevKey = NULL;
assoc->nextKey = (ThreadKeyAssoc *) sp->keys; assoc->nextKey = (ThreadKeyAssoc *) sp->keys;
if (assoc->nextKey != NULL) if (assoc->nextKey)
assoc->nextKey->prevKey = assoc; assoc->nextKey->prevKey = assoc;
sp->keys = (void *) assoc; sp->keys = (void *) assoc;
@ -1069,48 +1013,47 @@ void pte_tkAssocDestroy (ThreadKeyAssoc * assoc)
* ------------------------------------------------------------------- * -------------------------------------------------------------------
*/ */
{ {
ThreadKeyAssoc *prev = NULL;
ThreadKeyAssoc *next = NULL;
/* /*
* Both key->keyLock and thread->threadLock are locked on * Both key->keyLock and thread->threadLock are locked on
* entry to this routine. * entry to this routine.
*/ */
if (assoc != NULL) if (!assoc)
return;
/* Remove assoc from thread's keys chain */
prev = assoc->prevKey;
next = assoc->nextKey;
if (prev)
prev->nextKey = next;
if (next)
next->prevKey = prev;
/* We're at the head of the thread's keys chain */
if (assoc->thread->keys == assoc)
assoc->thread->keys = next;
if (assoc->thread->nextAssoc == assoc)
{ {
ThreadKeyAssoc * prev, * next; /*
* Thread is exiting and we're deleting the assoc to be processed next.
/* Remove assoc from thread's keys chain */ * Hand thread the assoc after this one.
prev = assoc->prevKey; */
next = assoc->nextKey; assoc->thread->nextAssoc = next;
if (prev != NULL)
prev->nextKey = next;
if (next != NULL)
next->prevKey = prev;
/* We're at the head of the thread's keys chain */
if (assoc->thread->keys == assoc)
assoc->thread->keys = next;
if (assoc->thread->nextAssoc == assoc)
{
/*
* Thread is exiting and we're deleting the assoc to be processed next.
* Hand thread the assoc after this one.
*/
assoc->thread->nextAssoc = next;
}
/* Remove assoc from key's threads chain */
prev = assoc->prevThread;
next = assoc->nextThread;
if (prev != NULL)
prev->nextThread = next;
if (next != NULL)
next->prevThread = prev;
/* We're at the head of the key's threads chain */
if (assoc->key->threads == assoc)
assoc->key->threads = next;
free (assoc);
} }
/* Remove assoc from key's threads chain */
prev = assoc->prevThread;
next = assoc->nextThread;
if (prev)
prev->nextThread = next;
if (next)
next->prevThread = prev;
/* We're at the head of the key's threads chain */
if (assoc->key->threads == assoc)
assoc->key->threads = next;
free (assoc);
} }