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); }
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); } }
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(); }