/* * Copyright (C) 2008 The Android Open Source Project * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include "private/bionic_defs.h" #include "private/ScopedSignalBlocker.h" #include "pthread_internal.h" extern "C" __noreturn void _exit_with_stack_teardown(void*, size_t); extern "C" __noreturn void __exit(int); extern "C" int __set_tid_address(int*); extern "C" void __cxa_thread_finalize(); /* CAVEAT: our implementation of pthread_cleanup_push/pop doesn't support C++ exceptions * and thread cancelation */ __BIONIC_WEAK_FOR_NATIVE_BRIDGE void __pthread_cleanup_push(__pthread_cleanup_t* c, __pthread_cleanup_func_t routine, void* arg) { pthread_internal_t* thread = __get_thread(); c->__cleanup_routine = routine; c->__cleanup_arg = arg; c->__cleanup_prev = thread->cleanup_stack; thread->cleanup_stack = c; } __BIONIC_WEAK_FOR_NATIVE_BRIDGE void __pthread_cleanup_pop(__pthread_cleanup_t* c, int execute) { pthread_internal_t* thread = __get_thread(); thread->cleanup_stack = c->__cleanup_prev; if (execute) { c->__cleanup_routine(c->__cleanup_arg); } } static void __pthread_unmap_tls(pthread_internal_t* thread) { // Unmap the bionic TLS, including guard pages. void* allocation = reinterpret_cast(thread->bionic_tls) - PTHREAD_GUARD_SIZE; munmap(allocation, BIONIC_TLS_SIZE + 2 * PTHREAD_GUARD_SIZE); } __BIONIC_WEAK_FOR_NATIVE_BRIDGE void pthread_exit(void* return_value) { // Call dtors for thread_local objects first. __cxa_thread_finalize(); pthread_internal_t* thread = __get_thread(); thread->return_value = return_value; // Call the cleanup handlers. 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; memset(&ss, 0, sizeof(ss)); ss.ss_flags = SS_DISABLE; sigaltstack(&ss, NULL); // Free it. munmap(thread->alternate_signal_stack, SIGNAL_STACK_SIZE); thread->alternate_signal_stack = NULL; } ThreadJoinState old_state = THREAD_NOT_JOINED; while (old_state == THREAD_NOT_JOINED && !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. // 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 // because we'll have freed the memory before the thread actually exits. __set_tid_address(NULL); // pthread_internal_t is freed below with stack, not here. __pthread_internal_remove(thread); if (thread->mmap_size != 0) { // 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 do before dropping to assembler. ScopedSignalBlocker ssb; __pthread_unmap_tls(thread); _exit_with_stack_teardown(thread->attr.stack_base, thread->mmap_size); } } // No need to free mapped space. Either there was no space mapped, or it is left for // the pthread_join caller to clean up. __pthread_unmap_tls(thread); __exit(0); }