/src/libgit2/src/util/runtime.c
Line | Count | Source |
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 "git2_util.h" |
9 | | #include "runtime.h" |
10 | | |
11 | | static git_runtime_shutdown_fn shutdown_callback[32]; |
12 | | static git_atomic32 shutdown_callback_count; |
13 | | |
14 | | static git_atomic32 init_count; |
15 | | |
16 | | static int init_common(git_runtime_init_fn init_fns[], size_t cnt) |
17 | 2 | { |
18 | 2 | size_t i; |
19 | 2 | int ret; |
20 | | |
21 | | /* Initialize subsystems that have global state */ |
22 | 36 | for (i = 0; i < cnt; i++) { |
23 | 34 | if ((ret = init_fns[i]()) != 0) |
24 | 0 | break; |
25 | 34 | } |
26 | | |
27 | 2 | GIT_MEMORY_BARRIER; |
28 | | |
29 | 2 | return ret; |
30 | 2 | } |
31 | | |
32 | | static void shutdown_common(void) |
33 | 0 | { |
34 | 0 | git_runtime_shutdown_fn cb; |
35 | 0 | int pos; |
36 | |
|
37 | 0 | for (pos = git_atomic32_get(&shutdown_callback_count); |
38 | 0 | pos > 0; |
39 | 0 | pos = git_atomic32_dec(&shutdown_callback_count)) { |
40 | 0 | cb = git_atomic_swap(shutdown_callback[pos - 1], NULL); |
41 | |
|
42 | 0 | if (cb != NULL) |
43 | 0 | cb(); |
44 | 0 | } |
45 | 0 | } |
46 | | |
47 | | int git_runtime_shutdown_register(git_runtime_shutdown_fn callback) |
48 | 18 | { |
49 | 18 | int count = git_atomic32_inc(&shutdown_callback_count); |
50 | | |
51 | 18 | if (count > (int)ARRAY_SIZE(shutdown_callback) || count == 0) { |
52 | 0 | git_error_set(GIT_ERROR_INVALID, |
53 | 0 | "too many shutdown callbacks registered"); |
54 | 0 | git_atomic32_dec(&shutdown_callback_count); |
55 | 0 | return -1; |
56 | 0 | } |
57 | | |
58 | 18 | shutdown_callback[count - 1] = callback; |
59 | | |
60 | 18 | return 0; |
61 | 18 | } |
62 | | |
63 | | #if defined(GIT_THREADS) && defined(GIT_WIN32) |
64 | | |
65 | | /* |
66 | | * On Win32, we use a spinlock to provide locking semantics. This is |
67 | | * lighter-weight than a proper critical section. |
68 | | */ |
69 | | static volatile LONG init_spinlock = 0; |
70 | | |
71 | | GIT_INLINE(int) init_lock(void) |
72 | | { |
73 | | while (InterlockedCompareExchange(&init_spinlock, 1, 0)) { Sleep(0); } |
74 | | return 0; |
75 | | } |
76 | | |
77 | | GIT_INLINE(int) init_unlock(void) |
78 | | { |
79 | | InterlockedExchange(&init_spinlock, 0); |
80 | | return 0; |
81 | | } |
82 | | |
83 | | #elif defined(GIT_THREADS) && defined(_POSIX_THREADS) |
84 | | |
85 | | /* |
86 | | * On POSIX, we need to use a proper mutex for locking. We might prefer |
87 | | * a spinlock here, too, but there's no static initializer for a |
88 | | * pthread_spinlock_t. |
89 | | */ |
90 | | static pthread_mutex_t init_mutex = PTHREAD_MUTEX_INITIALIZER; |
91 | | |
92 | | GIT_INLINE(int) init_lock(void) |
93 | 2 | { |
94 | 2 | return pthread_mutex_lock(&init_mutex) == 0 ? 0 : -1; |
95 | 2 | } |
96 | | |
97 | | GIT_INLINE(int) init_unlock(void) |
98 | 2 | { |
99 | 2 | return pthread_mutex_unlock(&init_mutex) == 0 ? 0 : -1; |
100 | 2 | } |
101 | | |
102 | | #elif defined(GIT_THREADS) |
103 | | # error unknown threading model |
104 | | #else |
105 | | |
106 | | # define init_lock() git__noop() |
107 | | # define init_unlock() git__noop() |
108 | | |
109 | | #endif |
110 | | |
111 | | int git_runtime_init(git_runtime_init_fn init_fns[], size_t cnt) |
112 | 2 | { |
113 | 2 | int ret; |
114 | | |
115 | 2 | if (init_lock() < 0) |
116 | 0 | return -1; |
117 | | |
118 | | /* Only do work on a 0 -> 1 transition of the refcount */ |
119 | 2 | if ((ret = git_atomic32_inc(&init_count)) == 1) { |
120 | 2 | if (init_common(init_fns, cnt) < 0) |
121 | 0 | ret = -1; |
122 | 2 | } |
123 | | |
124 | 2 | if (init_unlock() < 0) |
125 | 0 | return -1; |
126 | | |
127 | 2 | return ret; |
128 | 2 | } |
129 | | |
130 | | int git_runtime_init_count(void) |
131 | 0 | { |
132 | 0 | int ret; |
133 | |
|
134 | 0 | if (init_lock() < 0) |
135 | 0 | return -1; |
136 | | |
137 | 0 | ret = git_atomic32_get(&init_count); |
138 | |
|
139 | 0 | if (init_unlock() < 0) |
140 | 0 | return -1; |
141 | | |
142 | 0 | return ret; |
143 | 0 | } |
144 | | |
145 | | int git_runtime_shutdown(void) |
146 | 0 | { |
147 | 0 | int ret; |
148 | | |
149 | | /* Enter the lock */ |
150 | 0 | if (init_lock() < 0) |
151 | 0 | return -1; |
152 | | |
153 | | /* Only do work on a 1 -> 0 transition of the refcount */ |
154 | 0 | if ((ret = git_atomic32_dec(&init_count)) == 0) |
155 | 0 | shutdown_common(); |
156 | | |
157 | | /* Exit the lock */ |
158 | 0 | if (init_unlock() < 0) |
159 | 0 | return -1; |
160 | | |
161 | 0 | return ret; |
162 | 0 | } |