summaryrefslogtreecommitdiffstats
path: root/lib/libthr/thread
diff options
context:
space:
mode:
Diffstat (limited to 'lib/libthr/thread')
-rw-r--r--lib/libthr/thread/thr_exit.c56
-rw-r--r--lib/libthr/thread/thr_gc.c92
-rw-r--r--lib/libthr/thread/thr_info.c2
-rw-r--r--lib/libthr/thread/thr_init.c2
-rw-r--r--lib/libthr/thread/thr_join.c61
-rw-r--r--lib/libthr/thread/thr_private.h35
-rw-r--r--lib/libthr/thread/thr_stack.c14
7 files changed, 135 insertions, 127 deletions
diff --git a/lib/libthr/thread/thr_exit.c b/lib/libthr/thread/thr_exit.c
index 118876f..8c4a060 100644
--- a/lib/libthr/thread/thr_exit.c
+++ b/lib/libthr/thread/thr_exit.c
@@ -95,6 +95,7 @@ void
_pthread_exit(void *status)
{
pthread_t pthread;
+ int exitNow = 0;
/* Check if this thread is already in the process of exiting: */
if ((curthread->flags & PTHREAD_EXITING) != 0) {
@@ -121,37 +122,14 @@ _pthread_exit(void *status)
_thread_cleanupspecific();
}
- /*
- * Lock the garbage collector mutex to ensure that the garbage
- * collector is not using the dead thread list.
- */
- if (pthread_mutex_lock(&_gc_mutex) != 0)
- PANIC("Cannot lock gc mutex");
-
- /* Add this thread to the list of dead threads. */
- TAILQ_INSERT_HEAD(&_dead_list, curthread, dle);
-
- /*
- * Signal the garbage collector thread that there is something
- * to clean up.
- */
- if (pthread_cond_signal(&_gc_cond) != 0)
- PANIC("Cannot signal gc cond");
-
- /*
- * 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.
- */
- GIANT_LOCK(curthread);
-
- /* Unlock the garbage collector mutex. */
- if (pthread_mutex_unlock(&_gc_mutex) != 0)
- PANIC("Cannot unlock gc mutex");
+ /* Lock the dead list first to maintain correct lock order */
+ DEAD_LIST_LOCK;
+ _thread_critical_enter(curthread);
/* Check if there is a thread joining this one: */
if (curthread->joiner != NULL) {
pthread = curthread->joiner;
+ _SPINLOCK(&pthread->lock);
curthread->joiner = NULL;
/* Make the joining thread runnable: */
@@ -161,6 +139,7 @@ _pthread_exit(void *status)
pthread->join_status.ret = curthread->ret;
pthread->join_status.error = 0;
pthread->join_status.thread = NULL;
+ _SPINUNLOCK(&pthread->lock);
/* Make this thread collectable by the garbage collector. */
PTHREAD_ASSERT(((curthread->attr.flags & PTHREAD_DETACHED) ==
@@ -168,14 +147,31 @@ _pthread_exit(void *status)
curthread->attr.flags |= PTHREAD_DETACHED;
}
- /* Remove this thread from the thread list: */
+ /*
+ * Add this thread to the list of dead threads, and
+ * also remove it from the active threads list.
+ */
+ THREAD_LIST_LOCK;
+ TAILQ_INSERT_HEAD(&_dead_list, curthread, dle);
TAILQ_REMOVE(&_thread_list, curthread, tle);
-
PTHREAD_SET_STATE(curthread, PS_DEAD);
- GIANT_UNLOCK(curthread);
+ _thread_critical_exit(curthread);
+
+ /*
+ * Signal the garbage collector thread that there is something
+ * to clean up.
+ */
+ if (pthread_cond_signal(&_gc_cond) != 0)
+ PANIC("Cannot signal gc cond");
/* If we're the last thread, call it quits */
if (TAILQ_EMPTY(&_thread_list))
+ exitNow = 1;
+
+ THREAD_LIST_UNLOCK;
+ DEAD_LIST_UNLOCK;
+
+ if (exitNow)
exit(0);
/*
diff --git a/lib/libthr/thread/thr_gc.c b/lib/libthr/thread/thr_gc.c
index e62c50b..d491bff 100644
--- a/lib/libthr/thread/thr_gc.c
+++ b/lib/libthr/thread/thr_gc.c
@@ -75,19 +75,17 @@ _thread_gc(pthread_addr_t arg)
_thread_dump_info();
/*
- * Lock the garbage collector mutex which ensures that
+ * Lock the list of dead threads which ensures that
* this thread sees another thread exit:
*/
- if (pthread_mutex_lock(&_gc_mutex) != 0)
- PANIC("Cannot lock gc mutex");
+ DEAD_LIST_LOCK;
/* No stack or thread structure to free yet. */
p_stack = NULL;
pthread_cln = NULL;
- GIANT_LOCK(curthread);
-
/* Check if this is the last running thread. */
+ THREAD_LIST_LOCK;
if (TAILQ_FIRST(&_thread_list) == curthread &&
TAILQ_NEXT(curthread, tle) == NULL)
/*
@@ -95,6 +93,7 @@ _thread_gc(pthread_addr_t arg)
* now.
*/
f_done = 1;
+ THREAD_LIST_UNLOCK;
/*
* Enter a loop to search for the first dead thread that
@@ -106,57 +105,43 @@ _thread_gc(pthread_addr_t arg)
/* Don't destroy the initial thread. */
if (pthread == _thread_initial)
continue;
+
+ _SPINLOCK(&pthread->lock);
+
/*
- * Check if this thread has detached:
+ * Check if the stack was not specified by
+ * the caller to pthread_create() and has not
+ * been destroyed yet:
*/
- if ((pthread->attr.flags &
- PTHREAD_DETACHED) != 0) {
- /* Remove this thread from the dead list: */
- TAILQ_REMOVE(&_dead_list, pthread, dle);
-
- /*
- * Check if the stack was not specified by
- * the caller to pthread_create() and has not
- * been destroyed yet:
- */
- if (pthread->attr.stackaddr_attr == NULL &&
- pthread->stack != NULL) {
- _thread_stack_free(pthread->stack,
- pthread->attr.stacksize_attr,
- pthread->attr.guardsize_attr);
- }
-
- /*
- * Point to the thread structure that must
- * be freed outside the locks:
- */
- pthread_cln = pthread;
-
- } else {
- /*
- * This thread has not detached, so do
- * not destroy it.
- *
- * Check if the stack was not specified by
- * the caller to pthread_create() and has not
- * been destroyed yet:
- */
- if (pthread->attr.stackaddr_attr == NULL &&
- pthread->stack != NULL) {
- _thread_stack_free(pthread->stack,
- pthread->attr.stacksize_attr,
- pthread->attr.guardsize_attr);
-
- /*
- * NULL the stack pointer now that the
- * memory has been freed:
- */
- pthread->stack = NULL;
- }
+ if (pthread->attr.stackaddr_attr == NULL &&
+ pthread->stack != NULL) {
+ _thread_stack_free(pthread->stack,
+ pthread->attr.stacksize_attr,
+ pthread->attr.guardsize_attr);
+ pthread->stack = NULL;
}
+
+ /*
+ * If the thread has not been detached, leave
+ * it on the dead thread list.
+ */
+ if ((pthread->attr.flags & PTHREAD_DETACHED) == 0) {
+ _SPINUNLOCK(&pthread->lock);
+ continue;
+ }
+
+ /* Remove this thread from the dead list: */
+ TAILQ_REMOVE(&_dead_list, pthread, dle);
+
+ /*
+ * Point to the thread structure that must
+ * be freed outside the locks:
+ */
+ pthread_cln = pthread;
+
+ _SPINUNLOCK(&pthread->lock);
}
- GIANT_UNLOCK(curthread);
/*
* Check if this is not the last thread and there is no
* memory to free this time around.
@@ -177,15 +162,14 @@ _thread_gc(pthread_addr_t arg)
* timeout (for a backup poll).
*/
if ((ret = pthread_cond_timedwait(&_gc_cond,
- &_gc_mutex, &abstime)) != 0 && ret != ETIMEDOUT) {
+ &dead_list_lock, &abstime)) != 0 && ret != ETIMEDOUT) {
_thread_printf(STDERR_FILENO, "ret = %d", ret);
PANIC("gc cannot wait for a signal");
}
}
/* Unlock the garbage collector mutex: */
- if (pthread_mutex_unlock(&_gc_mutex) != 0)
- PANIC("Cannot unlock gc mutex");
+ DEAD_LIST_UNLOCK;
/*
* If there is memory to free, do it now. The call to
diff --git a/lib/libthr/thread/thr_info.c b/lib/libthr/thread/thr_info.c
index b76d8e5..b0fae83 100644
--- a/lib/libthr/thread/thr_info.c
+++ b/lib/libthr/thread/thr_info.c
@@ -109,6 +109,7 @@ _thread_dump_info(void)
}
/* Check if there are no dead threads: */
+ DEAD_LIST_LOCK;
if (TAILQ_FIRST(&_dead_list) == NULL) {
/* Output a record: */
strcpy(s, "\n\nTHERE ARE NO DEAD THREADS\n");
@@ -126,6 +127,7 @@ _thread_dump_info(void)
dump_thread(fd, pthread, /*long_version*/ 0);
}
}
+ DEAD_LIST_UNLOCK;
/* Close the dump file: */
__sys_close(fd);
diff --git a/lib/libthr/thread/thr_init.c b/lib/libthr/thread/thr_init.c
index 3b59da4..98b0eef 100644
--- a/lib/libthr/thread/thr_init.c
+++ b/lib/libthr/thread/thr_init.c
@@ -326,7 +326,7 @@ _thread_init(void)
clockinfo.tick : CLOCK_RES_USEC_MIN;
/* Initialise the garbage collector mutex and condition variable. */
- if (_pthread_mutex_init(&_gc_mutex,NULL) != 0 ||
+ if (_pthread_mutex_init(&dead_list_lock,NULL) != 0 ||
_pthread_cond_init(&_gc_cond,NULL) != 0)
PANIC("Failed to initialise garbage collector mutex or condvar");
}
diff --git a/lib/libthr/thread/thr_join.c b/lib/libthr/thread/thr_join.c
index 35b52bc..061058a 100644
--- a/lib/libthr/thread/thr_join.c
+++ b/lib/libthr/thread/thr_join.c
@@ -60,38 +60,36 @@ _pthread_join(pthread_t pthread, void **thread_return)
}
/*
- * Lock the garbage collector mutex to ensure that the garbage
- * collector is not using the dead thread list.
- */
- if (pthread_mutex_lock(&_gc_mutex) != 0)
- PANIC("Cannot lock gc mutex");
-
- GIANT_LOCK(curthread);
-
- /*
* Search for the specified thread in the list of active threads. This
* is done manually here rather than calling _find_thread() because
* the searches in _thread_list and _dead_list (as well as setting up
* join/detach state) have to be done atomically.
*/
+ DEAD_LIST_LOCK;
+ THREAD_LIST_LOCK;
TAILQ_FOREACH(thread, &_thread_list, tle)
- if (thread == pthread)
+ if (thread == pthread) {
+ _SPINLOCK(&pthread->lock);
break;
+ }
if (thread == NULL)
/*
* Search for the specified thread in the list of dead threads:
*/
TAILQ_FOREACH(thread, &_dead_list, dle)
- if (thread == pthread)
+ if (thread == pthread) {
+ _SPINLOCK(&pthread->lock);
break;
-
+ }
+ THREAD_LIST_UNLOCK;
/* Check if the thread was not found or has been detached: */
if (thread == NULL ||
((pthread->attr.flags & PTHREAD_DETACHED) != 0)) {
- if (pthread_mutex_unlock(&_gc_mutex) != 0)
- PANIC("Cannot lock gc mutex");
+ if (thread != NULL)
+ _SPINUNLOCK(&pthread->lock);
+ DEAD_LIST_UNLOCK;
ret = ESRCH;
goto out;
@@ -99,33 +97,32 @@ _pthread_join(pthread_t pthread, void **thread_return)
if (pthread->joiner != NULL) {
/* Multiple joiners are not supported. */
/* XXXTHR - support multiple joiners. */
- if (pthread_mutex_unlock(&_gc_mutex) != 0)
- PANIC("Cannot lock gc mutex");
+ _SPINUNLOCK(&pthread->lock);
+ DEAD_LIST_UNLOCK;
ret = ENOTSUP;
goto out;
}
- /*
- * Unlock the garbage collector mutex, now that the garbage collector
- * can't be run:
- */
- if (pthread_mutex_unlock(&_gc_mutex) != 0)
- PANIC("Cannot lock gc mutex");
/* Check if the thread is not dead: */
if (pthread->state != PS_DEAD) {
+ _thread_critical_enter(curthread);
/* Set the running thread to be the joiner: */
pthread->joiner = curthread;
/* Keep track of which thread we're joining to: */
curthread->join_status.thread = pthread;
+ _SPINUNLOCK(&pthread->lock);
while (curthread->join_status.thread == pthread) {
PTHREAD_SET_STATE(curthread, PS_JOIN);
/* Wait for our signal to wake up. */
- GIANT_UNLOCK(curthread);
+ _thread_critical_exit(curthread);
+ DEAD_LIST_UNLOCK;
_thread_suspend(curthread, NULL);
- GIANT_LOCK(curthread);
+ /* XXX - For correctness reasons. */
+ DEAD_LIST_LOCK;
+ _thread_critical_enter(curthread);
}
/*
@@ -135,6 +132,15 @@ _pthread_join(pthread_t pthread, void **thread_return)
ret = curthread->join_status.error;
if ((ret == 0) && (thread_return != NULL))
*thread_return = curthread->join_status.ret;
+ _thread_critical_exit(curthread);
+ /*
+ * XXX - Must unlock here, instead of doing it earlier,
+ * because it could lead to a deadlock. If the thread
+ * we are joining is waiting on this lock we would
+ * deadlock if we released this lock before unlocking the
+ * joined thread.
+ */
+ DEAD_LIST_UNLOCK;
} else {
/*
* The thread exited (is dead) without being detached, and no
@@ -149,12 +155,13 @@ _pthread_join(pthread_t pthread, void **thread_return)
/* Make the thread collectable by the garbage collector. */
pthread->attr.flags |= PTHREAD_DETACHED;
-
+ _SPINUNLOCK(&pthread->lock);
+ if (pthread_cond_signal(&_gc_cond) != 0)
+ PANIC("Cannot signal gc cond");
+ DEAD_LIST_UNLOCK;
}
out:
- GIANT_UNLOCK(curthread);
-
_thread_leave_cancellation_point();
/* Return the completion status: */
diff --git a/lib/libthr/thread/thr_private.h b/lib/libthr/thread/thr_private.h
index 6048bd0..3668a0e 100644
--- a/lib/libthr/thread/thr_private.h
+++ b/lib/libthr/thread/thr_private.h
@@ -594,6 +594,34 @@ SCLASS TAILQ_HEAD(, pthread) _dead_list
;
#endif
+/*
+ * These two locks protect the global active threads list and
+ * the global dead threads list, respectively. Combining these
+ * into one lock for both lists doesn't seem wise, since it
+ * would likely increase contention during busy thread creation
+ * and destruction for very little savings in space.
+ *
+ * The lock for the "dead threads list" must be a pthread mutex
+ * because it is used with condition variables to synchronize
+ * the gc thread with active threads in the process of exiting or
+ * dead threads who have just been joined.
+ */
+SCLASS spinlock_t thread_list_lock
+#ifdef GLOBAL_PTHREAD_PRIVATE
+= _SPINLOCK_INITIALIZER
+#endif
+;
+SCLASS pthread_mutex_t dead_list_lock
+#ifdef GLOBAL_PTHREAD_PRIVATE
+= NULL
+#endif
+;
+
+#define THREAD_LIST_LOCK _SPINLOCK(&thread_list_lock)
+#define THREAD_LIST_UNLOCK _SPINUNLOCK(&thread_list_lock)
+#define DEAD_LIST_LOCK _pthread_mutex_lock(&dead_list_lock)
+#define DEAD_LIST_UNLOCK _pthread_mutex_unlock(&dead_list_lock)
+
/* Initial thread: */
SCLASS struct pthread *_thread_initial
#ifdef GLOBAL_PTHREAD_PRIVATE
@@ -644,12 +672,7 @@ SCLASS struct umtx _giant_mutex
SCLASS int _giant_count;
-/* Garbage collector mutex and condition variable. */
-SCLASS pthread_mutex_t _gc_mutex
-#ifdef GLOBAL_PTHREAD_PRIVATE
-= NULL
-#endif
-;
+/* Garbage collector condition variable. */
SCLASS pthread_cond_t _gc_cond
#ifdef GLOBAL_PTHREAD_PRIVATE
= NULL
diff --git a/lib/libthr/thread/thr_stack.c b/lib/libthr/thread/thr_stack.c
index c75d6ee..28b396b 100644
--- a/lib/libthr/thread/thr_stack.c
+++ b/lib/libthr/thread/thr_stack.c
@@ -144,8 +144,7 @@ _thread_stack_alloc(size_t stacksize, size_t guardsize)
* Use the garbage collector mutex for synchronization of the
* spare stack list.
*/
- if (pthread_mutex_lock(&_gc_mutex) != 0)
- PANIC("Cannot lock gc mutex");
+ DEAD_LIST_LOCK;
if ((spare_stack = LIST_FIRST(&_dstackq)) != NULL) {
/* Use the spare stack. */
@@ -154,8 +153,7 @@ _thread_stack_alloc(size_t stacksize, size_t guardsize)
}
/* Unlock the garbage collector mutex. */
- if (pthread_mutex_unlock(&_gc_mutex) != 0)
- PANIC("Cannot unlock gc mutex");
+ DEAD_LIST_UNLOCK;
}
/*
* The user specified a non-default stack and/or guard size, so try to
@@ -167,8 +165,7 @@ _thread_stack_alloc(size_t stacksize, size_t guardsize)
* Use the garbage collector mutex for synchronization of the
* spare stack list.
*/
- if (pthread_mutex_lock(&_gc_mutex) != 0)
- PANIC("Cannot lock gc mutex");
+ DEAD_LIST_LOCK;
LIST_FOREACH(spare_stack, &_mstackq, qe) {
if (spare_stack->stacksize == stack_size &&
@@ -180,8 +177,7 @@ _thread_stack_alloc(size_t stacksize, size_t guardsize)
}
/* Unlock the garbage collector mutex. */
- if (pthread_mutex_unlock(&_gc_mutex) != 0)
- PANIC("Cannot unlock gc mutex");
+ DEAD_LIST_UNLOCK;
}
/* Check if a stack was not allocated from a stack cache: */
@@ -212,7 +208,7 @@ _thread_stack_alloc(size_t stacksize, size_t guardsize)
return (stack);
}
-/* This function must be called with _gc_mutex held. */
+/* This function must be called with the 'dead thread list' lock held. */
void
_thread_stack_free(void *stack, size_t stacksize, size_t guardsize)
{
OpenPOWER on IntegriCloud