1/*
2 * Copyright (C) 2008 The Android Open Source Project
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *  * Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 *  * Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in
12 *    the documentation and/or other materials provided with the
13 *    distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
18 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
19 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
22 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
25 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29#include <pthread.h>
30
31#include <signal.h>
32#include <stdlib.h>
33#include <sys/mman.h>
34
35#include "pthread_internal.h"
36
37extern "C" __noreturn void _exit_with_stack_teardown(void*, size_t);
38extern "C" __noreturn void __exit(int);
39extern "C" int __set_tid_address(int*);
40extern "C" void __cxa_thread_finalize();
41
42/* CAVEAT: our implementation of pthread_cleanup_push/pop doesn't support C++ exceptions
43 *         and thread cancelation
44 */
45
46void __pthread_cleanup_push(__pthread_cleanup_t* c, __pthread_cleanup_func_t routine, void* arg) {
47  pthread_internal_t* thread = __get_thread();
48  c->__cleanup_routine = routine;
49  c->__cleanup_arg = arg;
50  c->__cleanup_prev = thread->cleanup_stack;
51  thread->cleanup_stack = c;
52}
53
54void __pthread_cleanup_pop(__pthread_cleanup_t* c, int execute) {
55  pthread_internal_t* thread = __get_thread();
56  thread->cleanup_stack = c->__cleanup_prev;
57  if (execute) {
58    c->__cleanup_routine(c->__cleanup_arg);
59  }
60}
61
62void pthread_exit(void* return_value) {
63  // Call dtors for thread_local objects first.
64  __cxa_thread_finalize();
65
66  pthread_internal_t* thread = __get_thread();
67  thread->return_value = return_value;
68
69  // Call the cleanup handlers.
70  while (thread->cleanup_stack) {
71    __pthread_cleanup_t* c = thread->cleanup_stack;
72    thread->cleanup_stack = c->__cleanup_prev;
73    c->__cleanup_routine(c->__cleanup_arg);
74  }
75
76  // Call the TLS destructors. It is important to do that before removing this
77  // thread from the global list. This will ensure that if someone else deletes
78  // a TLS key, the corresponding value will be set to NULL in this thread's TLS
79  // space (see pthread_key_delete).
80  pthread_key_clean_all();
81
82  if (thread->alternate_signal_stack != NULL) {
83    // Tell the kernel to stop using the alternate signal stack.
84    stack_t ss;
85    ss.ss_sp = NULL;
86    ss.ss_flags = SS_DISABLE;
87    sigaltstack(&ss, NULL);
88
89    // Free it.
90    munmap(thread->alternate_signal_stack, SIGNAL_STACK_SIZE);
91    thread->alternate_signal_stack = NULL;
92  }
93
94  ThreadJoinState old_state = THREAD_NOT_JOINED;
95  while (old_state == THREAD_NOT_JOINED &&
96         !atomic_compare_exchange_weak(&thread->join_state, &old_state, THREAD_EXITED_NOT_JOINED)) {
97  }
98
99  if (old_state == THREAD_DETACHED) {
100    // The thread is detached, no one will use pthread_internal_t after pthread_exit.
101    // So we can free mapped space, which includes pthread_internal_t and thread stack.
102    // First make sure that the kernel does not try to clear the tid field
103    // because we'll have freed the memory before the thread actually exits.
104    __set_tid_address(NULL);
105
106    // pthread_internal_t is freed below with stack, not here.
107    __pthread_internal_remove(thread);
108
109    if (thread->mmap_size != 0) {
110      // We need to free mapped space for detached threads when they exit.
111      // That's not something we can do in C.
112
113      // We don't want to take a signal after we've unmapped the stack.
114      // That's one last thing we can handle in C.
115      sigset_t mask;
116      sigfillset(&mask);
117      sigprocmask(SIG_SETMASK, &mask, NULL);
118
119      _exit_with_stack_teardown(thread->attr.stack_base, thread->mmap_size);
120    }
121  }
122
123  // No need to free mapped space. Either there was no space mapped, or it is left for
124  // the pthread_join caller to clean up.
125  __exit(0);
126}
127