File Coverage

deps/libgit2/src/global.c
Criterion Covered Total %
statement 15 38 39.4
branch 7 20 35.0
condition n/a
subroutine n/a
pod n/a
total 22 58 37.9


line stmt bran cond sub pod time code
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              
8             #include "global.h"
9              
10             #include "alloc.h"
11             #include "hash.h"
12             #include "sysdir.h"
13             #include "filter.h"
14             #include "merge_driver.h"
15             #include "streams/registry.h"
16             #include "streams/mbedtls.h"
17             #include "streams/openssl.h"
18             #include "thread-utils.h"
19             #include "git2/global.h"
20             #include "transports/ssh.h"
21              
22             #if defined(GIT_MSVC_CRTDBG)
23             #include "win32/w32_stack.h"
24             #include "win32/w32_crtdbg_stacktrace.h"
25             #endif
26              
27             git_mutex git__mwindow_mutex;
28              
29             typedef int (*git_global_init_fn)(void);
30              
31             static git_global_init_fn git__init_callbacks[] = {
32             git_allocator_global_init,
33             git_hash_global_init,
34             git_sysdir_global_init,
35             git_filter_global_init,
36             git_merge_driver_global_init,
37             git_transport_ssh_global_init,
38             git_stream_registry_global_init,
39             git_openssl_stream_global_init,
40             git_mbedtls_stream_global_init,
41             git_mwindow_global_init
42             };
43              
44             static git_global_shutdown_fn git__shutdown_callbacks[ARRAY_SIZE(git__init_callbacks)];
45              
46             static git_atomic git__n_shutdown_callbacks;
47             static git_atomic git__n_inits;
48             char *git__user_agent;
49             char *git__ssl_ciphers;
50              
51 516           void git__on_shutdown(git_global_shutdown_fn callback)
52             {
53 516           int count = git_atomic_inc(&git__n_shutdown_callbacks);
54 516 50         assert(count <= (int) ARRAY_SIZE(git__shutdown_callbacks) && count > 0);
    50          
55 516           git__shutdown_callbacks[count - 1] = callback;
56 516           }
57              
58 0           static void git__global_state_cleanup(git_global_st *st)
59             {
60 0 0         if (!st)
61 0           return;
62              
63 0           git__free(st->error_t.message);
64 0           st->error_t.message = NULL;
65             }
66              
67 86           static int init_common(void)
68             {
69             size_t i;
70             int ret;
71              
72             /* Initialize the CRT debug allocator first, before our first malloc */
73             #if defined(GIT_MSVC_CRTDBG)
74             git_win32__crtdbg_stacktrace_init();
75             git_win32__stack_init();
76             #endif
77              
78             /* Initialize subsystems that have global state */
79 946 100         for (i = 0; i < ARRAY_SIZE(git__init_callbacks); i++)
80 860 50         if ((ret = git__init_callbacks[i]()) != 0)
81 0           break;
82              
83             GIT_MEMORY_BARRIER;
84              
85 86           return ret;
86             }
87              
88 0           static void shutdown_common(void)
89             {
90             int pos;
91              
92             /* Shutdown subsystems that have registered */
93 0 0         for (pos = git_atomic_get(&git__n_shutdown_callbacks);
94             pos > 0;
95 0           pos = git_atomic_dec(&git__n_shutdown_callbacks)) {
96              
97 0           git_global_shutdown_fn cb = git__swap(
98             git__shutdown_callbacks[pos - 1], NULL);
99              
100 0 0         if (cb != NULL)
101 0           cb();
102             }
103              
104 0           git__free(git__user_agent);
105 0           git__free(git__ssl_ciphers);
106 0           }
107              
108             /**
109             * Handle the global state with TLS
110             *
111             * If libgit2 is built with GIT_THREADS enabled,
112             * the `git_libgit2_init()` function must be called
113             * before calling any other function of the library.
114             *
115             * This function allocates a TLS index (using pthreads
116             * or the native Win32 API) to store the global state
117             * on a per-thread basis.
118             *
119             * Any internal method that requires global state will
120             * then call `git__global_state()` which returns a pointer
121             * to the global state structure; this pointer is lazily
122             * allocated on each thread.
123             *
124             * Before shutting down the library, the
125             * `git_libgit2_shutdown` method must be called to free
126             * the previously reserved TLS index.
127             *
128             * If libgit2 is built without threading support, the
129             * `git__global_statestate()` call returns a pointer to a single,
130             * statically allocated global state. The `git_thread_`
131             * functions are not available in that case.
132             */
133              
134             /*
135             * `git_libgit2_init()` allows subsystems to perform global setup,
136             * which may take place in the global scope. An explicit memory
137             * fence exists at the exit of `git_libgit2_init()`. Without this,
138             * CPU cores are free to reorder cache invalidation of `_tls_init`
139             * before cache invalidation of the subsystems' newly written global
140             * state.
141             */
142             #if defined(GIT_THREADS) && defined(GIT_WIN32)
143              
144             static DWORD _fls_index;
145             static volatile LONG _mutex = 0;
146              
147             static void WINAPI fls_free(void *st)
148             {
149             git__global_state_cleanup(st);
150             git__free(st);
151             }
152              
153             static int synchronized_threads_init(void)
154             {
155             int error;
156              
157             if ((_fls_index = FlsAlloc(fls_free)) == FLS_OUT_OF_INDEXES)
158             return -1;
159              
160             git_threads_init();
161              
162             if (git_mutex_init(&git__mwindow_mutex))
163             return -1;
164              
165             error = init_common();
166              
167             return error;
168             }
169              
170             int git_libgit2_init(void)
171             {
172             int ret;
173              
174             /* Enter the lock */
175             while (InterlockedCompareExchange(&_mutex, 1, 0)) { Sleep(0); }
176              
177             /* Only do work on a 0 -> 1 transition of the refcount */
178             if ((ret = git_atomic_inc(&git__n_inits)) == 1) {
179             if (synchronized_threads_init() < 0)
180             ret = -1;
181             }
182              
183             /* Exit the lock */
184             InterlockedExchange(&_mutex, 0);
185              
186             return ret;
187             }
188              
189             int git_libgit2_shutdown(void)
190             {
191             int ret;
192              
193             /* Enter the lock */
194             while (InterlockedCompareExchange(&_mutex, 1, 0)) { Sleep(0); }
195              
196             /* Only do work on a 1 -> 0 transition of the refcount */
197             if ((ret = git_atomic_dec(&git__n_inits)) == 0) {
198             shutdown_common();
199              
200             FlsFree(_fls_index);
201             git_mutex_free(&git__mwindow_mutex);
202              
203             #if defined(GIT_MSVC_CRTDBG)
204             git_win32__crtdbg_stacktrace_cleanup();
205             git_win32__stack_cleanup();
206             #endif
207             }
208              
209             /* Exit the lock */
210             InterlockedExchange(&_mutex, 0);
211              
212             return ret;
213             }
214              
215             git_global_st *git__global_state(void)
216             {
217             git_global_st *ptr;
218              
219             assert(git_atomic_get(&git__n_inits) > 0);
220              
221             if ((ptr = FlsGetValue(_fls_index)) != NULL)
222             return ptr;
223              
224             ptr = git__calloc(1, sizeof(git_global_st));
225             if (!ptr)
226             return NULL;
227              
228             git_buf_init(&ptr->error_buf, 0);
229              
230             FlsSetValue(_fls_index, ptr);
231             return ptr;
232             }
233              
234             #elif defined(GIT_THREADS) && defined(_POSIX_THREADS)
235              
236             static pthread_key_t _tls_key;
237             static pthread_mutex_t _init_mutex = PTHREAD_MUTEX_INITIALIZER;
238             static pthread_once_t _once_init = PTHREAD_ONCE_INIT;
239             int init_error = 0;
240              
241             static void cb__free_status(void *st)
242             {
243             git__global_state_cleanup(st);
244             git__free(st);
245             }
246              
247             static void init_once(void)
248             {
249             if ((init_error = git_mutex_init(&git__mwindow_mutex)) != 0)
250             return;
251              
252             pthread_key_create(&_tls_key, &cb__free_status);
253              
254             init_error = init_common();
255             }
256              
257             int git_libgit2_init(void)
258             {
259             int ret, err;
260              
261             if ((err = pthread_mutex_lock(&_init_mutex)) != 0)
262             return err;
263              
264             ret = git_atomic_inc(&git__n_inits);
265             err = pthread_once(&_once_init, init_once);
266             err |= pthread_mutex_unlock(&_init_mutex);
267              
268             if (err || init_error)
269             return err | init_error;
270              
271             return ret;
272             }
273              
274             int git_libgit2_shutdown(void)
275             {
276             void *ptr = NULL;
277             pthread_once_t new_once = PTHREAD_ONCE_INIT;
278             int error, ret;
279              
280             if ((error = pthread_mutex_lock(&_init_mutex)) != 0)
281             return error;
282              
283             if ((ret = git_atomic_dec(&git__n_inits)) != 0)
284             goto out;
285              
286             /* Shut down any subsystems that have global state */
287             shutdown_common();
288              
289             ptr = pthread_getspecific(_tls_key);
290             pthread_setspecific(_tls_key, NULL);
291              
292             git__global_state_cleanup(ptr);
293             git__free(ptr);
294              
295             pthread_key_delete(_tls_key);
296             git_mutex_free(&git__mwindow_mutex);
297             _once_init = new_once;
298              
299             out:
300             if ((error = pthread_mutex_unlock(&_init_mutex)) != 0)
301             return error;
302              
303             return ret;
304             }
305              
306             git_global_st *git__global_state(void)
307             {
308             git_global_st *ptr;
309              
310             assert(git_atomic_get(&git__n_inits) > 0);
311              
312             if ((ptr = pthread_getspecific(_tls_key)) != NULL)
313             return ptr;
314              
315             ptr = git__calloc(1, sizeof(git_global_st));
316             if (!ptr)
317             return NULL;
318              
319             git_buf_init(&ptr->error_buf, 0);
320             pthread_setspecific(_tls_key, ptr);
321             return ptr;
322             }
323              
324             #else
325              
326             static git_global_st __state;
327              
328 86           int git_libgit2_init(void)
329             {
330             int ret;
331              
332             /* Only init subsystems the first time */
333 86 50         if ((ret = git_atomic_inc(&git__n_inits)) != 1)
334 0           return ret;
335              
336 86 50         if ((ret = init_common()) < 0)
337 0           return ret;
338              
339 86           return 1;
340             }
341              
342 0           int git_libgit2_shutdown(void)
343             {
344             int ret;
345              
346             /* Shut down any subsystems that have global state */
347 0 0         if ((ret = git_atomic_dec(&git__n_inits)) == 0) {
348 0           shutdown_common();
349 0           git__global_state_cleanup(&__state);
350 0           memset(&__state, 0, sizeof(__state));
351             }
352              
353 0           return ret;
354             }
355              
356 33444           git_global_st *git__global_state(void)
357             {
358 33444           return &__state;
359             }
360              
361             #endif /* GIT_THREADS */