void pthread_exit(void * retval) { pthread_internal_t* thread = __get_thread(); void* stack_base = thread->attr.stack_base; int stack_size = thread->attr.stack_size; int user_stack = (thread->attr.flags & PTHREAD_ATTR_FLAG_USER_STACK) != 0; sigset_t mask; // call the cleanup handlers first while (thread->cleanup_stack) { __pthread_cleanup_t* c = thread->cleanup_stack; thread->cleanup_stack = c->__cleanup_prev; c->__cleanup_routine(c->__cleanup_arg); } // call the TLS destructors, it is important to do that before removing this // thread from the global list. this will ensure that if someone else deletes // a TLS key, the corresponding value will be set to NULL in this thread's TLS // space (see pthread_key_delete) pthread_key_clean_all(); // if the thread is detached, destroy the pthread_internal_t // otherwise, keep it in memory and signal any joiners. pthread_mutex_lock(&gThreadListLock); if (thread->attr.flags & PTHREAD_ATTR_FLAG_DETACHED) { _pthread_internal_remove_locked(thread); } else { /* make sure that the thread struct doesn't have stale pointers to a stack that * will be unmapped after the exit call below. */ if (!user_stack) { thread->attr.stack_base = NULL; thread->attr.stack_size = 0; thread->tls = NULL; } /* Indicate that the thread has exited for joining threads. */ thread->attr.flags |= PTHREAD_ATTR_FLAG_ZOMBIE; thread->return_value = retval; /* Signal the joining thread if present. */ if (thread->attr.flags & PTHREAD_ATTR_FLAG_JOINED) { pthread_cond_signal(&thread->join_cond); } } pthread_mutex_unlock(&gThreadListLock); sigfillset(&mask); sigdelset(&mask, SIGSEGV); (void)sigprocmask(SIG_SETMASK, &mask, (sigset_t *)NULL); // destroy the thread stack if (user_stack) _exit_thread((int)retval); else _exit_with_stack_teardown(stack_base, stack_size, (int)retval); }
int pthread_join(pthread_t t, void** return_value) { if (t == pthread_self()) { return EDEADLK; } 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) != 0) { 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_ptr = &thread->tid; } // 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); } // 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()); return 0; }
void pthread_exit(void* return_value) { pthread_internal_t* thread = __get_thread(); ... if ((thread->attr.flags & PTHREAD_ATTR_FLAG_DETACHED) != 0) { // The thread is detached, so we can free the pthread_internal_t. // First make sure that the kernel does not try to clear the tid field // because we'll have freed the memory before the thread actually exits. __set_tid_address(NULL); _pthread_internal_remove_locked(thread); } else { // Make sure that the pthread_internal_t doesn't have stale pointers to a stack that // will be unmapped after the exit call below. if (!user_allocated_stack) { thread->attr.stack_base = NULL; thread->attr.stack_size = 0; thread->tls = NULL; } // pthread_join is responsible for destroying the pthread_internal_t for non-detached threads. // The kernel will futex_wake on the pthread_internal_t::tid field to wake pthread_join. } pthread_mutex_unlock(&g_thread_list_lock); // Perform a second key cleanup. When using jemalloc, a call to free from // _pthread_internal_remove_locked causes the memory associated with a key // to be reallocated. // TODO: When b/16847284 is fixed this call can be removed. pthread_key_clean_all(); if (user_allocated_stack) { // Cleaning up this thread's stack is the creator's responsibility, not ours. __exit(0); } else { // We need to munmap the stack we're running on before calling 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(stack_base, stack_size); } }
int pthread_detach(pthread_t t) { pthread_accessor thread(t); if (thread.get() == NULL) { return ESRCH; } if (thread->attr.flags & PTHREAD_ATTR_FLAG_DETACHED) { return EINVAL; // Already detached. } if (thread->attr.flags & PTHREAD_ATTR_FLAG_JOINED) { return 0; // Already being joined; silently do nothing, like glibc. } if (thread->tid == 0) { // Already exited; clean up. _pthread_internal_remove_locked(thread.get()); return 0; } thread->attr.flags |= PTHREAD_ATTR_FLAG_DETACHED; return 0; }
void pthread_exit(void* return_value) { pthread_internal_t* thread = __get_thread(); thread->return_value = return_value; // Call the cleanup handlers first. while (thread->cleanup_stack) { __pthread_cleanup_t* c = thread->cleanup_stack; thread->cleanup_stack = c->__cleanup_prev; c->__cleanup_routine(c->__cleanup_arg); } // Call the TLS destructors. It is important to do that before removing this // thread from the global list. This will ensure that if someone else deletes // a TLS key, the corresponding value will be set to NULL in this thread's TLS // space (see pthread_key_delete). pthread_key_clean_all(); if (thread->alternate_signal_stack != NULL) { // Tell the kernel to stop using the alternate signal stack. stack_t ss; ss.ss_sp = NULL; ss.ss_flags = SS_DISABLE; sigaltstack(&ss, NULL); // Free it. munmap(thread->alternate_signal_stack, SIGSTKSZ); thread->alternate_signal_stack = NULL; } // Keep track of what we need to know about the stack before we lose the pthread_internal_t. void* stack_base = thread->attr.stack_base; size_t stack_size = thread->attr.stack_size; bool user_allocated_stack = ((thread->attr.flags & PTHREAD_ATTR_FLAG_USER_ALLOCATED_STACK) != 0); pthread_mutex_lock(&gThreadListLock); if ((thread->attr.flags & PTHREAD_ATTR_FLAG_DETACHED) != 0) { // The thread is detached, so we can free the pthread_internal_t. // First make sure that the kernel does not try to clear the tid field // because we'll have freed the memory before the thread actually exits. __set_tid_address(NULL); _pthread_internal_remove_locked(thread); } else { // Make sure that the pthread_internal_t doesn't have stale pointers to a stack that // will be unmapped after the exit call below. if (!user_allocated_stack) { thread->attr.stack_base = NULL; thread->attr.stack_size = 0; thread->tls = NULL; } // pthread_join is responsible for destroying the pthread_internal_t for non-detached threads. // The kernel will futex_wake on the pthread_internal_t::tid field to wake pthread_join. } pthread_mutex_unlock(&gThreadListLock); if (user_allocated_stack) { // Cleaning up this thread's stack is the creator's responsibility, not ours. __exit(0); } else { // We need to munmap the stack we're running on before calling 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(stack_base, stack_size, 0); } // NOTREACHED, but we told the compiler this function is noreturn, and it doesn't believe us. abort(); }
int pthread_join(pthread_t t, void** return_value) { if (t == pthread_self()) { return EDEADLK; } 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) != 0) { 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_ptr = &thread->tid; } // 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) { // ARC MOD BEGIN // Use __nacl_irt_sched_yield instead of __futex_wait. // __nacl_irt_thread_exit does not give us a notice with // futex_wait, so we will yield and poll until thread completes. // // Note that nacl-glibc's has similar code in nptl/pthread_join.c // and sysdeps/nacl/lowlevellock.h. #if defined(HAVE_ARC) __nacl_irt_sched_yield(); #else // ARC MOD END __futex_wait(tid_ptr, tid, NULL); // ARC MOD BEGIN #endif // ARC MOD END } // 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; } // ARC MOD BEGIN // Unmap stack if PTHREAD_ATTR_FLAG_USER_STACK is not // set. Upstream bionic unmaps the stack in thread which are about // to exit, but we cannot do this on NaCl because the stack should // be available when we call __nacl_irt_thread_exit. Instead, we // unmap the stack from the thread which calls pthread_join. #if defined(HAVE_ARC) if (!thread->user_allocated_stack() && thread->attr.stack_base) { if (munmap(thread->attr.stack_base, thread->attr.stack_size) != 0) { static const int kStderrFd = 2; static const char kMsg[] = "failed to unmap the stack!\n"; write(kStderrFd, kMsg, sizeof(kMsg) - 1); abort(); } // Clear the pointer to unmapped stack so pthread_join from // other threads will not try to unmap this region again. thread->attr.stack_base = NULL; thread->attr.stack_size = 0; thread->tls = NULL; } #endif // HAVE_ARC // ARC MOD END _pthread_internal_remove_locked(thread.get()); return 0; }