blob: aa9bd3832075d0fd2295e2edfcd2d98e539d7622 [file] [log] [blame]
Elliott Hughesc3f11402013-10-30 14:40:09 -07001/*
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 <stdlib.h>
32#include <sys/mman.h>
33
34#include "pthread_internal.h"
35
36extern "C" void _exit_with_stack_teardown(void*, size_t, int);
37extern "C" void __exit(int);
38
39/* CAVEAT: our implementation of pthread_cleanup_push/pop doesn't support C++ exceptions
40 * and thread cancelation
41 */
42
43void __pthread_cleanup_push(__pthread_cleanup_t* c, __pthread_cleanup_func_t routine, void* arg) {
44 pthread_internal_t* thread = __get_thread();
45 c->__cleanup_routine = routine;
46 c->__cleanup_arg = arg;
47 c->__cleanup_prev = thread->cleanup_stack;
48 thread->cleanup_stack = c;
49}
50
51void __pthread_cleanup_pop(__pthread_cleanup_t* c, int execute) {
52 pthread_internal_t* thread = __get_thread();
53 thread->cleanup_stack = c->__cleanup_prev;
54 if (execute) {
55 c->__cleanup_routine(c->__cleanup_arg);
56 }
57}
58
59void pthread_exit(void* retval) {
60 pthread_internal_t* thread = __get_thread();
61
62 // Call the cleanup handlers first.
63 while (thread->cleanup_stack) {
64 __pthread_cleanup_t* c = thread->cleanup_stack;
65 thread->cleanup_stack = c->__cleanup_prev;
66 c->__cleanup_routine(c->__cleanup_arg);
67 }
68
69 // Call the TLS destructors. It is important to do that before removing this
70 // thread from the global list. This will ensure that if someone else deletes
71 // a TLS key, the corresponding value will be set to NULL in this thread's TLS
72 // space (see pthread_key_delete).
73 pthread_key_clean_all();
74
75 if (thread->alternate_signal_stack != NULL) {
76 // Tell the kernel to stop using the alternate signal stack.
77 stack_t ss;
78 ss.ss_sp = NULL;
79 ss.ss_flags = SS_DISABLE;
80 sigaltstack(&ss, NULL);
81
82 // Free it.
83 munmap(thread->alternate_signal_stack, SIGSTKSZ);
84 thread->alternate_signal_stack = NULL;
85 }
86
87 // Keep track of what we need to know about the stack before we lose the pthread_internal_t.
88 void* stack_base = thread->attr.stack_base;
89 size_t stack_size = thread->attr.stack_size;
90 bool user_allocated_stack = ((thread->attr.flags & PTHREAD_ATTR_FLAG_USER_ALLOCATED_STACK) != 0);
91
92 // If the thread is detached, destroy the pthread_internal_t,
93 // otherwise keep it in memory and signal any joiners.
94 pthread_mutex_lock(&gThreadListLock);
95 if (thread->attr.flags & PTHREAD_ATTR_FLAG_DETACHED) {
96 _pthread_internal_remove_locked(thread);
97 } else {
98 // Make sure that the pthread_internal_t doesn't have stale pointers to a stack that
99 // will be unmapped after the exit call below.
100 if (!user_allocated_stack) {
101 thread->attr.stack_base = NULL;
102 thread->attr.stack_size = 0;
103 thread->tls = NULL;
104 }
105
106 // Indicate that the thread has exited for joining threads.
107 thread->attr.flags |= PTHREAD_ATTR_FLAG_ZOMBIE;
108 thread->return_value = retval;
109
110 // Signal the joining thread if present.
111 if (thread->attr.flags & PTHREAD_ATTR_FLAG_JOINED) {
112 pthread_cond_signal(&thread->join_cond);
113 }
114 }
115 pthread_mutex_unlock(&gThreadListLock);
116
117 if (user_allocated_stack) {
118 // Cleaning up this thread's stack is the creator's responsibility, not ours.
119 __exit(0);
120 } else {
121 // We need to munmap the stack we're running on before calling exit.
122 // That's not something we can do in C.
123
124 // We don't want to take a signal after we've unmapped the stack.
125 // That's one last thing we can handle in C.
126 sigset_t mask;
127 sigfillset(&mask);
128 sigprocmask(SIG_SETMASK, &mask, NULL);
129
130 _exit_with_stack_teardown(stack_base, stack_size, 0);
131 }
132
133 /* NOTREACHED, but we told the compiler this function is noreturn, and it doesn't believe us. */
134 abort();
135}