1570af302Sopenharmony_ci/*
2570af302Sopenharmony_ci * Copyright (c) 2024 Huawei Device Co., Ltd.
3570af302Sopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License");
4570af302Sopenharmony_ci * you may not use this file except in compliance with the License.
5570af302Sopenharmony_ci * You may obtain a copy of the License at
6570af302Sopenharmony_ci *
7570af302Sopenharmony_ci * http://www.apache.org/licenses/LICENSE-2.0
8570af302Sopenharmony_ci *
9570af302Sopenharmony_ci * Unless required by applicable law or agreed to in writing, software
10570af302Sopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS,
11570af302Sopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12570af302Sopenharmony_ci * See the License for the specific language governing permissions and
13570af302Sopenharmony_ci * limitations under the License.
14570af302Sopenharmony_ci */
15570af302Sopenharmony_ci
16570af302Sopenharmony_ci#include "pthread_impl.h"
17570af302Sopenharmony_ci
18570af302Sopenharmony_ci__attribute__((__weak__)) extern void add_dso_handle_node(void *dso_handle) ;
19570af302Sopenharmony_ci__attribute__((__weak__)) extern void remove_dso_handle_node(void *dso_handle);
20570af302Sopenharmony_ci
21570af302Sopenharmony_ci/*
22570af302Sopenharmony_ci * There are two ways to implement cxa_thread_atexit_impl:
23570af302Sopenharmony_ci * - CXA_THREAD_USE_TSD(default): use pthread_key_xxx to implement cxa_thread_atexit_impl.
24570af302Sopenharmony_ci * - CXA_THREAD_USE_TLS: put dtors in pthread to implement cxa_thread_atexit_impl.
25570af302Sopenharmony_ci */
26570af302Sopenharmony_ci#ifdef CXA_THREAD_USE_TSD
27570af302Sopenharmony_cistruct dtor_list {
28570af302Sopenharmony_ci    void (*dtor) (void*);
29570af302Sopenharmony_ci    void *arg;
30570af302Sopenharmony_ci    void *dso_handle;
31570af302Sopenharmony_ci    struct dtor_list* next;
32570af302Sopenharmony_ci};
33570af302Sopenharmony_ci
34570af302Sopenharmony_ci// A list for current thread local dtors.
35570af302Sopenharmony_ci__thread struct dtor_list* thread_local_dtors = NULL;
36570af302Sopenharmony_ci// Whether the current thread local dtors have not been executed or registered.
37570af302Sopenharmony_ci__thread bool thread_local_dtors_alive = false;
38570af302Sopenharmony_cistatic pthread_key_t dtors_key;
39570af302Sopenharmony_ci
40570af302Sopenharmony_civoid run_cur_thread_dtors(void *)
41570af302Sopenharmony_ci{
42570af302Sopenharmony_ci    while (thread_local_dtors != NULL) {
43570af302Sopenharmony_ci        struct dtor_list* cur = thread_local_dtors;
44570af302Sopenharmony_ci        thread_local_dtors = cur->next;
45570af302Sopenharmony_ci        cur->dtor(cur->arg);
46570af302Sopenharmony_ci        if (remove_dso_handle_node) {
47570af302Sopenharmony_ci            remove_dso_handle_node(cur->dso_handle);
48570af302Sopenharmony_ci        }
49570af302Sopenharmony_ci        __libc_free(cur);
50570af302Sopenharmony_ci    }
51570af302Sopenharmony_ci    thread_local_dtors_alive = false;
52570af302Sopenharmony_ci    return;
53570af302Sopenharmony_ci}
54570af302Sopenharmony_ci
55570af302Sopenharmony_ci__attribute__((constructor())) void cxa_thread_init()
56570af302Sopenharmony_ci{
57570af302Sopenharmony_ci    if (pthread_key_create(&dtors_key, run_cur_thread_dtors) != 0) {
58570af302Sopenharmony_ci        abort();
59570af302Sopenharmony_ci    }
60570af302Sopenharmony_ci    return;
61570af302Sopenharmony_ci}
62570af302Sopenharmony_ci
63570af302Sopenharmony_ci/*
64570af302Sopenharmony_ci * Used for the thread calls exit(include main thread).
65570af302Sopenharmony_ci * We can't register a destructor of libc for run_cur_thread_dtors because of deadlock problem:
66570af302Sopenharmony_ci *   exit -> __libc_exit_fini[acquire init_fini_lock] -> run_cur_thread_dtors ->
67570af302Sopenharmony_ci *   remove_dso_handle_node-> do_dlclose ->dlclose_impl[try to get init_fini_lock] -> deadlock.
68570af302Sopenharmony_ci * So we call __cxa_thread_finalize actively at exit.
69570af302Sopenharmony_ci */
70570af302Sopenharmony_civoid __cxa_thread_finalize()
71570af302Sopenharmony_ci{
72570af302Sopenharmony_ci    run_cur_thread_dtors(NULL);
73570af302Sopenharmony_ci    return;
74570af302Sopenharmony_ci}
75570af302Sopenharmony_ci
76570af302Sopenharmony_ciint __cxa_thread_atexit_impl(void (*func)(void*), void *arg, void *dso_handle)
77570af302Sopenharmony_ci{
78570af302Sopenharmony_ci    if (!thread_local_dtors_alive) {
79570af302Sopenharmony_ci        // Bind dtors_key to current thread, so that `run_cur_thread_dtors` can be executed when thread exits.
80570af302Sopenharmony_ci        if (pthread_setspecific(dtors_key, &dtors_key) != 0) {
81570af302Sopenharmony_ci            return -1;
82570af302Sopenharmony_ci        }
83570af302Sopenharmony_ci        thread_local_dtors_alive = true;
84570af302Sopenharmony_ci    }
85570af302Sopenharmony_ci    struct dtor_list* dtor = __libc_malloc(sizeof(*dtor));
86570af302Sopenharmony_ci    if (!dtor) {
87570af302Sopenharmony_ci        return -1;
88570af302Sopenharmony_ci    }
89570af302Sopenharmony_ci    dtor->dtor = func;
90570af302Sopenharmony_ci    dtor->arg = arg;
91570af302Sopenharmony_ci    dtor->dso_handle = dso_handle;
92570af302Sopenharmony_ci    dtor->next = thread_local_dtors;
93570af302Sopenharmony_ci    thread_local_dtors = dtor;
94570af302Sopenharmony_ci    if (add_dso_handle_node != NULL) {
95570af302Sopenharmony_ci        add_dso_handle_node(dso_handle);
96570af302Sopenharmony_ci    }
97570af302Sopenharmony_ci
98570af302Sopenharmony_ci    return 0;
99570af302Sopenharmony_ci}
100570af302Sopenharmony_ci#endif
101570af302Sopenharmony_ci
102570af302Sopenharmony_ci#ifdef CXA_THREAD_USE_TLS
103570af302Sopenharmony_ci
104570af302Sopenharmony_ciint __cxa_thread_atexit_impl(void (*func)(void*), void *arg, void *dso_handle)
105570af302Sopenharmony_ci{
106570af302Sopenharmony_ci    struct thread_local_dtor* dtor = __libc_malloc(sizeof(*dtor));
107570af302Sopenharmony_ci    if (!dtor) {
108570af302Sopenharmony_ci        return -1;
109570af302Sopenharmony_ci    }
110570af302Sopenharmony_ci    dtor->func = func;
111570af302Sopenharmony_ci    dtor->arg = arg;
112570af302Sopenharmony_ci    dtor->dso_handle = dso_handle;
113570af302Sopenharmony_ci    pthread_t thr = __pthread_self();
114570af302Sopenharmony_ci    dtor->next = thr->thread_local_dtors;
115570af302Sopenharmony_ci    thr->thread_local_dtors = dtor;
116570af302Sopenharmony_ci    if (add_dso_handle_node != NULL) {
117570af302Sopenharmony_ci        add_dso_handle_node(dso_handle);
118570af302Sopenharmony_ci    }
119570af302Sopenharmony_ci    return 0;
120570af302Sopenharmony_ci}
121570af302Sopenharmony_ci
122570af302Sopenharmony_civoid __cxa_thread_finalize()
123570af302Sopenharmony_ci{
124570af302Sopenharmony_ci    pthread_t thr = __pthread_self();
125570af302Sopenharmony_ci    while (thr->thread_local_dtors != NULL) {
126570af302Sopenharmony_ci        struct thread_local_dtor* cur = thr->thread_local_dtors;
127570af302Sopenharmony_ci        thr->thread_local_dtors= cur->next;
128570af302Sopenharmony_ci        cur->func(cur->arg);
129570af302Sopenharmony_ci        if (remove_dso_handle_node) {
130570af302Sopenharmony_ci            remove_dso_handle_node(cur->dso_handle);
131570af302Sopenharmony_ci        }
132570af302Sopenharmony_ci    }
133570af302Sopenharmony_ci    return;
134570af302Sopenharmony_ci}
135570af302Sopenharmony_ci
136570af302Sopenharmony_ci#endif
137570af302Sopenharmony_ci
138