index e6acc34cec152e04a0444d27f2400313c4e3bc95..e3350efd35169b8efbd3523bf72323d0a41c0928 100644 (file)
#include <errno.h>
+#include "private/bionic_futex.h"
#include "pthread_accessor.h"
-int pthread_join(pthread_t t, void ** ret_val) {
+int pthread_join(pthread_t t, void** return_value) {
if (t == pthread_self()) {
return EDEADLK;
}
- pthread_accessor thread(t);
- if (thread.get() == NULL) {
+ pid_t tid;
+ volatile int* tid_ptr;
+ {
+ pthread_accessor thread(t);
+ if (thread.get() == NULL) {
return ESRCH;
- }
+ }
- if (thread->attr.flags & PTHREAD_ATTR_FLAG_DETACHED) {
- return EINVAL;
- }
+ if ((thread->attr.flags & PTHREAD_ATTR_FLAG_DETACHED) != 0) {
+ return EINVAL;
+ }
- // Wait for thread death when needed.
+ if ((thread->attr.flags & PTHREAD_ATTR_FLAG_JOINED) != 0) {
+ return EINVAL;
+ }
- // If the 'join_count' is negative, this is a 'zombie' thread that
- // is already dead and without stack/TLS. Otherwise, we need to increment 'join-count'
- // and wait to be signaled
- int count = thread->join_count;
- if (count >= 0) {
- thread->join_count += 1;
- pthread_cond_wait(&thread->join_cond, &gThreadListLock);
- count = --thread->join_count;
+ // Okay, looks like we can signal our intention to join.
+ thread->attr.flags |= PTHREAD_ATTR_FLAG_JOINED;
+ tid = thread->tid;
+ tid_ptr = &thread->tid;
}
- if (ret_val) {
- *ret_val = thread->return_value;
+
+ // We set the PTHREAD_ATTR_FLAG_JOINED flag with the lock held,
+ // so no one is going to remove this thread except us.
+
+ // Wait for the thread to actually exit, if it hasn't already.
+ while (*tid_ptr != 0) {
+ __futex_wait(tid_ptr, tid, NULL);
}
- // Remove thread from thread list when we're the last joiner or when the
- // thread was already a zombie.
- if (count <= 0) {
- _pthread_internal_remove_locked(thread.get());
+ // Take the lock again so we can pull the thread's return value
+ // and remove the thread from the list.
+ pthread_accessor thread(t);
+
+ if (return_value) {
+ *return_value = thread->return_value;
}
+
+ _pthread_internal_remove_locked(thread.get(), true);
return 0;
}