am 94babaee: Merge "Make pthread join_state not protected by g_thread_list_lock."
* commit '94babaee1b6598b15bd807461055d4dcaaa52f10': Make pthread join_state not protected by g_thread_list_lock.
This commit is contained in:
commit
f7dbefb08f
@ -170,6 +170,11 @@ int pthread_attr_getguardsize(const pthread_attr_t* attr, size_t* guard_size) {
|
|||||||
int pthread_getattr_np(pthread_t t, pthread_attr_t* attr) {
|
int pthread_getattr_np(pthread_t t, pthread_attr_t* attr) {
|
||||||
pthread_internal_t* thread = reinterpret_cast<pthread_internal_t*>(t);
|
pthread_internal_t* thread = reinterpret_cast<pthread_internal_t*>(t);
|
||||||
*attr = thread->attr;
|
*attr = thread->attr;
|
||||||
|
// We prefer reading join_state here to setting thread->attr.flags in pthread_detach.
|
||||||
|
// Because data race exists in the latter case.
|
||||||
|
if (atomic_load(&thread->join_state) == THREAD_DETACHED) {
|
||||||
|
attr->flags |= PTHREAD_ATTR_FLAG_DETACHED;
|
||||||
|
}
|
||||||
// The main thread's stack information is not stored in thread->attr, and we need to
|
// The main thread's stack information is not stored in thread->attr, and we need to
|
||||||
// collect that at runtime.
|
// collect that at runtime.
|
||||||
if (thread->tid == getpid()) {
|
if (thread->tid == getpid()) {
|
||||||
|
@ -86,6 +86,12 @@ void __init_alternate_signal_stack(pthread_internal_t* thread) {
|
|||||||
int __init_thread(pthread_internal_t* thread, bool add_to_thread_list) {
|
int __init_thread(pthread_internal_t* thread, bool add_to_thread_list) {
|
||||||
int error = 0;
|
int error = 0;
|
||||||
|
|
||||||
|
if (__predict_true((thread->attr.flags & PTHREAD_ATTR_FLAG_DETACHED) == 0)) {
|
||||||
|
atomic_init(&thread->join_state, THREAD_NOT_JOINED);
|
||||||
|
} else {
|
||||||
|
atomic_init(&thread->join_state, THREAD_DETACHED);
|
||||||
|
}
|
||||||
|
|
||||||
// Set the scheduling policy/priority of the thread.
|
// Set the scheduling policy/priority of the thread.
|
||||||
if (thread->attr.sched_policy != SCHED_NORMAL) {
|
if (thread->attr.sched_policy != SCHED_NORMAL) {
|
||||||
sched_param param;
|
sched_param param;
|
||||||
@ -263,7 +269,7 @@ int pthread_create(pthread_t* thread_out, pthread_attr_t const* attr,
|
|||||||
if (init_errno != 0) {
|
if (init_errno != 0) {
|
||||||
// Mark the thread detached and replace its start_routine with a no-op.
|
// Mark the thread detached and replace its start_routine with a no-op.
|
||||||
// Letting the thread run is the easiest way to clean up its resources.
|
// Letting the thread run is the easiest way to clean up its resources.
|
||||||
thread->attr.flags |= PTHREAD_ATTR_FLAG_DETACHED;
|
atomic_store(&thread->join_state, THREAD_DETACHED);
|
||||||
thread->start_routine = __do_nothing;
|
thread->start_routine = __do_nothing;
|
||||||
pthread_mutex_unlock(&thread->startup_handshake_mutex);
|
pthread_mutex_unlock(&thread->startup_handshake_mutex);
|
||||||
return init_errno;
|
return init_errno;
|
||||||
|
@ -38,21 +38,18 @@ int pthread_detach(pthread_t t) {
|
|||||||
return ESRCH;
|
return ESRCH;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (thread->attr.flags & PTHREAD_ATTR_FLAG_DETACHED) {
|
ThreadJoinState old_state = THREAD_NOT_JOINED;
|
||||||
return EINVAL; // Already detached.
|
while (old_state == THREAD_NOT_JOINED &&
|
||||||
|
!atomic_compare_exchange_weak(&thread->join_state, &old_state, THREAD_DETACHED)) {
|
||||||
}
|
}
|
||||||
|
switch (old_state) {
|
||||||
if (thread->attr.flags & PTHREAD_ATTR_FLAG_JOINED) {
|
case THREAD_NOT_JOINED: return 0;
|
||||||
return 0; // Already being joined; silently do nothing, like glibc.
|
case THREAD_JOINED: return 0; // Already being joined; silently do nothing, like glibc.
|
||||||
}
|
case THREAD_DETACHED: return THREAD_DETACHED;
|
||||||
|
case THREAD_EXITED_NOT_JOINED: // Call pthread_join out of scope of pthread_accessor.
|
||||||
// If the thread has not exited, we can detach it safely.
|
|
||||||
if ((thread->attr.flags & PTHREAD_ATTR_FLAG_ZOMBIE) == 0) {
|
|
||||||
thread->attr.flags |= PTHREAD_ATTR_FLAG_DETACHED;
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// The thread is in zombie state, use pthread_join to clean it up.
|
// The thread is in THREAD_EXITED_NOT_JOINED, use pthread_join to clean it up.
|
||||||
return pthread_join(t, NULL);
|
return pthread_join(t, NULL);
|
||||||
}
|
}
|
||||||
|
@ -87,9 +87,12 @@ void pthread_exit(void* return_value) {
|
|||||||
thread->alternate_signal_stack = NULL;
|
thread->alternate_signal_stack = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool free_mapped_space = false;
|
ThreadJoinState old_state = THREAD_NOT_JOINED;
|
||||||
pthread_mutex_lock(&g_thread_list_lock);
|
while (old_state == THREAD_NOT_JOINED &&
|
||||||
if ((thread->attr.flags & PTHREAD_ATTR_FLAG_DETACHED) != 0) {
|
!atomic_compare_exchange_weak(&thread->join_state, &old_state, THREAD_EXITED_NOT_JOINED)) {
|
||||||
|
}
|
||||||
|
|
||||||
|
if (old_state == THREAD_DETACHED) {
|
||||||
// The thread is detached, no one will use pthread_internal_t after pthread_exit.
|
// The thread is detached, no one will use pthread_internal_t after pthread_exit.
|
||||||
// So we can free mapped space, which includes pthread_internal_t and thread stack.
|
// So we can free mapped space, which includes pthread_internal_t and thread stack.
|
||||||
// First make sure that the kernel does not try to clear the tid field
|
// First make sure that the kernel does not try to clear the tid field
|
||||||
@ -97,28 +100,25 @@ void pthread_exit(void* return_value) {
|
|||||||
__set_tid_address(NULL);
|
__set_tid_address(NULL);
|
||||||
|
|
||||||
// pthread_internal_t is freed below with stack, not here.
|
// pthread_internal_t is freed below with stack, not here.
|
||||||
|
pthread_mutex_lock(&g_thread_list_lock);
|
||||||
_pthread_internal_remove_locked(thread, false);
|
_pthread_internal_remove_locked(thread, false);
|
||||||
free_mapped_space = true;
|
pthread_mutex_unlock(&g_thread_list_lock);
|
||||||
} else {
|
|
||||||
// Mark the thread as exiting without freeing pthread_internal_t.
|
if (thread->mmap_size != 0) {
|
||||||
thread->attr.flags |= PTHREAD_ATTR_FLAG_ZOMBIE;
|
// We need to free mapped space for detached threads when they exit.
|
||||||
|
// That's not something we can do in C.
|
||||||
|
|
||||||
|
// We don't want to take a signal after we've unmapped the stack.
|
||||||
|
// That's one last thing we can handle in C.
|
||||||
|
sigset_t mask;
|
||||||
|
sigfillset(&mask);
|
||||||
|
sigprocmask(SIG_SETMASK, &mask, NULL);
|
||||||
|
|
||||||
|
_exit_with_stack_teardown(thread->attr.stack_base, thread->mmap_size);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
pthread_mutex_unlock(&g_thread_list_lock);
|
|
||||||
|
|
||||||
if (free_mapped_space && thread->mmap_size != 0) {
|
// No need to free mapped space. Either there was no space mapped, or it is left for
|
||||||
// We need to free mapped space for detached threads when they exit.
|
// the pthread_join caller to clean up.
|
||||||
// That's not something we can do in C.
|
__exit(0);
|
||||||
|
|
||||||
// We don't want to take a signal after we've unmapped the stack.
|
|
||||||
// That's one last thing we can handle in C.
|
|
||||||
sigset_t mask;
|
|
||||||
sigfillset(&mask);
|
|
||||||
sigprocmask(SIG_SETMASK, &mask, NULL);
|
|
||||||
|
|
||||||
_exit_with_stack_teardown(thread->attr.stack_base, thread->mmap_size);
|
|
||||||
} else {
|
|
||||||
// No need to free mapped space. Either there was no space mapped, or it is left for
|
|
||||||
// the pthread_join caller to clean up.
|
|
||||||
__exit(0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,7 @@
|
|||||||
#define _PTHREAD_INTERNAL_H_
|
#define _PTHREAD_INTERNAL_H_
|
||||||
|
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
|
#include <stdatomic.h>
|
||||||
|
|
||||||
#include "private/bionic_tls.h"
|
#include "private/bionic_tls.h"
|
||||||
|
|
||||||
@ -46,6 +47,13 @@ struct pthread_key_data_t {
|
|||||||
void* data;
|
void* data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum ThreadJoinState {
|
||||||
|
THREAD_NOT_JOINED,
|
||||||
|
THREAD_EXITED_NOT_JOINED,
|
||||||
|
THREAD_JOINED,
|
||||||
|
THREAD_DETACHED
|
||||||
|
};
|
||||||
|
|
||||||
struct pthread_internal_t {
|
struct pthread_internal_t {
|
||||||
struct pthread_internal_t* next;
|
struct pthread_internal_t* next;
|
||||||
struct pthread_internal_t* prev;
|
struct pthread_internal_t* prev;
|
||||||
@ -74,6 +82,8 @@ struct pthread_internal_t {
|
|||||||
|
|
||||||
pthread_attr_t attr;
|
pthread_attr_t attr;
|
||||||
|
|
||||||
|
_Atomic(ThreadJoinState) join_state;
|
||||||
|
|
||||||
__pthread_cleanup_t* cleanup_stack;
|
__pthread_cleanup_t* cleanup_stack;
|
||||||
|
|
||||||
void* (*start_routine)(void*);
|
void* (*start_routine)(void*);
|
||||||
|
@ -44,16 +44,15 @@ int pthread_join(pthread_t t, void** return_value) {
|
|||||||
return ESRCH;
|
return ESRCH;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((thread->attr.flags & PTHREAD_ATTR_FLAG_DETACHED) != 0) {
|
ThreadJoinState old_state = THREAD_NOT_JOINED;
|
||||||
|
while ((old_state == THREAD_NOT_JOINED || old_state == THREAD_EXITED_NOT_JOINED) &&
|
||||||
|
!atomic_compare_exchange_weak(&thread->join_state, &old_state, THREAD_JOINED)) {
|
||||||
|
}
|
||||||
|
|
||||||
|
if (old_state == THREAD_DETACHED || old_state == THREAD_JOINED) {
|
||||||
return EINVAL;
|
return EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((thread->attr.flags & PTHREAD_ATTR_FLAG_JOINED) != 0) {
|
|
||||||
return EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Okay, looks like we can signal our intention to join.
|
|
||||||
thread->attr.flags |= PTHREAD_ATTR_FLAG_JOINED;
|
|
||||||
tid = thread->tid;
|
tid = thread->tid;
|
||||||
tid_ptr = &thread->tid;
|
tid_ptr = &thread->tid;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user