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