]> git.proxmox.com Git - libgit2.git/blame - src/global.c
push: remove own copy of callbacks
[libgit2.git] / src / global.c
CommitLineData
a15c550d 1/*
359fc2d2 2 * Copyright (C) the libgit2 contributors. All rights reserved.
a15c550d
VM
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"
7ebefd22 9#include "hash.h"
83634d38 10#include "sysdir.h"
799e22ea 11#include "git2/global.h"
73f0278e 12#include "git2/sys/openssl.h"
a15c550d
VM
13#include "thread-utils.h"
14
8cef828d
CMN
15
16git_mutex git__mwindow_mutex;
17
a3aa5f4d
RB
18#define MAX_SHUTDOWN_CB 8
19
24e53d2f 20#ifdef GIT_OPENSSL
cf15ac8a
CMN
21# include <openssl/ssl.h>
22SSL_CTX *git__ssl_ctx;
f59a34d2 23# ifdef GIT_THREADS
081e76ba 24static git_mutex *openssl_locks;
f59a34d2 25# endif
cf15ac8a
CMN
26#endif
27
3816debc
RB
28static git_global_shutdown_fn git__shutdown_callbacks[MAX_SHUTDOWN_CB];
29static git_atomic git__n_shutdown_callbacks;
30static git_atomic git__n_inits;
a3aa5f4d
RB
31
32void git__on_shutdown(git_global_shutdown_fn callback)
33{
34 int count = git_atomic_inc(&git__n_shutdown_callbacks);
001befcd 35 assert(count <= MAX_SHUTDOWN_CB && count > 0);
a3aa5f4d
RB
36 git__shutdown_callbacks[count - 1] = callback;
37}
38
83fe60fa
ET
39static 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
a3aa5f4d
RB
48static void git__shutdown(void)
49{
50 int pos;
51
83fe60fa 52 /* Shutdown subsystems that have registered */
0bf5430d
AG
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 }
a3aa5f4d
RB
58}
59
24e53d2f 60#if defined(GIT_THREADS) && defined(GIT_OPENSSL)
081e76ba
CMN
61void 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}
081e76ba 76
fe6b51ae 77static void shutdown_ssl_locking(void)
50aae000 78{
d6ecc311
UM
79 int num_locks, i;
80
81 num_locks = CRYPTO_num_locks();
e0836577 82 CRYPTO_set_locking_callback(NULL);
d6ecc311
UM
83
84 for (i = 0; i < num_locks; ++i)
85 git_mutex_free(openssl_locks);
50aae000
ET
86 git__free(openssl_locks);
87}
88#endif
081e76ba 89
8f897b6f
CMN
90static void init_ssl(void)
91{
24e53d2f 92#ifdef GIT_OPENSSL
bc48bcdc
JG
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
8f897b6f
CMN
100 SSL_load_error_strings();
101 OpenSSL_add_ssl_algorithms();
f0f97370
CMN
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 */
8f897b6f 108 git__ssl_ctx = SSL_CTX_new(SSLv23_method());
bc48bcdc 109 SSL_CTX_set_options(git__ssl_ctx, ssl_opts);
081e76ba
CMN
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 }
fe6b51ae
CMN
116#endif
117}
081e76ba 118
fe6b51ae
CMN
119int git_openssl_set_locking(void)
120{
24e53d2f 121#ifdef GIT_OPENSSL
081e76ba 122# ifdef GIT_THREADS
fe6b51ae 123 int num_locks, i;
081e76ba 124
fe6b51ae
CMN
125 num_locks = CRYPTO_num_locks();
126 openssl_locks = git__calloc(num_locks, sizeof(git_mutex));
127 GITERR_CHECK_ALLOC(openssl_locks);
081e76ba 128
fe6b51ae
CMN
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 }
081e76ba 134 }
50aae000 135
fe6b51ae
CMN
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;
081e76ba 142# endif
2d2cd625 143#else
fe6b51ae
CMN
144 giterr_set(GITERR_SSL, "libgit2 was not built with OpenSSL support");
145 return -1;
8f897b6f
CMN
146#endif
147}
148
a15c550d
VM
149/**
150 * Handle the global state with TLS
151 *
152 * If libgit2 is built with GIT_THREADS enabled,
799e22ea 153 * the `git_libgit2_init()` function must be called
a15c550d
VM
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
799e22ea 166 * `git_libgit2_shutdown` method must be called to free
a15c550d
VM
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
7ebefd22 175/*
799e22ea 176 * `git_libgit2_init()` allows subsystems to perform global setup,
7ebefd22 177 * which may take place in the global scope. An explicit memory
799e22ea 178 * fence exists at the exit of `git_libgit2_init()`. Without this,
7ebefd22
ET
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 */
a15c550d
VM
183#if defined(GIT_THREADS) && defined(GIT_WIN32)
184
185static DWORD _tls_index;
6e94a1ef 186static volatile LONG _mutex = 0;
e411b74e 187
fb591767 188static int synchronized_threads_init(void)
a15c550d 189{
7ebefd22
ET
190 int error;
191
a15c550d 192 _tls_index = TlsAlloc();
1a42dd17
RB
193 if (git_mutex_init(&git__mwindow_mutex))
194 return -1;
7ebefd22
ET
195
196 /* Initialize any other subsystems that have global state */
e411b74e 197 if ((error = git_hash_global_init()) >= 0)
83634d38 198 error = git_sysdir_global_init();
7ebefd22 199
43095341 200 win32_pthread_initialize();
cdc95a0d 201
e411b74e
BS
202 return error;
203}
204
799e22ea 205int git_libgit2_init(void)
e411b74e 206{
6d91dc53 207 int ret;
cdc95a0d
PK
208
209 /* Enter the lock */
210 while (InterlockedCompareExchange(&_mutex, 1, 0)) { Sleep(0); }
e411b74e 211
cdc95a0d 212 /* Only do work on a 0 -> 1 transition of the refcount */
6d91dc53
ET
213 if ((ret = git_atomic_inc(&git__n_inits)) == 1) {
214 if (synchronized_threads_init() < 0)
215 ret = -1;
216 }
43095341 217
cdc95a0d
PK
218 /* Exit the lock */
219 InterlockedExchange(&_mutex, 0);
220
6d91dc53 221 return ret;
a15c550d
VM
222}
223
fb591767 224static void synchronized_threads_shutdown(void)
a15c550d 225{
43095341 226 /* Shut down any subsystems that have global state */
a3aa5f4d 227 git__shutdown();
83fe60fa 228
06c985d8 229 git__free_tls_data();
83fe60fa 230
a15c550d 231 TlsFree(_tls_index);
43095341 232 git_mutex_free(&git__mwindow_mutex);
e411b74e
BS
233}
234
6d91dc53 235int git_libgit2_shutdown(void)
e411b74e 236{
6d91dc53
ET
237 int ret;
238
cdc95a0d
PK
239 /* Enter the lock */
240 while (InterlockedCompareExchange(&_mutex, 1, 0)) { Sleep(0); }
e411b74e 241
cdc95a0d 242 /* Only do work on a 1 -> 0 transition of the refcount */
6d91dc53 243 if ((ret = git_atomic_dec(&git__n_inits)) == 0)
e411b74e 244 synchronized_threads_shutdown();
cdc95a0d
PK
245
246 /* Exit the lock */
247 InterlockedExchange(&_mutex, 0);
6d91dc53
ET
248
249 return ret;
a15c550d
VM
250}
251
252git_global_st *git__global_state(void)
253{
254 void *ptr;
255
3816debc 256 assert(git_atomic_get(&git__n_inits) > 0);
93b5fabc 257
a15c550d
VM
258 if ((ptr = TlsGetValue(_tls_index)) != NULL)
259 return ptr;
260
2bc8fa02 261 ptr = git__malloc(sizeof(git_global_st));
a15c550d
VM
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
06c985d8
JH
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 */
275void git__free_tls_data(void)
55c5f756
JH
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
a15c550d
VM
286#elif defined(GIT_THREADS) && defined(_POSIX_THREADS)
287
288static pthread_key_t _tls_key;
e411b74e
BS
289static pthread_once_t _once_init = PTHREAD_ONCE_INIT;
290int init_error = 0;
a15c550d
VM
291
292static void cb__free_status(void *st)
293{
83fe60fa 294 git__global_state_cleanup(st);
2bc8fa02 295 git__free(st);
a15c550d
VM
296}
297
e411b74e 298static void init_once(void)
a15c550d 299{
e411b74e
BS
300 if ((init_error = git_mutex_init(&git__mwindow_mutex)) != 0)
301 return;
a15c550d 302 pthread_key_create(&_tls_key, &cb__free_status);
7ebefd22 303
cf15ac8a 304
7ebefd22 305 /* Initialize any other subsystems that have global state */
e411b74e 306 if ((init_error = git_hash_global_init()) >= 0)
83634d38 307 init_error = git_sysdir_global_init();
7ebefd22 308
cf15ac8a
CMN
309 /* OpenSSL needs to be initialized from the main thread */
310 init_ssl();
311
7ebefd22 312 GIT_MEMORY_BARRIER;
e411b74e 313}
7ebefd22 314
799e22ea 315int git_libgit2_init(void)
e411b74e 316{
6d91dc53
ET
317 int ret;
318
e411b74e 319 pthread_once(&_once_init, init_once);
6d91dc53
ET
320 ret = git_atomic_inc(&git__n_inits);
321
322 return init_error ? init_error : ret;
a15c550d
VM
323}
324
6d91dc53 325int git_libgit2_shutdown(void)
a15c550d 326{
efaa342c 327 void *ptr = NULL;
e411b74e 328 pthread_once_t new_once = PTHREAD_ONCE_INIT;
6d91dc53 329 int ret;
e411b74e 330
83fe60fa 331 if ((ret = git_atomic_dec(&git__n_inits)) != 0)
6d91dc53 332 return ret;
e411b74e 333
a3aa5f4d
RB
334 /* Shut down any subsystems that have global state */
335 git__shutdown();
336
efaa342c 337 ptr = pthread_getspecific(_tls_key);
e411b74e 338 pthread_setspecific(_tls_key, NULL);
83fe60fa
ET
339
340 git__global_state_cleanup(ptr);
e411b74e 341 git__free(ptr);
53607868 342
a15c550d 343 pthread_key_delete(_tls_key);
c3320aca 344 git_mutex_free(&git__mwindow_mutex);
e411b74e 345 _once_init = new_once;
6d91dc53 346
83fe60fa 347 return 0;
a15c550d
VM
348}
349
350git_global_st *git__global_state(void)
351{
352 void *ptr;
353
3816debc 354 assert(git_atomic_get(&git__n_inits) > 0);
93b5fabc 355
a15c550d
VM
356 if ((ptr = pthread_getspecific(_tls_key)) != NULL)
357 return ptr;
358
2bc8fa02 359 ptr = git__malloc(sizeof(git_global_st));
a15c550d
VM
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
370static git_global_st __state;
371
799e22ea 372int git_libgit2_init(void)
a15c550d 373{
e6b0ae7a
CMN
374 static int ssl_inited = 0;
375
376 if (!ssl_inited) {
377 init_ssl();
378 ssl_inited = 1;
379 }
380
6d91dc53 381 return git_atomic_inc(&git__n_inits);
a15c550d
VM
382}
383
6d91dc53 384int git_libgit2_shutdown(void)
a15c550d 385{
6d91dc53
ET
386 int ret;
387
41954a49 388 /* Shut down any subsystems that have global state */
83fe60fa
ET
389 if ((ret = git_atomic_dec(&git__n_inits)) != 0)
390 return ret;
6d91dc53 391
83fe60fa
ET
392 git__shutdown();
393 git__global_state_cleanup(&__state);
394
395 return 0;
a15c550d
VM
396}
397
398git_global_st *git__global_state(void)
399{
400 return &__state;
401}
402
403#endif /* GIT_THREADS */