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