diff --git a/src/global.c b/src/global.c index 4f024f17e..fe3a3c8b8 100644 --- a/src/global.c +++ b/src/global.c @@ -18,6 +18,7 @@ git_mutex git__mwindow_mutex; git_global_shutdown_fn git__shutdown_callbacks[MAX_SHUTDOWN_CB]; git_atomic git__n_shutdown_callbacks; +git_atomic git__n_inits; void git__on_shutdown(git_global_shutdown_fn callback) { @@ -73,47 +74,99 @@ static void git__shutdown(void) #if defined(GIT_THREADS) && defined(GIT_WIN32) static DWORD _tls_index; -static int _tls_init = 0; +static HANDLE _init_mutex = NULL; +static HANDLE _initialized_event = NULL; -int git_threads_init(void) +static int synchronized_init_and_claim_mutex(volatile HANDLE *m) +{ + if (*m == NULL) { + HANDLE nm = CreateMutex(NULL, FALSE, NULL); + if (InterlockedCompareExchangePointer(m, nm, NULL) != NULL) { + CloseHandle(nm); + } + } + return WaitForSingleObject(*m, INFINITE) == WAIT_FAILED; +} + +static int synchronized_threads_init() { int error; - if (_tls_init) - return 0; + if (synchronized_init_and_claim_mutex(&_init_mutex) != WAIT_OBJECT_0) { + giterr_set(GITERR_OS, "git_threads_init: WaitForSingleObject failed"); + return -1; + } + + if (!_initialized_event) { + _initialized_event = CreateEvent(NULL, TRUE, FALSE, NULL); + } _tls_index = TlsAlloc(); if (git_mutex_init(&git__mwindow_mutex)) return -1; /* Initialize any other subsystems that have global state */ - if ((error = git_hash_global_init()) >= 0 && - (error = git_futils_dirs_global_init()) >= 0) - _tls_init = 1; - - GIT_MEMORY_BARRIER; + if ((error = git_hash_global_init()) >= 0) + error = git_futils_dirs_global_init(); win32_pthread_initialize(); - + SetEvent(_initialized_event); + ReleaseMutex(_init_mutex); return error; } +int git_threads_init(void) +{ + int error = -1, + n_inits = git_atomic_inc(&git__n_inits); + + if (n_inits == 1) + error = synchronized_threads_init(); + else { + while (!_initialized_event); /* SPIN */ + error = (WaitForSingleObject(_initialized_event, INFINITE) == WAIT_OBJECT_0) ? 0 : -1; + } + + GIT_MEMORY_BARRIER; + return error; +} + +static void synchronized_threads_shutdown() +{ + if (WaitForSingleObject(_init_mutex, INFINITE) != WAIT_OBJECT_0) { + giterr_set(GITERR_OS, "git_threads_shutdown: WaitForSingleObject failed"); + return; + } + + if (git__n_inits.val != 0) { + /* Someone else initialized while we were waiting */ + ReleaseMutex(_init_mutex); + return; + } + ResetEvent(_initialized_event); + + /* Shut down any subsystems that have global state */ + git__shutdown(); + TlsFree(_tls_index); + git_mutex_free(&git__mwindow_mutex); + + ReleaseMutex(_init_mutex); +} + void git_threads_shutdown(void) { - /* Shut down any subsystems that have global state */ - git__shutdown(); + int n_inits = git_atomic_dec(&git__n_inits); - TlsFree(_tls_index); - _tls_init = 0; - - git_mutex_free(&git__mwindow_mutex); + if (n_inits == 0) + synchronized_threads_shutdown(); + GIT_MEMORY_BARRIER; } git_global_st *git__global_state(void) { void *ptr; - assert(_tls_init); + assert(git__n_inits.val); if ((ptr = TlsGetValue(_tls_index)) != NULL) return ptr; @@ -130,55 +183,57 @@ git_global_st *git__global_state(void) #elif defined(GIT_THREADS) && defined(_POSIX_THREADS) static pthread_key_t _tls_key; -static int _tls_init = 0; +static pthread_once_t _once_init = PTHREAD_ONCE_INIT; +int init_error = 0; static void cb__free_status(void *st) { git__free(st); } -int git_threads_init(void) +static void init_once(void) { - int error = 0; - - if (_tls_init) - return 0; - - if (git_mutex_init(&git__mwindow_mutex)) - return -1; + if ((init_error = git_mutex_init(&git__mwindow_mutex)) != 0) + return; pthread_key_create(&_tls_key, &cb__free_status); /* Initialize any other subsystems that have global state */ - if ((error = git_hash_global_init()) >= 0 && - (error = git_futils_dirs_global_init()) >= 0) - _tls_init = 1; + if ((init_error = git_hash_global_init()) >= 0) + init_error = git_futils_dirs_global_init(); GIT_MEMORY_BARRIER; +} - return error; +int git_threads_init(void) +{ + pthread_once(&_once_init, init_once); + git_atomic_inc(&git__n_inits); + return init_error; } void git_threads_shutdown(void) { + pthread_once_t new_once = PTHREAD_ONCE_INIT; + + if (git_atomic_dec(&git__n_inits) > 0) return; + /* Shut down any subsystems that have global state */ git__shutdown(); - if (_tls_init) { - void *ptr = pthread_getspecific(_tls_key); - pthread_setspecific(_tls_key, NULL); - git__free(ptr); - } + void *ptr = pthread_getspecific(_tls_key); + pthread_setspecific(_tls_key, NULL); + git__free(ptr); pthread_key_delete(_tls_key); - _tls_init = 0; git_mutex_free(&git__mwindow_mutex); + _once_init = new_once; } git_global_st *git__global_state(void) { void *ptr; - assert(_tls_init); + assert(git__n_inits.val); if ((ptr = pthread_getspecific(_tls_key)) != NULL) return ptr;