]>
Commit | Line | Data |
---|---|---|
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" |
41954a49 | 11 | #include "git2/threads.h" |
799e22ea | 12 | #include "git2/global.h" |
a15c550d VM |
13 | #include "thread-utils.h" |
14 | ||
8cef828d CMN |
15 | |
16 | git_mutex git__mwindow_mutex; | |
17 | ||
a3aa5f4d RB |
18 | #define MAX_SHUTDOWN_CB 8 |
19 | ||
cf15ac8a CMN |
20 | #ifdef GIT_SSL |
21 | # include <openssl/ssl.h> | |
22 | SSL_CTX *git__ssl_ctx; | |
f59a34d2 | 23 | # ifdef GIT_THREADS |
081e76ba | 24 | static git_mutex *openssl_locks; |
f59a34d2 | 25 | # endif |
cf15ac8a CMN |
26 | #endif |
27 | ||
3816debc RB |
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; | |
a3aa5f4d RB |
31 | |
32 | void 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 | ||
39 | static void git__shutdown(void) | |
40 | { | |
41 | int pos; | |
42 | ||
0bf5430d AG |
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 | } | |
001befcd | 48 | |
a3aa5f4d RB |
49 | } |
50 | ||
081e76ba CMN |
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 | } | |
081e76ba | 67 | |
fe6b51ae | 68 | static void shutdown_ssl_locking(void) |
50aae000 | 69 | { |
d6ecc311 UM |
70 | int num_locks, i; |
71 | ||
72 | num_locks = CRYPTO_num_locks(); | |
e0836577 | 73 | CRYPTO_set_locking_callback(NULL); |
d6ecc311 UM |
74 | |
75 | for (i = 0; i < num_locks; ++i) | |
76 | git_mutex_free(openssl_locks); | |
50aae000 ET |
77 | git__free(openssl_locks); |
78 | } | |
79 | #endif | |
081e76ba | 80 | |
8f897b6f CMN |
81 | static void init_ssl(void) |
82 | { | |
83 | #ifdef GIT_SSL | |
bc48bcdc JG |
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 | ||
8f897b6f CMN |
91 | SSL_load_error_strings(); |
92 | OpenSSL_add_ssl_algorithms(); | |
f0f97370 CMN |
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 | */ | |
8f897b6f | 99 | git__ssl_ctx = SSL_CTX_new(SSLv23_method()); |
bc48bcdc | 100 | SSL_CTX_set_options(git__ssl_ctx, ssl_opts); |
081e76ba CMN |
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 | } | |
fe6b51ae CMN |
107 | #endif |
108 | } | |
081e76ba | 109 | |
fe6b51ae CMN |
110 | int git_openssl_set_locking(void) |
111 | { | |
112 | #ifdef GIT_SSL | |
081e76ba | 113 | # ifdef GIT_THREADS |
fe6b51ae | 114 | int num_locks, i; |
081e76ba | 115 | |
fe6b51ae CMN |
116 | num_locks = CRYPTO_num_locks(); |
117 | openssl_locks = git__calloc(num_locks, sizeof(git_mutex)); | |
118 | GITERR_CHECK_ALLOC(openssl_locks); | |
081e76ba | 119 | |
fe6b51ae CMN |
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 | } | |
081e76ba | 125 | } |
50aae000 | 126 | |
fe6b51ae CMN |
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; | |
081e76ba | 133 | # endif |
fe6b51ae CMN |
134 | giterr_set(GITERR_SSL, "libgit2 was not built with OpenSSL support"); |
135 | return -1; | |
8f897b6f CMN |
136 | #endif |
137 | } | |
138 | ||
a15c550d VM |
139 | /** |
140 | * Handle the global state with TLS | |
141 | * | |
142 | * If libgit2 is built with GIT_THREADS enabled, | |
799e22ea | 143 | * the `git_libgit2_init()` function must be called |
a15c550d VM |
144 | * before calling any other function of the library. |
145 | * | |
146 | * This function allocates a TLS index (using pthreads | |
147 | * or the native Win32 API) to store the global state | |
148 | * on a per-thread basis. | |
149 | * | |
150 | * Any internal method that requires global state will | |
151 | * then call `git__global_state()` which returns a pointer | |
152 | * to the global state structure; this pointer is lazily | |
153 | * allocated on each thread. | |
154 | * | |
155 | * Before shutting down the library, the | |
799e22ea | 156 | * `git_libgit2_shutdown` method must be called to free |
a15c550d VM |
157 | * the previously reserved TLS index. |
158 | * | |
159 | * If libgit2 is built without threading support, the | |
160 | * `git__global_statestate()` call returns a pointer to a single, | |
161 | * statically allocated global state. The `git_thread_` | |
162 | * functions are not available in that case. | |
163 | */ | |
164 | ||
7ebefd22 | 165 | /* |
799e22ea | 166 | * `git_libgit2_init()` allows subsystems to perform global setup, |
7ebefd22 | 167 | * which may take place in the global scope. An explicit memory |
799e22ea | 168 | * fence exists at the exit of `git_libgit2_init()`. Without this, |
7ebefd22 ET |
169 | * CPU cores are free to reorder cache invalidation of `_tls_init` |
170 | * before cache invalidation of the subsystems' newly written global | |
171 | * state. | |
172 | */ | |
a15c550d VM |
173 | #if defined(GIT_THREADS) && defined(GIT_WIN32) |
174 | ||
175 | static DWORD _tls_index; | |
6e94a1ef | 176 | static volatile LONG _mutex = 0; |
e411b74e | 177 | |
fb591767 | 178 | static int synchronized_threads_init(void) |
a15c550d | 179 | { |
7ebefd22 ET |
180 | int error; |
181 | ||
a15c550d | 182 | _tls_index = TlsAlloc(); |
1a42dd17 RB |
183 | if (git_mutex_init(&git__mwindow_mutex)) |
184 | return -1; | |
7ebefd22 ET |
185 | |
186 | /* Initialize any other subsystems that have global state */ | |
e411b74e | 187 | if ((error = git_hash_global_init()) >= 0) |
83634d38 | 188 | error = git_sysdir_global_init(); |
7ebefd22 | 189 | |
43095341 | 190 | win32_pthread_initialize(); |
cdc95a0d | 191 | |
e411b74e BS |
192 | return error; |
193 | } | |
194 | ||
799e22ea | 195 | int git_libgit2_init(void) |
e411b74e | 196 | { |
cdc95a0d PK |
197 | int error = 0; |
198 | ||
199 | /* Enter the lock */ | |
200 | while (InterlockedCompareExchange(&_mutex, 1, 0)) { Sleep(0); } | |
e411b74e | 201 | |
cdc95a0d | 202 | /* Only do work on a 0 -> 1 transition of the refcount */ |
3816debc | 203 | if (1 == git_atomic_inc(&git__n_inits)) |
e411b74e | 204 | error = synchronized_threads_init(); |
43095341 | 205 | |
cdc95a0d PK |
206 | /* Exit the lock */ |
207 | InterlockedExchange(&_mutex, 0); | |
208 | ||
7ebefd22 | 209 | return error; |
a15c550d VM |
210 | } |
211 | ||
fb591767 | 212 | static void synchronized_threads_shutdown(void) |
a15c550d | 213 | { |
43095341 | 214 | /* Shut down any subsystems that have global state */ |
a3aa5f4d | 215 | git__shutdown(); |
a15c550d | 216 | TlsFree(_tls_index); |
43095341 | 217 | git_mutex_free(&git__mwindow_mutex); |
e411b74e BS |
218 | } |
219 | ||
799e22ea | 220 | void git_libgit2_shutdown(void) |
e411b74e | 221 | { |
cdc95a0d PK |
222 | /* Enter the lock */ |
223 | while (InterlockedCompareExchange(&_mutex, 1, 0)) { Sleep(0); } | |
e411b74e | 224 | |
cdc95a0d | 225 | /* Only do work on a 1 -> 0 transition of the refcount */ |
3816debc | 226 | if (0 == git_atomic_dec(&git__n_inits)) |
e411b74e | 227 | synchronized_threads_shutdown(); |
cdc95a0d PK |
228 | |
229 | /* Exit the lock */ | |
230 | InterlockedExchange(&_mutex, 0); | |
a15c550d VM |
231 | } |
232 | ||
233 | git_global_st *git__global_state(void) | |
234 | { | |
235 | void *ptr; | |
236 | ||
3816debc | 237 | assert(git_atomic_get(&git__n_inits) > 0); |
93b5fabc | 238 | |
a15c550d VM |
239 | if ((ptr = TlsGetValue(_tls_index)) != NULL) |
240 | return ptr; | |
241 | ||
2bc8fa02 | 242 | ptr = git__malloc(sizeof(git_global_st)); |
a15c550d VM |
243 | if (!ptr) |
244 | return NULL; | |
245 | ||
246 | memset(ptr, 0x0, sizeof(git_global_st)); | |
247 | TlsSetValue(_tls_index, ptr); | |
248 | return ptr; | |
249 | } | |
250 | ||
251 | #elif defined(GIT_THREADS) && defined(_POSIX_THREADS) | |
252 | ||
253 | static pthread_key_t _tls_key; | |
e411b74e BS |
254 | static pthread_once_t _once_init = PTHREAD_ONCE_INIT; |
255 | int init_error = 0; | |
a15c550d VM |
256 | |
257 | static void cb__free_status(void *st) | |
258 | { | |
fdea219a CMN |
259 | git_global_st *state = (git_global_st *) st; |
260 | git__free(state->error_t.message); | |
261 | ||
2bc8fa02 | 262 | git__free(st); |
a15c550d VM |
263 | } |
264 | ||
e411b74e | 265 | static void init_once(void) |
a15c550d | 266 | { |
e411b74e BS |
267 | if ((init_error = git_mutex_init(&git__mwindow_mutex)) != 0) |
268 | return; | |
a15c550d | 269 | pthread_key_create(&_tls_key, &cb__free_status); |
7ebefd22 | 270 | |
cf15ac8a | 271 | |
7ebefd22 | 272 | /* Initialize any other subsystems that have global state */ |
e411b74e | 273 | if ((init_error = git_hash_global_init()) >= 0) |
83634d38 | 274 | init_error = git_sysdir_global_init(); |
7ebefd22 | 275 | |
cf15ac8a CMN |
276 | /* OpenSSL needs to be initialized from the main thread */ |
277 | init_ssl(); | |
278 | ||
7ebefd22 | 279 | GIT_MEMORY_BARRIER; |
e411b74e | 280 | } |
7ebefd22 | 281 | |
799e22ea | 282 | int git_libgit2_init(void) |
e411b74e BS |
283 | { |
284 | pthread_once(&_once_init, init_once); | |
285 | git_atomic_inc(&git__n_inits); | |
286 | return init_error; | |
a15c550d VM |
287 | } |
288 | ||
799e22ea | 289 | void git_libgit2_shutdown(void) |
a15c550d | 290 | { |
efaa342c | 291 | void *ptr = NULL; |
e411b74e BS |
292 | pthread_once_t new_once = PTHREAD_ONCE_INIT; |
293 | ||
294 | if (git_atomic_dec(&git__n_inits) > 0) return; | |
295 | ||
a3aa5f4d RB |
296 | /* Shut down any subsystems that have global state */ |
297 | git__shutdown(); | |
298 | ||
efaa342c | 299 | ptr = pthread_getspecific(_tls_key); |
e411b74e BS |
300 | pthread_setspecific(_tls_key, NULL); |
301 | git__free(ptr); | |
53607868 | 302 | |
a15c550d | 303 | pthread_key_delete(_tls_key); |
c3320aca | 304 | git_mutex_free(&git__mwindow_mutex); |
e411b74e | 305 | _once_init = new_once; |
a15c550d VM |
306 | } |
307 | ||
308 | git_global_st *git__global_state(void) | |
309 | { | |
310 | void *ptr; | |
311 | ||
3816debc | 312 | assert(git_atomic_get(&git__n_inits) > 0); |
93b5fabc | 313 | |
a15c550d VM |
314 | if ((ptr = pthread_getspecific(_tls_key)) != NULL) |
315 | return ptr; | |
316 | ||
2bc8fa02 | 317 | ptr = git__malloc(sizeof(git_global_st)); |
a15c550d VM |
318 | if (!ptr) |
319 | return NULL; | |
320 | ||
321 | memset(ptr, 0x0, sizeof(git_global_st)); | |
322 | pthread_setspecific(_tls_key, ptr); | |
323 | return ptr; | |
324 | } | |
325 | ||
326 | #else | |
327 | ||
328 | static git_global_st __state; | |
329 | ||
799e22ea | 330 | int git_libgit2_init(void) |
a15c550d | 331 | { |
e6b0ae7a CMN |
332 | static int ssl_inited = 0; |
333 | ||
334 | if (!ssl_inited) { | |
335 | init_ssl(); | |
336 | ssl_inited = 1; | |
337 | } | |
338 | ||
3816debc | 339 | git_atomic_inc(&git__n_inits); |
7ebefd22 | 340 | return 0; |
a15c550d VM |
341 | } |
342 | ||
799e22ea | 343 | void git_libgit2_shutdown(void) |
a15c550d | 344 | { |
41954a49 | 345 | /* Shut down any subsystems that have global state */ |
3816debc RB |
346 | if (0 == git_atomic_dec(&git__n_inits)) |
347 | git__shutdown(); | |
a15c550d VM |
348 | } |
349 | ||
350 | git_global_st *git__global_state(void) | |
351 | { | |
352 | return &__state; | |
353 | } | |
354 | ||
355 | #endif /* GIT_THREADS */ |