2 * Copyright (C) the libgit2 contributors. All rights reserved.
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.
11 #include "git2/threads.h"
12 #include "thread-utils.h"
15 git_mutex git__mwindow_mutex
;
17 #define MAX_SHUTDOWN_CB 8
20 # include <openssl/ssl.h>
21 SSL_CTX
*git__ssl_ctx
;
23 static git_mutex
*openssl_locks
;
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
;
31 void git__on_shutdown(git_global_shutdown_fn callback
)
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
;
38 static void git__shutdown(void)
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
);
50 #if defined(GIT_THREADS) && defined(GIT_SSL)
51 void openssl_locking_function(int mode
, int n
, const char *file
, int line
)
58 lock
= mode
& CRYPTO_LOCK
;
61 git_mutex_lock(&openssl_locks
[n
]);
63 git_mutex_unlock(&openssl_locks
[n
]);
69 static void init_ssl(void)
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
);
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
);
93 for (i
= 0; i
< num_locks
; i
++) {
94 if (git_mutex_init(&openssl_locks
[i
]) != 0) {
95 SSL_CTX_free(git__ssl_ctx
);
100 CRYPTO_set_locking_callback(openssl_locking_function
);
107 * Handle the global state with TLS
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.
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.
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.
122 * Before shutting down the library, the
123 * `git_threads_shutdown` method must be called to free
124 * the previously reserved TLS index.
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.
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
140 #if defined(GIT_THREADS) && defined(GIT_WIN32)
142 static DWORD _tls_index
;
143 static volatile LONG _mutex
= 0;
145 static int synchronized_threads_init(void)
149 _tls_index
= TlsAlloc();
150 if (git_mutex_init(&git__mwindow_mutex
))
153 /* Initialize any other subsystems that have global state */
154 if ((error
= git_hash_global_init()) >= 0)
155 error
= git_sysdir_global_init();
157 win32_pthread_initialize();
162 int git_threads_init(void)
167 while (InterlockedCompareExchange(&_mutex
, 1, 0)) { Sleep(0); }
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();
174 InterlockedExchange(&_mutex
, 0);
179 static void synchronized_threads_shutdown(void)
181 /* Shut down any subsystems that have global state */
184 git_mutex_free(&git__mwindow_mutex
);
187 void git_threads_shutdown(void)
190 while (InterlockedCompareExchange(&_mutex
, 1, 0)) { Sleep(0); }
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();
197 InterlockedExchange(&_mutex
, 0);
200 git_global_st
*git__global_state(void)
204 assert(git_atomic_get(&git__n_inits
) > 0);
206 if ((ptr
= TlsGetValue(_tls_index
)) != NULL
)
209 ptr
= git__malloc(sizeof(git_global_st
));
213 memset(ptr
, 0x0, sizeof(git_global_st
));
214 TlsSetValue(_tls_index
, ptr
);
218 #elif defined(GIT_THREADS) && defined(_POSIX_THREADS)
220 static pthread_key_t _tls_key
;
221 static pthread_once_t _once_init
= PTHREAD_ONCE_INIT
;
224 static void cb__free_status(void *st
)
226 git_global_st
*state
= (git_global_st
*) st
;
227 git__free(state
->error_t
.message
);
232 static void init_once(void)
234 if ((init_error
= git_mutex_init(&git__mwindow_mutex
)) != 0)
236 pthread_key_create(&_tls_key
, &cb__free_status
);
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();
243 /* OpenSSL needs to be initialized from the main thread */
249 int git_threads_init(void)
251 pthread_once(&_once_init
, init_once
);
252 git_atomic_inc(&git__n_inits
);
256 void git_threads_shutdown(void)
259 pthread_once_t new_once
= PTHREAD_ONCE_INIT
;
261 if (git_atomic_dec(&git__n_inits
) > 0) return;
263 /* Shut down any subsystems that have global state */
266 ptr
= pthread_getspecific(_tls_key
);
267 pthread_setspecific(_tls_key
, NULL
);
270 pthread_key_delete(_tls_key
);
271 git_mutex_free(&git__mwindow_mutex
);
272 _once_init
= new_once
;
275 git_global_st
*git__global_state(void)
279 assert(git_atomic_get(&git__n_inits
) > 0);
281 if ((ptr
= pthread_getspecific(_tls_key
)) != NULL
)
284 ptr
= git__malloc(sizeof(git_global_st
));
288 memset(ptr
, 0x0, sizeof(git_global_st
));
289 pthread_setspecific(_tls_key
, ptr
);
295 static git_global_st __state
;
297 int git_threads_init(void)
299 static int ssl_inited
= 0;
306 git_atomic_inc(&git__n_inits
);
310 void git_threads_shutdown(void)
312 /* Shut down any subsystems that have global state */
313 if (0 == git_atomic_dec(&git__n_inits
))
317 git_global_st
*git__global_state(void)
322 #endif /* GIT_THREADS */