summaryrefslogtreecommitdiff
path: root/src/thread/pthread_create.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/thread/pthread_create.c')
-rw-r--r--src/thread/pthread_create.c41
1 files changed, 26 insertions, 15 deletions
diff --git a/src/thread/pthread_create.c b/src/thread/pthread_create.c
index 55744155..087f6206 100644
--- a/src/thread/pthread_create.c
+++ b/src/thread/pthread_create.c
@@ -69,12 +69,25 @@ _Noreturn void __pthread_exit(void *result)
__pthread_tsd_run_dtors();
+ __block_app_sigs(&set);
+
+ /* This atomic potentially competes with a concurrent pthread_detach
+ * call; the loser is responsible for freeing thread resources. */
+ int state = a_cas(&self->detach_state, DT_JOINABLE, DT_EXITING);
+
+ if (state==DT_DETACHED && self->map_base) {
+ /* Since __unmapself bypasses the normal munmap code path,
+ * explicitly wait for vmlock holders first. This must be
+ * done before any locks are taken, to avoid lock ordering
+ * issues that could lead to deadlock. */
+ __vm_wait();
+ }
+
/* Access to target the exiting thread with syscalls that use
* its kernel tid is controlled by killlock. For detached threads,
* any use past this point would have undefined behavior, but for
* joinable threads it's a valid usage that must be handled.
* Signals must be blocked since pthread_kill must be AS-safe. */
- __block_app_sigs(&set);
LOCK(self->killlock);
/* The thread list lock must be AS-safe, and thus depends on
@@ -87,12 +100,23 @@ _Noreturn void __pthread_exit(void *result)
if (self->next == self) {
__tl_unlock();
UNLOCK(self->killlock);
+ self->detach_state = state;
__restore_sigs(&set);
exit(0);
}
/* At this point we are committed to thread termination. */
+ /* After the kernel thread exits, its tid may be reused. Clear it
+ * to prevent inadvertent use and inform functions that would use
+ * it that it's no longer available. At this point the killlock
+ * may be released, since functions that use it will consistently
+ * see the thread as having exited. Release it now so that no
+ * remaining locks (except thread list) are held if we end up
+ * resetting need_locks below. */
+ self->tid = 0;
+ UNLOCK(self->killlock);
+
/* Process robust list in userspace to handle non-pshared mutexes
* and the detached thread case where the robust list head will
* be invalid when the kernel would process it. */
@@ -125,10 +149,6 @@ _Noreturn void __pthread_exit(void *result)
self->prev->next = self->next;
self->prev = self->next = self;
- /* This atomic potentially competes with a concurrent pthread_detach
- * call; the loser is responsible for freeing thread resources. */
- int state = a_cas(&self->detach_state, DT_JOINABLE, DT_EXITING);
-
if (state==DT_DETACHED && self->map_base) {
/* Detached threads must block even implementation-internal
* signals, since they will not have a stack in their last
@@ -140,24 +160,15 @@ _Noreturn void __pthread_exit(void *result)
if (self->robust_list.off)
__syscall(SYS_set_robust_list, 0, 3*sizeof(long));
- /* Since __unmapself bypasses the normal munmap code path,
- * explicitly wait for vmlock holders first. */
- __vm_wait();
-
/* The following call unmaps the thread's stack mapping
* and then exits without touching the stack. */
__unmapself(self->map_base, self->map_size);
}
/* Wake any joiner. */
+ a_store(&self->detach_state, DT_EXITED);
__wake(&self->detach_state, 1, 1);
- /* After the kernel thread exits, its tid may be reused. Clear it
- * to prevent inadvertent use and inform functions that would use
- * it that it's no longer available. */
- self->tid = 0;
- UNLOCK(self->killlock);
-
for (;;) __syscall(SYS_exit, 0);
}