-
Notifications
You must be signed in to change notification settings - Fork 86
/
tls_atexit.c
172 lines (157 loc) · 5.37 KB
/
tls_atexit.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
/**
* This file has no copyright assigned and is placed in the Public Domain.
* This file is part of the mingw-w64 runtime package.
* No warranty is given; refer to the file DISCLAIMER.PD within this package.
*/
#include <sect_attribs.h>
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <corecrt_startup.h>
#include <process.h>
typedef void (__thiscall * dtor_fn)(void*);
int __mingw_cxa_atexit(dtor_fn dtor, void *obj, void *dso);
int __mingw_cxa_thread_atexit(dtor_fn dtor, void *obj, void *dso);
typedef struct dtor_obj dtor_obj;
struct dtor_obj {
dtor_fn dtor;
void *obj;
dtor_obj *next;
};
HANDLE __dso_handle;
extern char __mingw_module_is_dll;
static CRITICAL_SECTION lock;
static int inited = 0;
static dtor_obj *global_dtors = NULL;
static DWORD tls_dtors_slot = TLS_OUT_OF_INDEXES;
int __mingw_cxa_atexit(dtor_fn dtor, void *obj, void *dso) {
if (!inited)
return 1;
assert(!dso || dso == &__dso_handle);
dtor_obj *handler = (dtor_obj *) calloc(1, sizeof(*handler));
if (!handler)
return 1;
handler->dtor = dtor;
handler->obj = obj;
EnterCriticalSection(&lock);
handler->next = global_dtors;
global_dtors = handler;
LeaveCriticalSection(&lock);
return 0;
}
static void run_dtor_list(dtor_obj **ptr) {
if (!ptr)
return;
while (*ptr) {
dtor_obj *cur = *ptr;
*ptr = cur->next;
cur->dtor(cur->obj);
free(cur);
}
}
int __mingw_cxa_thread_atexit(dtor_fn dtor, void *obj, void *dso) {
if (!inited)
return 1;
assert(!dso || dso == &__dso_handle);
dtor_obj **head = (dtor_obj **)TlsGetValue(tls_dtors_slot);
if (!head) {
head = (dtor_obj **) calloc(1, sizeof(*head));
if (!head)
return 1;
TlsSetValue(tls_dtors_slot, head);
}
dtor_obj *handler = (dtor_obj *) calloc(1, sizeof(*handler));
if (!handler)
return 1;
handler->dtor = dtor;
handler->obj = obj;
handler->next = *head;
*head = handler;
return 0;
}
static void WINAPI tls_atexit_callback(HANDLE __UNUSED_PARAM(hDllHandle), DWORD dwReason, LPVOID __UNUSED_PARAM(lpReserved)) {
if (dwReason == DLL_PROCESS_DETACH) {
dtor_obj **p = (dtor_obj **)TlsGetValue(tls_dtors_slot);
run_dtor_list(p);
free(p);
TlsSetValue(tls_dtors_slot, NULL);
TlsFree(tls_dtors_slot);
run_dtor_list(&global_dtors);
}
}
static void WINAPI tls_callback(HANDLE hDllHandle, DWORD dwReason, LPVOID __UNUSED_PARAM(lpReserved)) {
dtor_obj **p;
switch (dwReason) {
case DLL_PROCESS_ATTACH:
if (inited == 0) {
InitializeCriticalSection(&lock);
__dso_handle = hDllHandle;
tls_dtors_slot = TlsAlloc();
/*
* We can only call _register_thread_local_exe_atexit_callback once
* in a process; if we call it a second time the process terminates.
* When DLLs are unloaded, this callback is invoked before we run the
* _onexit tables, but for exes, we need to ask this to be called before
* all other registered atexit functions.
* Since we are registered as a normal TLS callback, we will be called
* another time later as well, but that doesn't matter, it's safe to
* invoke this with DLL_PROCESS_DETACH twice.
*/
if (!__mingw_module_is_dll)
_register_thread_local_exe_atexit_callback(tls_atexit_callback);
}
inited = 1;
break;
case DLL_PROCESS_DETACH:
/*
* If there are other threads still running that haven't been detached,
* we don't attempt to run their destructors (MSVC doesn't either), but
* simply leak the destructor list and whatever resources the destructors
* would have released.
*
* From Vista onwards, we could have used FlsAlloc to get a TLS key that
* runs a destructor on each thread that has a value attached ot it, but
* since MSVC doesn't run destructors on other threads in this case,
* users shouldn't assume it and we don't attempt to do anything potentially
* risky about it. TL;DR, threads with pending TLS destructors for a DLL
* need to be joined before unloading the DLL.
*
* This gets called both when exiting cleanly (via exit or returning from
* main, or when a DLL is unloaded), and when exiting bypassing some of
* the cleanup, by calling _exit or ExitProcess. In the latter cases,
* destructors (both TLS and global) in loaded DLLs still get called,
* but none get called for the main executable. This matches what the
* standard says, but differs from what MSVC does with a dynamically
* linked CRT (which still runs TLS destructors for the main thread).
*/
if (__mingw_module_is_dll) {
p = (dtor_obj **)TlsGetValue(tls_dtors_slot);
run_dtor_list(p);
free(p);
TlsSetValue(tls_dtors_slot, NULL);
/* For DLLs, run dtors when detached. For EXEs, run dtors via the
* thread local atexit callback, to make sure they don't run when
* exiting the process with _exit or ExitProcess. */
run_dtor_list(&global_dtors);
TlsFree(tls_dtors_slot);
}
if (inited == 1) {
inited = 0;
DeleteCriticalSection(&lock);
}
break;
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
p = (dtor_obj **)TlsGetValue(tls_dtors_slot);
run_dtor_list(p);
free(p);
TlsSetValue(tls_dtors_slot, NULL);
break;
}
}
_CRTALLOC(".CRT$XLB") PIMAGE_TLS_CALLBACK __xl_b = tls_callback;