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