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
27struct 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;
38static pthread_key_t dtors_key;
39
40void 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
55__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 */
70void __cxa_thread_finalize()
71{
72    run_cur_thread_dtors(NULL);
73    return;
74}
75
76int __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
104int __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
122void __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