1 /*
2 * Copyright (c) 2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "pthread_impl.h"
17
18 __attribute__((__weak__)) extern void add_dso_handle_node(void *dso_handle) ;
19 __attribute__((__weak__)) extern void remove_dso_handle_node(void *dso_handle);
20
21 /*
22 * There are two ways to implement cxa_thread_atexit_impl:
23 * - CXA_THREAD_USE_TSD(default): use pthread_key_xxx to implement cxa_thread_atexit_impl.
24 * - CXA_THREAD_USE_TLS: put dtors in pthread to implement cxa_thread_atexit_impl.
25 */
26 #ifdef CXA_THREAD_USE_TSD
27 struct dtor_list {
28 void (*dtor) (void*);
29 void *arg;
30 void *dso_handle;
31 struct dtor_list* next;
32 };
33
34 // A list for current thread local dtors.
35 __thread struct dtor_list* thread_local_dtors = NULL;
36 // Whether the current thread local dtors have not been executed or registered.
37 __thread bool thread_local_dtors_alive = false;
38 static pthread_key_t dtors_key;
39
run_cur_thread_dtors(void *)40 void run_cur_thread_dtors(void *)
41 {
42 while (thread_local_dtors != NULL) {
43 struct dtor_list* cur = thread_local_dtors;
44 thread_local_dtors = cur->next;
45 cur->dtor(cur->arg);
46 if (remove_dso_handle_node) {
47 remove_dso_handle_node(cur->dso_handle);
48 }
49 __libc_free(cur);
50 }
51 thread_local_dtors_alive = false;
52 return;
53 }
54
cxa_thread_initnull55 __attribute__((constructor())) void cxa_thread_init()
56 {
57 if (pthread_key_create(&dtors_key, run_cur_thread_dtors) != 0) {
58 abort();
59 }
60 return;
61 }
62
63 /*
64 * Used for the thread calls exit(include main thread).
65 * We can't register a destructor of libc for run_cur_thread_dtors because of deadlock problem:
66 * exit -> __libc_exit_fini[acquire init_fini_lock] -> run_cur_thread_dtors ->
67 * remove_dso_handle_node-> do_dlclose ->dlclose_impl[try to get init_fini_lock] -> deadlock.
68 * So we call __cxa_thread_finalize actively at exit.
69 */
__cxa_thread_finalizenull70 void __cxa_thread_finalize()
71 {
72 run_cur_thread_dtors(NULL);
73 return;
74 }
75
__cxa_thread_atexit_impl(void (*func)(void*), void *arg, void *dso_handle)76 int __cxa_thread_atexit_impl(void (*func)(void*), void *arg, void *dso_handle)
77 {
78 if (!thread_local_dtors_alive) {
79 // Bind dtors_key to current thread, so that `run_cur_thread_dtors` can be executed when thread exits.
80 if (pthread_setspecific(dtors_key, &dtors_key) != 0) {
81 return -1;
82 }
83 thread_local_dtors_alive = true;
84 }
85 struct dtor_list* dtor = __libc_malloc(sizeof(*dtor));
86 if (!dtor) {
87 return -1;
88 }
89 dtor->dtor = func;
90 dtor->arg = arg;
91 dtor->dso_handle = dso_handle;
92 dtor->next = thread_local_dtors;
93 thread_local_dtors = dtor;
94 if (add_dso_handle_node != NULL) {
95 add_dso_handle_node(dso_handle);
96 }
97
98 return 0;
99 }
100 #endif
101
102 #ifdef CXA_THREAD_USE_TLS
103
__cxa_thread_atexit_impl(void (*func)(void*), void *arg, void *dso_handle)104 int __cxa_thread_atexit_impl(void (*func)(void*), void *arg, void *dso_handle)
105 {
106 struct thread_local_dtor* dtor = __libc_malloc(sizeof(*dtor));
107 if (!dtor) {
108 return -1;
109 }
110 dtor->func = func;
111 dtor->arg = arg;
112 dtor->dso_handle = dso_handle;
113 pthread_t thr = __pthread_self();
114 dtor->next = thr->thread_local_dtors;
115 thr->thread_local_dtors = dtor;
116 if (add_dso_handle_node != NULL) {
117 add_dso_handle_node(dso_handle);
118 }
119 return 0;
120 }
121
__cxa_thread_finalizenull122 void __cxa_thread_finalize()
123 {
124 pthread_t thr = __pthread_self();
125 while (thr->thread_local_dtors != NULL) {
126 struct thread_local_dtor* cur = thr->thread_local_dtors;
127 thr->thread_local_dtors= cur->next;
128 cur->func(cur->arg);
129 if (remove_dso_handle_node) {
130 remove_dso_handle_node(cur->dso_handle);
131 }
132 }
133 return;
134 }
135
136 #endif
137
138