Merge pull request #3974 from libgit2/pks/synchronize-shutdown global: synchronize initialization and shutdown with pthreads
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120
diff --git a/src/global.c b/src/global.c
index 45b1ab8..e2ad8fe 100644
--- a/src/global.c
+++ b/src/global.c
@@ -247,6 +247,7 @@ BOOL WINAPI DllMain(HINSTANCE hInstDll, DWORD fdwReason, LPVOID lpvReserved)
#elif defined(GIT_THREADS) && defined(_POSIX_THREADS)
static pthread_key_t _tls_key;
+static pthread_mutex_t _init_mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_once_t _once_init = PTHREAD_ONCE_INIT;
int init_error = 0;
@@ -268,12 +269,19 @@ static void init_once(void)
int git_libgit2_init(void)
{
- int ret;
+ int ret, err;
ret = git_atomic_inc(&git__n_inits);
- pthread_once(&_once_init, init_once);
- return init_error ? init_error : ret;
+ if ((err = pthread_mutex_lock(&_init_mutex)) != 0)
+ return err;
+ err = pthread_once(&_once_init, init_once);
+ err |= pthread_mutex_unlock(&_init_mutex);
+
+ if (err || init_error)
+ return err | init_error;
+
+ return ret;
}
int git_libgit2_shutdown(void)
@@ -285,6 +293,9 @@ int git_libgit2_shutdown(void)
if ((ret = git_atomic_dec(&git__n_inits)) != 0)
return ret;
+ if ((ret = pthread_mutex_lock(&_init_mutex)) != 0)
+ return ret;
+
/* Shut down any subsystems that have global state */
shutdown_common();
@@ -298,6 +309,9 @@ int git_libgit2_shutdown(void)
git_mutex_free(&git__mwindow_mutex);
_once_init = new_once;
+ if ((ret = pthread_mutex_unlock(&_init_mutex)) != 0)
+ return ret;
+
return 0;
}
@@ -327,7 +341,7 @@ int git_libgit2_init(void)
{
int ret;
- /* Only init SSL the first time */
+ /* Only init subsystems the first time */
if ((ret = git_atomic_inc(&git__n_inits)) != 1)
return ret;
@@ -345,6 +359,7 @@ int git_libgit2_shutdown(void)
if ((ret = git_atomic_dec(&git__n_inits)) == 0) {
shutdown_common();
git__global_state_cleanup(&__state);
+ memset(&__state, 0, sizeof(__state));
}
return ret;
diff --git a/tests/core/init.c b/tests/core/init.c
index e17b784..cd90b37 100644
--- a/tests/core/init.c
+++ b/tests/core/init.c
@@ -12,3 +12,43 @@ void test_core_init__returns_count(void)
cl_assert_equal_i(1, git_libgit2_shutdown());
}
+void test_core_init__reinit_succeeds(void)
+{
+ cl_assert_equal_i(0, git_libgit2_shutdown());
+ cl_assert_equal_i(1, git_libgit2_init());
+ cl_sandbox_set_search_path_defaults();
+}
+
+#ifdef GIT_THREADS
+static void *reinit(void *unused)
+{
+ unsigned i;
+
+ for (i = 0; i < 20; i++) {
+ cl_assert(git_libgit2_init() > 0);
+ cl_assert(git_libgit2_shutdown() >= 0);
+ }
+
+ return unused;
+}
+#endif
+
+void test_core_init__concurrent_init_succeeds(void)
+{
+#ifdef GIT_THREADS
+ git_thread threads[10];
+ unsigned i;
+
+ cl_assert_equal_i(0, git_libgit2_shutdown());
+
+ for (i = 0; i < ARRAY_SIZE(threads); i++)
+ git_thread_create(&threads[i], reinit, NULL);
+ for (i = 0; i < ARRAY_SIZE(threads); i++)
+ git_thread_join(&threads[i], NULL);
+
+ cl_assert_equal_i(1, git_libgit2_init());
+ cl_sandbox_set_search_path_defaults();
+#else
+ cl_skip();
+#endif
+}