]>
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" |
a15c550d VM |
12 | #include "thread-utils.h" |
13 | ||
8cef828d CMN |
14 | |
15 | git_mutex git__mwindow_mutex; | |
16 | ||
a3aa5f4d RB |
17 | #define MAX_SHUTDOWN_CB 8 |
18 | ||
cf15ac8a CMN |
19 | #ifdef GIT_SSL |
20 | # include <openssl/ssl.h> | |
21 | SSL_CTX *git__ssl_ctx; | |
22 | #endif | |
23 | ||
1d3364ac CMN |
24 | git_mutex git__ssl_mutex; |
25 | git_atomic git__ssl_init; | |
26 | ||
3816debc RB |
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; | |
a3aa5f4d RB |
30 | |
31 | void git__on_shutdown(git_global_shutdown_fn callback) | |
32 | { | |
33 | int count = git_atomic_inc(&git__n_shutdown_callbacks); | |
001befcd | 34 | assert(count <= MAX_SHUTDOWN_CB && count > 0); |
a3aa5f4d RB |
35 | git__shutdown_callbacks[count - 1] = callback; |
36 | } | |
37 | ||
38 | static void git__shutdown(void) | |
39 | { | |
40 | int pos; | |
41 | ||
0bf5430d AG |
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 | } | |
001befcd | 47 | |
a3aa5f4d RB |
48 | } |
49 | ||
8f897b6f CMN |
50 | static void init_ssl(void) |
51 | { | |
52 | #ifdef GIT_SSL | |
53 | SSL_load_error_strings(); | |
54 | OpenSSL_add_ssl_algorithms(); | |
55 | git__ssl_ctx = SSL_CTX_new(SSLv23_method()); | |
56 | #endif | |
57 | } | |
58 | ||
a15c550d VM |
59 | /** |
60 | * Handle the global state with TLS | |
61 | * | |
62 | * If libgit2 is built with GIT_THREADS enabled, | |
63 | * the `git_threads_init()` function must be called | |
64 | * before calling any other function of the library. | |
65 | * | |
66 | * This function allocates a TLS index (using pthreads | |
67 | * or the native Win32 API) to store the global state | |
68 | * on a per-thread basis. | |
69 | * | |
70 | * Any internal method that requires global state will | |
71 | * then call `git__global_state()` which returns a pointer | |
72 | * to the global state structure; this pointer is lazily | |
73 | * allocated on each thread. | |
74 | * | |
75 | * Before shutting down the library, the | |
76 | * `git_threads_shutdown` method must be called to free | |
77 | * the previously reserved TLS index. | |
78 | * | |
79 | * If libgit2 is built without threading support, the | |
80 | * `git__global_statestate()` call returns a pointer to a single, | |
81 | * statically allocated global state. The `git_thread_` | |
82 | * functions are not available in that case. | |
83 | */ | |
84 | ||
7ebefd22 ET |
85 | /* |
86 | * `git_threads_init()` allows subsystems to perform global setup, | |
87 | * which may take place in the global scope. An explicit memory | |
88 | * fence exists at the exit of `git_threads_init()`. Without this, | |
89 | * CPU cores are free to reorder cache invalidation of `_tls_init` | |
90 | * before cache invalidation of the subsystems' newly written global | |
91 | * state. | |
92 | */ | |
a15c550d VM |
93 | #if defined(GIT_THREADS) && defined(GIT_WIN32) |
94 | ||
95 | static DWORD _tls_index; | |
6e94a1ef | 96 | static volatile LONG _mutex = 0; |
e411b74e BS |
97 | |
98 | static int synchronized_threads_init() | |
a15c550d | 99 | { |
7ebefd22 ET |
100 | int error; |
101 | ||
a15c550d | 102 | _tls_index = TlsAlloc(); |
1a42dd17 RB |
103 | if (git_mutex_init(&git__mwindow_mutex)) |
104 | return -1; | |
7ebefd22 ET |
105 | |
106 | /* Initialize any other subsystems that have global state */ | |
e411b74e | 107 | if ((error = git_hash_global_init()) >= 0) |
83634d38 | 108 | error = git_sysdir_global_init(); |
7ebefd22 | 109 | |
43095341 | 110 | win32_pthread_initialize(); |
cdc95a0d | 111 | |
e411b74e BS |
112 | return error; |
113 | } | |
114 | ||
115 | int git_threads_init(void) | |
116 | { | |
cdc95a0d PK |
117 | int error = 0; |
118 | ||
119 | /* Enter the lock */ | |
120 | while (InterlockedCompareExchange(&_mutex, 1, 0)) { Sleep(0); } | |
e411b74e | 121 | |
cdc95a0d | 122 | /* Only do work on a 0 -> 1 transition of the refcount */ |
3816debc | 123 | if (1 == git_atomic_inc(&git__n_inits)) |
e411b74e | 124 | error = synchronized_threads_init(); |
43095341 | 125 | |
cdc95a0d PK |
126 | /* Exit the lock */ |
127 | InterlockedExchange(&_mutex, 0); | |
128 | ||
7ebefd22 | 129 | return error; |
a15c550d VM |
130 | } |
131 | ||
e411b74e | 132 | static void synchronized_threads_shutdown() |
a15c550d | 133 | { |
43095341 | 134 | /* Shut down any subsystems that have global state */ |
a3aa5f4d | 135 | git__shutdown(); |
a15c550d | 136 | TlsFree(_tls_index); |
43095341 | 137 | git_mutex_free(&git__mwindow_mutex); |
e411b74e BS |
138 | } |
139 | ||
140 | void git_threads_shutdown(void) | |
141 | { | |
cdc95a0d PK |
142 | /* Enter the lock */ |
143 | while (InterlockedCompareExchange(&_mutex, 1, 0)) { Sleep(0); } | |
e411b74e | 144 | |
cdc95a0d | 145 | /* Only do work on a 1 -> 0 transition of the refcount */ |
3816debc | 146 | if (0 == git_atomic_dec(&git__n_inits)) |
e411b74e | 147 | synchronized_threads_shutdown(); |
cdc95a0d PK |
148 | |
149 | /* Exit the lock */ | |
150 | InterlockedExchange(&_mutex, 0); | |
a15c550d VM |
151 | } |
152 | ||
153 | git_global_st *git__global_state(void) | |
154 | { | |
155 | void *ptr; | |
156 | ||
3816debc | 157 | assert(git_atomic_get(&git__n_inits) > 0); |
93b5fabc | 158 | |
a15c550d VM |
159 | if ((ptr = TlsGetValue(_tls_index)) != NULL) |
160 | return ptr; | |
161 | ||
2bc8fa02 | 162 | ptr = git__malloc(sizeof(git_global_st)); |
a15c550d VM |
163 | if (!ptr) |
164 | return NULL; | |
165 | ||
166 | memset(ptr, 0x0, sizeof(git_global_st)); | |
167 | TlsSetValue(_tls_index, ptr); | |
168 | return ptr; | |
169 | } | |
170 | ||
171 | #elif defined(GIT_THREADS) && defined(_POSIX_THREADS) | |
172 | ||
173 | static pthread_key_t _tls_key; | |
e411b74e BS |
174 | static pthread_once_t _once_init = PTHREAD_ONCE_INIT; |
175 | int init_error = 0; | |
a15c550d VM |
176 | |
177 | static void cb__free_status(void *st) | |
178 | { | |
2bc8fa02 | 179 | git__free(st); |
a15c550d VM |
180 | } |
181 | ||
e411b74e | 182 | static void init_once(void) |
a15c550d | 183 | { |
e411b74e BS |
184 | if ((init_error = git_mutex_init(&git__mwindow_mutex)) != 0) |
185 | return; | |
cf15ac8a CMN |
186 | if ((init_error = git_mutex_init(&git__ssl_mutex)) != 0) |
187 | return; | |
a15c550d | 188 | pthread_key_create(&_tls_key, &cb__free_status); |
7ebefd22 | 189 | |
cf15ac8a | 190 | |
7ebefd22 | 191 | /* Initialize any other subsystems that have global state */ |
e411b74e | 192 | if ((init_error = git_hash_global_init()) >= 0) |
83634d38 | 193 | init_error = git_sysdir_global_init(); |
7ebefd22 | 194 | |
cf15ac8a CMN |
195 | /* OpenSSL needs to be initialized from the main thread */ |
196 | init_ssl(); | |
197 | ||
7ebefd22 | 198 | GIT_MEMORY_BARRIER; |
e411b74e | 199 | } |
7ebefd22 | 200 | |
e411b74e BS |
201 | int git_threads_init(void) |
202 | { | |
203 | pthread_once(&_once_init, init_once); | |
204 | git_atomic_inc(&git__n_inits); | |
205 | return init_error; | |
a15c550d VM |
206 | } |
207 | ||
208 | void git_threads_shutdown(void) | |
209 | { | |
efaa342c | 210 | void *ptr = NULL; |
e411b74e BS |
211 | pthread_once_t new_once = PTHREAD_ONCE_INIT; |
212 | ||
213 | if (git_atomic_dec(&git__n_inits) > 0) return; | |
214 | ||
a3aa5f4d RB |
215 | /* Shut down any subsystems that have global state */ |
216 | git__shutdown(); | |
217 | ||
efaa342c | 218 | ptr = pthread_getspecific(_tls_key); |
e411b74e BS |
219 | pthread_setspecific(_tls_key, NULL); |
220 | git__free(ptr); | |
53607868 | 221 | |
a15c550d | 222 | pthread_key_delete(_tls_key); |
c3320aca | 223 | git_mutex_free(&git__mwindow_mutex); |
e411b74e | 224 | _once_init = new_once; |
a15c550d VM |
225 | } |
226 | ||
227 | git_global_st *git__global_state(void) | |
228 | { | |
229 | void *ptr; | |
230 | ||
3816debc | 231 | assert(git_atomic_get(&git__n_inits) > 0); |
93b5fabc | 232 | |
a15c550d VM |
233 | if ((ptr = pthread_getspecific(_tls_key)) != NULL) |
234 | return ptr; | |
235 | ||
2bc8fa02 | 236 | ptr = git__malloc(sizeof(git_global_st)); |
a15c550d VM |
237 | if (!ptr) |
238 | return NULL; | |
239 | ||
240 | memset(ptr, 0x0, sizeof(git_global_st)); | |
241 | pthread_setspecific(_tls_key, ptr); | |
242 | return ptr; | |
243 | } | |
244 | ||
245 | #else | |
246 | ||
247 | static git_global_st __state; | |
248 | ||
7ebefd22 | 249 | int git_threads_init(void) |
a15c550d | 250 | { |
8f897b6f | 251 | init_ssl(); |
3816debc | 252 | git_atomic_inc(&git__n_inits); |
7ebefd22 | 253 | return 0; |
a15c550d VM |
254 | } |
255 | ||
256 | void git_threads_shutdown(void) | |
257 | { | |
41954a49 | 258 | /* Shut down any subsystems that have global state */ |
3816debc RB |
259 | if (0 == git_atomic_dec(&git__n_inits)) |
260 | git__shutdown(); | |
a15c550d VM |
261 | } |
262 | ||
263 | git_global_st *git__global_state(void) | |
264 | { | |
265 | return &__state; | |
266 | } | |
267 | ||
268 | #endif /* GIT_THREADS */ |