summaryrefslogtreecommitdiffstats
path: root/lib/libpthread/thread/thr_exit.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/libpthread/thread/thr_exit.c')
-rw-r--r--lib/libpthread/thread/thr_exit.c80
1 files changed, 41 insertions, 39 deletions
diff --git a/lib/libpthread/thread/thr_exit.c b/lib/libpthread/thread/thr_exit.c
index 0d22638..7fbeb65 100644
--- a/lib/libpthread/thread/thr_exit.c
+++ b/lib/libpthread/thread/thr_exit.c
@@ -41,6 +41,9 @@
#include <pthread.h>
#include "pthread_private.h"
+#define FLAGS_IN_SCHEDQ \
+ (PTHREAD_FLAGS_IN_PRIOQ|PTHREAD_FLAGS_IN_WAITQ|PTHREAD_FLAGS_IN_WORKQ)
+
void __exit(int status)
{
int flags;
@@ -138,7 +141,7 @@ _thread_exit_cleanup(void)
void
pthread_exit(void *status)
{
- pthread_t pthread;
+ int frame;
/* Check if this thread is already in the process of exiting: */
if ((_thread_run->flags & PTHREAD_EXITING) != 0) {
@@ -172,25 +175,24 @@ pthread_exit(void *status)
_thread_run->poll_data.fds = NULL;
}
- /*
- * Defer signals to protect the scheduling queues from access
- * by the signal handler:
- */
- _thread_kern_sig_defer();
-
- /* Check if there are any threads joined to this one: */
- while ((pthread = TAILQ_FIRST(&(_thread_run->join_queue))) != NULL) {
- /* Remove the thread from the queue: */
- TAILQ_REMOVE(&_thread_run->join_queue, pthread, qe);
-
- /* Wake the joined thread and let it detach this thread: */
- PTHREAD_NEW_STATE(pthread,PS_RUNNING);
+ if ((frame = _thread_run->sigframe_count) == 0)
+ _thread_exit_finish();
+ else {
+ /*
+ * Jump back and unwind the signal frames to gracefully
+ * cleanup.
+ */
+ ___longjmp(*_thread_run->sigframes[frame]->sig_jb, 1);
}
- /*
- * Undefer and handle pending signals, yielding if necessary:
- */
- _thread_kern_sig_undefer();
+ /* This point should not be reached. */
+ PANIC("Dead thread has resumed");
+}
+
+void
+_thread_exit_finish(void)
+{
+ pthread_t pthread;
/*
* Lock the garbage collector mutex to ensure that the garbage
@@ -203,20 +205,6 @@ pthread_exit(void *status)
TAILQ_INSERT_HEAD(&_dead_list, _thread_run, dle);
/*
- * Defer signals to protect the scheduling queues from access
- * by the signal handler:
- */
- _thread_kern_sig_defer();
-
- /* Remove this thread from the thread list: */
- TAILQ_REMOVE(&_thread_list, _thread_run, tle);
-
- /*
- * Undefer and handle pending signals, yielding if necessary:
- */
- _thread_kern_sig_undefer();
-
- /*
* Signal the garbage collector thread that there is something
* to clean up.
*/
@@ -224,19 +212,33 @@ pthread_exit(void *status)
PANIC("Cannot signal gc cond");
/*
- * Mark the thread as dead so it will not return if it
- * gets context switched out when the mutex is unlocked.
+ * Avoid a race condition where a scheduling signal can occur
+ * causing the garbage collector thread to run. If this happens,
+ * the current thread can be cleaned out from under us.
*/
- PTHREAD_SET_STATE(_thread_run, PS_DEAD);
+ _thread_kern_sig_defer();
/* Unlock the garbage collector mutex: */
if (pthread_mutex_unlock(&_gc_mutex) != 0)
PANIC("Cannot lock gc mutex");
- /* This this thread will never be re-scheduled. */
- _thread_kern_sched(NULL);
+ /* Check if there are any threads joined to this one: */
+ while ((pthread = TAILQ_FIRST(&(_thread_run->join_queue))) != NULL) {
+ /* Remove the thread from the queue: */
+ TAILQ_REMOVE(&_thread_run->join_queue, pthread, sqe);
+ pthread->flags &= ~PTHREAD_FLAGS_IN_JOINQ;
+
+ /*
+ * Wake the joined thread and let it
+ * detach this thread:
+ */
+ PTHREAD_NEW_STATE(pthread, PS_RUNNING);
+ }
- /* This point should not be reached. */
- PANIC("Dead thread has resumed");
+ /* Remove this thread from the thread list: */
+ TAILQ_REMOVE(&_thread_list, _thread_run, tle);
+
+ /* This thread will never be re-scheduled. */
+ _thread_kern_sched_state(PS_DEAD, __FILE__, __LINE__);
}
#endif
OpenPOWER on IntegriCloud