Commit 59c6c286 by Patrick Steinhardt

global: synchronize initialization and shutdown with pthreads

When trying to initialize and tear down global data structures
from different threads at once with `git_libgit2_init` and
`git_libgit2_shutdown`, we race around initializing data. While
we use `pthread_once` to assert that we only initilize data a
single time, we actually reset the `pthread_once_t` on the last
call to `git_libgit2_shutdown`. As resetting this variable is not
synchronized with other threads trying to access it, this is
actually racy when one thread tries to do a complete shutdown of
libgit2 while another thread tries to initialize it.

Fix the issue by creating a mutex which synchronizes `init_once`
and the library shutdown.
parent 41ad9ebf
...@@ -247,6 +247,7 @@ BOOL WINAPI DllMain(HINSTANCE hInstDll, DWORD fdwReason, LPVOID lpvReserved) ...@@ -247,6 +247,7 @@ BOOL WINAPI DllMain(HINSTANCE hInstDll, DWORD fdwReason, LPVOID lpvReserved)
#elif defined(GIT_THREADS) && defined(_POSIX_THREADS) #elif defined(GIT_THREADS) && defined(_POSIX_THREADS)
static pthread_key_t _tls_key; static pthread_key_t _tls_key;
static pthread_mutex_t _init_mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_once_t _once_init = PTHREAD_ONCE_INIT; static pthread_once_t _once_init = PTHREAD_ONCE_INIT;
int init_error = 0; int init_error = 0;
...@@ -268,12 +269,19 @@ static void init_once(void) ...@@ -268,12 +269,19 @@ static void init_once(void)
int git_libgit2_init(void) int git_libgit2_init(void)
{ {
int ret; int ret, err;
ret = git_atomic_inc(&git__n_inits); 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) int git_libgit2_shutdown(void)
...@@ -285,6 +293,9 @@ int git_libgit2_shutdown(void) ...@@ -285,6 +293,9 @@ int git_libgit2_shutdown(void)
if ((ret = git_atomic_dec(&git__n_inits)) != 0) if ((ret = git_atomic_dec(&git__n_inits)) != 0)
return ret; return ret;
if ((ret = pthread_mutex_lock(&_init_mutex)) != 0)
return ret;
/* Shut down any subsystems that have global state */ /* Shut down any subsystems that have global state */
shutdown_common(); shutdown_common();
...@@ -298,6 +309,9 @@ int git_libgit2_shutdown(void) ...@@ -298,6 +309,9 @@ int git_libgit2_shutdown(void)
git_mutex_free(&git__mwindow_mutex); git_mutex_free(&git__mwindow_mutex);
_once_init = new_once; _once_init = new_once;
if ((ret = pthread_mutex_unlock(&_init_mutex)) != 0)
return ret;
return 0; return 0;
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment