]> git.proxmox.com Git - libgit2.git/blob - src/global.c
win32: clean up unused warnings in DllMain
[libgit2.git] / src / global.c
1 /*
2 * Copyright (C) the libgit2 contributors. All rights reserved.
3 *
4 * This file is part of libgit2, distributed under the GNU GPL v2 with
5 * a Linking Exception. For full terms see the included COPYING file.
6 */
7 #include "common.h"
8 #include "global.h"
9 #include "hash.h"
10 #include "sysdir.h"
11 #include "filter.h"
12 #include "merge_driver.h"
13 #include "openssl_stream.h"
14 #include "thread-utils.h"
15 #include "git2/global.h"
16 #include "transports/ssh.h"
17
18 #if defined(GIT_MSVC_CRTDBG)
19 #include "win32/w32_stack.h"
20 #include "win32/w32_crtdbg_stacktrace.h"
21 #endif
22
23 git_mutex git__mwindow_mutex;
24
25 #define MAX_SHUTDOWN_CB 8
26
27 static git_global_shutdown_fn git__shutdown_callbacks[MAX_SHUTDOWN_CB];
28 static git_atomic git__n_shutdown_callbacks;
29 static git_atomic git__n_inits;
30 char *git__user_agent;
31 char *git__ssl_ciphers;
32
33 void git__on_shutdown(git_global_shutdown_fn callback)
34 {
35 int count = git_atomic_inc(&git__n_shutdown_callbacks);
36 assert(count <= MAX_SHUTDOWN_CB && count > 0);
37 git__shutdown_callbacks[count - 1] = callback;
38 }
39
40 static void git__global_state_cleanup(git_global_st *st)
41 {
42 if (!st)
43 return;
44
45 git__free(st->error_t.message);
46 st->error_t.message = NULL;
47 }
48
49 static int init_common(void)
50 {
51 int ret;
52
53 /* Initialize the CRT debug allocator first, before our first malloc */
54 #if defined(GIT_MSVC_CRTDBG)
55 git_win32__crtdbg_stacktrace_init();
56 git_win32__stack_init();
57 #endif
58
59 /* Initialize any other subsystems that have global state */
60 if ((ret = git_hash_global_init()) == 0 &&
61 (ret = git_sysdir_global_init()) == 0 &&
62 (ret = git_filter_global_init()) == 0 &&
63 (ret = git_merge_driver_global_init()) == 0 &&
64 (ret = git_transport_ssh_global_init()) == 0)
65 ret = git_openssl_stream_global_init();
66
67 GIT_MEMORY_BARRIER;
68
69 return ret;
70 }
71
72 static void shutdown_common(void)
73 {
74 int pos;
75
76 /* Shutdown subsystems that have registered */
77 for (pos = git_atomic_get(&git__n_shutdown_callbacks);
78 pos > 0;
79 pos = git_atomic_dec(&git__n_shutdown_callbacks)) {
80
81 git_global_shutdown_fn cb = git__swap(
82 git__shutdown_callbacks[pos - 1], NULL);
83
84 if (cb != NULL)
85 cb();
86 }
87
88 git__free(git__user_agent);
89 git__free(git__ssl_ciphers);
90
91 #if defined(GIT_MSVC_CRTDBG)
92 git_win32__crtdbg_stacktrace_cleanup();
93 git_win32__stack_cleanup();
94 #endif
95 }
96
97 /**
98 * Handle the global state with TLS
99 *
100 * If libgit2 is built with GIT_THREADS enabled,
101 * the `git_libgit2_init()` function must be called
102 * before calling any other function of the library.
103 *
104 * This function allocates a TLS index (using pthreads
105 * or the native Win32 API) to store the global state
106 * on a per-thread basis.
107 *
108 * Any internal method that requires global state will
109 * then call `git__global_state()` which returns a pointer
110 * to the global state structure; this pointer is lazily
111 * allocated on each thread.
112 *
113 * Before shutting down the library, the
114 * `git_libgit2_shutdown` method must be called to free
115 * the previously reserved TLS index.
116 *
117 * If libgit2 is built without threading support, the
118 * `git__global_statestate()` call returns a pointer to a single,
119 * statically allocated global state. The `git_thread_`
120 * functions are not available in that case.
121 */
122
123 /*
124 * `git_libgit2_init()` allows subsystems to perform global setup,
125 * which may take place in the global scope. An explicit memory
126 * fence exists at the exit of `git_libgit2_init()`. Without this,
127 * CPU cores are free to reorder cache invalidation of `_tls_init`
128 * before cache invalidation of the subsystems' newly written global
129 * state.
130 */
131 #if defined(GIT_THREADS) && defined(GIT_WIN32)
132
133 static DWORD _tls_index;
134 static volatile LONG _mutex = 0;
135
136 static int synchronized_threads_init(void)
137 {
138 int error;
139
140 _tls_index = TlsAlloc();
141
142 win32_pthread_initialize();
143
144 if (git_mutex_init(&git__mwindow_mutex))
145 return -1;
146
147 error = init_common();
148
149 return error;
150 }
151
152 int git_libgit2_init(void)
153 {
154 int ret;
155
156 /* Enter the lock */
157 while (InterlockedCompareExchange(&_mutex, 1, 0)) { Sleep(0); }
158
159 /* Only do work on a 0 -> 1 transition of the refcount */
160 if ((ret = git_atomic_inc(&git__n_inits)) == 1) {
161 if (synchronized_threads_init() < 0)
162 ret = -1;
163 }
164
165 /* Exit the lock */
166 InterlockedExchange(&_mutex, 0);
167
168 return ret;
169 }
170
171 int git_libgit2_shutdown(void)
172 {
173 int ret;
174
175 /* Enter the lock */
176 while (InterlockedCompareExchange(&_mutex, 1, 0)) { Sleep(0); }
177
178 /* Only do work on a 1 -> 0 transition of the refcount */
179 if ((ret = git_atomic_dec(&git__n_inits)) == 0) {
180 shutdown_common();
181
182 git__free_tls_data();
183
184 TlsFree(_tls_index);
185 git_mutex_free(&git__mwindow_mutex);
186 }
187
188 /* Exit the lock */
189 InterlockedExchange(&_mutex, 0);
190
191 return ret;
192 }
193
194 git_global_st *git__global_state(void)
195 {
196 git_global_st *ptr;
197
198 assert(git_atomic_get(&git__n_inits) > 0);
199
200 if ((ptr = TlsGetValue(_tls_index)) != NULL)
201 return ptr;
202
203 ptr = git__calloc(1, sizeof(git_global_st));
204 if (!ptr)
205 return NULL;
206
207 git_buf_init(&ptr->error_buf, 0);
208
209 TlsSetValue(_tls_index, ptr);
210 return ptr;
211 }
212
213 /**
214 * Free the TLS data associated with this thread.
215 * This should only be used by the thread as it
216 * is exiting.
217 */
218 void git__free_tls_data(void)
219 {
220 void *ptr = TlsGetValue(_tls_index);
221 if (!ptr)
222 return;
223
224 git__global_state_cleanup(ptr);
225 git__free(ptr);
226 TlsSetValue(_tls_index, NULL);
227 }
228
229 BOOL WINAPI DllMain(HINSTANCE hInstDll, DWORD fdwReason, LPVOID lpvReserved)
230 {
231 GIT_UNUSED(hInstDll);
232 GIT_UNUSED(lpvReserved);
233
234 /* This is how Windows lets us know our thread is being shut down */
235 if (fdwReason == DLL_THREAD_DETACH) {
236 git__free_tls_data();
237 }
238
239 /*
240 * Windows pays attention to this during library loading. We don't do anything
241 * so we trivially succeed.
242 */
243 return TRUE;
244 }
245
246 #elif defined(GIT_THREADS) && defined(_POSIX_THREADS)
247
248 static pthread_key_t _tls_key;
249 static pthread_once_t _once_init = PTHREAD_ONCE_INIT;
250 int init_error = 0;
251
252 static void cb__free_status(void *st)
253 {
254 git__global_state_cleanup(st);
255 git__free(st);
256 }
257
258 static void init_once(void)
259 {
260 if ((init_error = git_mutex_init(&git__mwindow_mutex)) != 0)
261 return;
262
263 pthread_key_create(&_tls_key, &cb__free_status);
264
265 init_error = init_common();
266 }
267
268 int git_libgit2_init(void)
269 {
270 int ret;
271
272 ret = git_atomic_inc(&git__n_inits);
273 pthread_once(&_once_init, init_once);
274
275 return init_error ? init_error : ret;
276 }
277
278 int git_libgit2_shutdown(void)
279 {
280 void *ptr = NULL;
281 pthread_once_t new_once = PTHREAD_ONCE_INIT;
282 int ret;
283
284 if ((ret = git_atomic_dec(&git__n_inits)) != 0)
285 return ret;
286
287 /* Shut down any subsystems that have global state */
288 shutdown_common();
289
290 ptr = pthread_getspecific(_tls_key);
291 pthread_setspecific(_tls_key, NULL);
292
293 git__global_state_cleanup(ptr);
294 git__free(ptr);
295
296 pthread_key_delete(_tls_key);
297 git_mutex_free(&git__mwindow_mutex);
298 _once_init = new_once;
299
300 return 0;
301 }
302
303 git_global_st *git__global_state(void)
304 {
305 git_global_st *ptr;
306
307 assert(git_atomic_get(&git__n_inits) > 0);
308
309 if ((ptr = pthread_getspecific(_tls_key)) != NULL)
310 return ptr;
311
312 ptr = git__calloc(1, sizeof(git_global_st));
313 if (!ptr)
314 return NULL;
315
316 git_buf_init(&ptr->error_buf, 0);
317 pthread_setspecific(_tls_key, ptr);
318 return ptr;
319 }
320
321 #else
322
323 static git_global_st __state;
324
325 int git_libgit2_init(void)
326 {
327 int ret;
328
329 /* Only init SSL the first time */
330 if ((ret = git_atomic_inc(&git__n_inits)) != 1)
331 return ret;
332
333 if ((ret = init_common()) < 0)
334 return ret;
335
336 return 1;
337 }
338
339 int git_libgit2_shutdown(void)
340 {
341 int ret;
342
343 /* Shut down any subsystems that have global state */
344 if ((ret = git_atomic_dec(&git__n_inits)) == 0) {
345 shutdown_common();
346 git__global_state_cleanup(&__state);
347 }
348
349 return ret;
350 }
351
352 git_global_st *git__global_state(void)
353 {
354 return &__state;
355 }
356
357 #endif /* GIT_THREADS */