]> git.proxmox.com Git - libgit2.git/blob - src/global.c
Rename routine to free TLS data
[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 "git2/global.h"
12 #include "git2/sys/openssl.h"
13 #include "thread-utils.h"
14
15
16 git_mutex git__mwindow_mutex;
17
18 #define MAX_SHUTDOWN_CB 8
19
20 #ifdef GIT_SSL
21 # include <openssl/ssl.h>
22 SSL_CTX *git__ssl_ctx;
23 # ifdef GIT_THREADS
24 static git_mutex *openssl_locks;
25 # endif
26 #endif
27
28 static git_global_shutdown_fn git__shutdown_callbacks[MAX_SHUTDOWN_CB];
29 static git_atomic git__n_shutdown_callbacks;
30 static git_atomic git__n_inits;
31
32 void git__on_shutdown(git_global_shutdown_fn callback)
33 {
34 int count = git_atomic_inc(&git__n_shutdown_callbacks);
35 assert(count <= MAX_SHUTDOWN_CB && count > 0);
36 git__shutdown_callbacks[count - 1] = callback;
37 }
38
39 static void git__global_state_cleanup(git_global_st *st)
40 {
41 if (!st)
42 return;
43
44 git__free(st->error_t.message);
45 st->error_t.message = NULL;
46 }
47
48 static void git__shutdown(void)
49 {
50 int pos;
51
52 /* Shutdown subsystems that have registered */
53 for (pos = git_atomic_get(&git__n_shutdown_callbacks); pos > 0; pos = git_atomic_dec(&git__n_shutdown_callbacks)) {
54 git_global_shutdown_fn cb = git__swap(git__shutdown_callbacks[pos - 1], NULL);
55 if (cb != NULL)
56 cb();
57 }
58 }
59
60 #if defined(GIT_THREADS) && defined(GIT_SSL)
61 void openssl_locking_function(int mode, int n, const char *file, int line)
62 {
63 int lock;
64
65 GIT_UNUSED(file);
66 GIT_UNUSED(line);
67
68 lock = mode & CRYPTO_LOCK;
69
70 if (lock) {
71 git_mutex_lock(&openssl_locks[n]);
72 } else {
73 git_mutex_unlock(&openssl_locks[n]);
74 }
75 }
76
77 static void shutdown_ssl_locking(void)
78 {
79 int num_locks, i;
80
81 num_locks = CRYPTO_num_locks();
82 CRYPTO_set_locking_callback(NULL);
83
84 for (i = 0; i < num_locks; ++i)
85 git_mutex_free(openssl_locks);
86 git__free(openssl_locks);
87 }
88 #endif
89
90 static void init_ssl(void)
91 {
92 #ifdef GIT_SSL
93 long ssl_opts = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3;
94
95 /* Older OpenSSL and MacOS OpenSSL doesn't have this */
96 #ifdef SSL_OP_NO_COMPRESSION
97 ssl_opts |= SSL_OP_NO_COMPRESSION;
98 #endif
99
100 SSL_load_error_strings();
101 OpenSSL_add_ssl_algorithms();
102 /*
103 * Load SSLv{2,3} and TLSv1 so that we can talk with servers
104 * which use the SSL hellos, which are often used for
105 * compatibility. We then disable SSL so we only allow OpenSSL
106 * to speak TLSv1 to perform the encryption itself.
107 */
108 git__ssl_ctx = SSL_CTX_new(SSLv23_method());
109 SSL_CTX_set_options(git__ssl_ctx, ssl_opts);
110 SSL_CTX_set_mode(git__ssl_ctx, SSL_MODE_AUTO_RETRY);
111 SSL_CTX_set_verify(git__ssl_ctx, SSL_VERIFY_NONE, NULL);
112 if (!SSL_CTX_set_default_verify_paths(git__ssl_ctx)) {
113 SSL_CTX_free(git__ssl_ctx);
114 git__ssl_ctx = NULL;
115 }
116 #endif
117 }
118
119 int git_openssl_set_locking(void)
120 {
121 #ifdef GIT_SSL
122 # ifdef GIT_THREADS
123 int num_locks, i;
124
125 num_locks = CRYPTO_num_locks();
126 openssl_locks = git__calloc(num_locks, sizeof(git_mutex));
127 GITERR_CHECK_ALLOC(openssl_locks);
128
129 for (i = 0; i < num_locks; i++) {
130 if (git_mutex_init(&openssl_locks[i]) != 0) {
131 giterr_set(GITERR_SSL, "failed to initialize openssl locks");
132 return -1;
133 }
134 }
135
136 CRYPTO_set_locking_callback(openssl_locking_function);
137 git__on_shutdown(shutdown_ssl_locking);
138 return 0;
139 # else
140 giterr_set(GITERR_THREAD, "libgit2 as not built with threads");
141 return -1;
142 # endif
143 #else
144 giterr_set(GITERR_SSL, "libgit2 was not built with OpenSSL support");
145 return -1;
146 #endif
147 }
148
149 /**
150 * Handle the global state with TLS
151 *
152 * If libgit2 is built with GIT_THREADS enabled,
153 * the `git_libgit2_init()` function must be called
154 * before calling any other function of the library.
155 *
156 * This function allocates a TLS index (using pthreads
157 * or the native Win32 API) to store the global state
158 * on a per-thread basis.
159 *
160 * Any internal method that requires global state will
161 * then call `git__global_state()` which returns a pointer
162 * to the global state structure; this pointer is lazily
163 * allocated on each thread.
164 *
165 * Before shutting down the library, the
166 * `git_libgit2_shutdown` method must be called to free
167 * the previously reserved TLS index.
168 *
169 * If libgit2 is built without threading support, the
170 * `git__global_statestate()` call returns a pointer to a single,
171 * statically allocated global state. The `git_thread_`
172 * functions are not available in that case.
173 */
174
175 /*
176 * `git_libgit2_init()` allows subsystems to perform global setup,
177 * which may take place in the global scope. An explicit memory
178 * fence exists at the exit of `git_libgit2_init()`. Without this,
179 * CPU cores are free to reorder cache invalidation of `_tls_init`
180 * before cache invalidation of the subsystems' newly written global
181 * state.
182 */
183 #if defined(GIT_THREADS) && defined(GIT_WIN32)
184
185 static DWORD _tls_index;
186 static volatile LONG _mutex = 0;
187
188 static int synchronized_threads_init(void)
189 {
190 int error;
191
192 _tls_index = TlsAlloc();
193 if (git_mutex_init(&git__mwindow_mutex))
194 return -1;
195
196 /* Initialize any other subsystems that have global state */
197 if ((error = git_hash_global_init()) >= 0)
198 error = git_sysdir_global_init();
199
200 win32_pthread_initialize();
201
202 return error;
203 }
204
205 int git_libgit2_init(void)
206 {
207 int ret;
208
209 /* Enter the lock */
210 while (InterlockedCompareExchange(&_mutex, 1, 0)) { Sleep(0); }
211
212 /* Only do work on a 0 -> 1 transition of the refcount */
213 if ((ret = git_atomic_inc(&git__n_inits)) == 1) {
214 if (synchronized_threads_init() < 0)
215 ret = -1;
216 }
217
218 /* Exit the lock */
219 InterlockedExchange(&_mutex, 0);
220
221 return ret;
222 }
223
224 static void synchronized_threads_shutdown(void)
225 {
226 /* Shut down any subsystems that have global state */
227 git__shutdown();
228
229 git__free_tls_data();
230
231 TlsFree(_tls_index);
232 git_mutex_free(&git__mwindow_mutex);
233 }
234
235 int git_libgit2_shutdown(void)
236 {
237 int ret;
238
239 /* Enter the lock */
240 while (InterlockedCompareExchange(&_mutex, 1, 0)) { Sleep(0); }
241
242 /* Only do work on a 1 -> 0 transition of the refcount */
243 if ((ret = git_atomic_dec(&git__n_inits)) == 0)
244 synchronized_threads_shutdown();
245
246 /* Exit the lock */
247 InterlockedExchange(&_mutex, 0);
248
249 return ret;
250 }
251
252 git_global_st *git__global_state(void)
253 {
254 void *ptr;
255
256 assert(git_atomic_get(&git__n_inits) > 0);
257
258 if ((ptr = TlsGetValue(_tls_index)) != NULL)
259 return ptr;
260
261 ptr = git__malloc(sizeof(git_global_st));
262 if (!ptr)
263 return NULL;
264
265 memset(ptr, 0x0, sizeof(git_global_st));
266 TlsSetValue(_tls_index, ptr);
267 return ptr;
268 }
269
270 /**
271 * Free the TLS data associated with this thread.
272 * This should only be used by the thread as it
273 * is exiting.
274 */
275 void git__free_tls_data(void)
276 {
277 void *ptr = TlsGetValue(_tls_index);
278 if (!ptr)
279 return;
280
281 git__global_state_cleanup(ptr);
282 git__free(ptr);
283 TlsSetValue(_tls_index, NULL);
284 }
285
286 #elif defined(GIT_THREADS) && defined(_POSIX_THREADS)
287
288 static pthread_key_t _tls_key;
289 static pthread_once_t _once_init = PTHREAD_ONCE_INIT;
290 int init_error = 0;
291
292 static void cb__free_status(void *st)
293 {
294 git__global_state_cleanup(st);
295 git__free(st);
296 }
297
298 static void init_once(void)
299 {
300 if ((init_error = git_mutex_init(&git__mwindow_mutex)) != 0)
301 return;
302 pthread_key_create(&_tls_key, &cb__free_status);
303
304
305 /* Initialize any other subsystems that have global state */
306 if ((init_error = git_hash_global_init()) >= 0)
307 init_error = git_sysdir_global_init();
308
309 /* OpenSSL needs to be initialized from the main thread */
310 init_ssl();
311
312 GIT_MEMORY_BARRIER;
313 }
314
315 int git_libgit2_init(void)
316 {
317 int ret;
318
319 pthread_once(&_once_init, init_once);
320 ret = git_atomic_inc(&git__n_inits);
321
322 return init_error ? init_error : ret;
323 }
324
325 int git_libgit2_shutdown(void)
326 {
327 void *ptr = NULL;
328 pthread_once_t new_once = PTHREAD_ONCE_INIT;
329 int ret;
330
331 if ((ret = git_atomic_dec(&git__n_inits)) != 0)
332 return ret;
333
334 /* Shut down any subsystems that have global state */
335 git__shutdown();
336
337 ptr = pthread_getspecific(_tls_key);
338 pthread_setspecific(_tls_key, NULL);
339
340 git__global_state_cleanup(ptr);
341 git__free(ptr);
342
343 pthread_key_delete(_tls_key);
344 git_mutex_free(&git__mwindow_mutex);
345 _once_init = new_once;
346
347 return 0;
348 }
349
350 git_global_st *git__global_state(void)
351 {
352 void *ptr;
353
354 assert(git_atomic_get(&git__n_inits) > 0);
355
356 if ((ptr = pthread_getspecific(_tls_key)) != NULL)
357 return ptr;
358
359 ptr = git__malloc(sizeof(git_global_st));
360 if (!ptr)
361 return NULL;
362
363 memset(ptr, 0x0, sizeof(git_global_st));
364 pthread_setspecific(_tls_key, ptr);
365 return ptr;
366 }
367
368 #else
369
370 static git_global_st __state;
371
372 int git_libgit2_init(void)
373 {
374 static int ssl_inited = 0;
375
376 if (!ssl_inited) {
377 init_ssl();
378 ssl_inited = 1;
379 }
380
381 return git_atomic_inc(&git__n_inits);
382 }
383
384 int git_libgit2_shutdown(void)
385 {
386 int ret;
387
388 /* Shut down any subsystems that have global state */
389 if ((ret = git_atomic_dec(&git__n_inits)) != 0)
390 return ret;
391
392 git__shutdown();
393 git__global_state_cleanup(&__state);
394
395 return 0;
396 }
397
398 git_global_st *git__global_state(void)
399 {
400 return &__state;
401 }
402
403 #endif /* GIT_THREADS */