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