diff options
Diffstat (limited to 'src/global.c')
| -rw-r--r-- | src/global.c | 251 |
1 files changed, 48 insertions, 203 deletions
diff --git a/src/global.c b/src/global.c index f67811a95..c4e925b73 100644 --- a/src/global.c +++ b/src/global.c @@ -8,6 +8,7 @@ #include "global.h" #include "alloc.h" +#include "threadstate.h" #include "hash.h" #include "sysdir.h" #include "filter.h" @@ -27,6 +28,7 @@ typedef int (*git_global_init_fn)(void); static git_global_init_fn git__init_callbacks[] = { git_allocator_global_init, + git_threadstate_global_init, git_threads_global_init, git_hash_global_init, git_sysdir_global_init, @@ -53,15 +55,6 @@ void git__on_shutdown(git_global_shutdown_fn callback) git__shutdown_callbacks[count - 1] = callback; } -static void git__global_state_cleanup(git_global_st *st) -{ - if (!st) - return; - - git__free(st->error_t.message); - st->error_t.message = NULL; -} - static int init_common(void) { size_t i; @@ -94,32 +87,6 @@ static void shutdown_common(void) } } -/** - * Handle the global state with TLS - * - * If libgit2 is built with GIT_THREADS enabled, - * the `git_libgit2_init()` function must be called - * before calling any other function of the library. - * - * This function allocates a TLS index (using pthreads - * or the native Win32 API) to store the global state - * on a per-thread basis. - * - * Any internal method that requires global state will - * then call `git__global_state()` which returns a pointer - * to the global state structure; this pointer is lazily - * allocated on each thread. - * - * Before shutting down the library, the - * `git_libgit2_shutdown` method must be called to free - * the previously reserved TLS index. - * - * If libgit2 is built without threading support, the - * `git__global_statestate()` call returns a pointer to a single, - * statically allocated global state. The `git_thread_` - * functions are not available in that case. - */ - /* * `git_libgit2_init()` allows subsystems to perform global setup, * which may take place in the global scope. An explicit memory @@ -130,42 +97,67 @@ static void shutdown_common(void) */ #if defined(GIT_THREADS) && defined(GIT_WIN32) -static DWORD _fls_index; -static volatile LONG _mutex = 0; +/* + * On Win32, we use a spinlock to provide locking semantics. This is + * lighter-weight than a proper critical section. + */ +static volatile LONG init_spinlock = 0; -static void WINAPI fls_free(void *st) +GIT_INLINE(int) init_lock(void) { - git__global_state_cleanup(st); - git__free(st); + while (InterlockedCompareExchange(&init_spinlock, 1, 0)) { Sleep(0); } + return 0; } -static int synchronized_threads_init(void) +GIT_INLINE(int) init_unlock(void) { - int error; + InterlockedExchange(&init_spinlock, 0); + return 0; +} - if ((_fls_index = FlsAlloc(fls_free)) == FLS_OUT_OF_INDEXES) - return -1; +#elif defined(GIT_THREADS) && defined(_POSIX_THREADS) - error = init_common(); +/* + * On POSIX, we need to use a proper mutex for locking. We might prefer + * a spinlock here, too, but there's no static initializer for a + * pthread_spinlock_t. + */ +static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; - return error; +GIT_INLINE(int) init_lock(void) +{ + return pthread_mutex_lock(&mutex) == 0 ? 0 : -1; } +GIT_INLINE(int) init_unlock(void) +{ + return pthread_mutex_unlock(&mutex) == 0 ? 0 : -1; +} + +#elif defined(GIT_THREADS) +# error unknown threading model +#else + +# define init_lock() 0 +# define init_unlock() 0 + +#endif + int git_libgit2_init(void) { int ret; - /* Enter the lock */ - while (InterlockedCompareExchange(&_mutex, 1, 0)) { Sleep(0); } + if (init_lock() < 0) + return -1; /* Only do work on a 0 -> 1 transition of the refcount */ if ((ret = git_atomic_inc(&git__n_inits)) == 1) { - if (synchronized_threads_init() < 0) + if (init_common() < 0) ret = -1; } - /* Exit the lock */ - InterlockedExchange(&_mutex, 0); + if (init_unlock() < 0) + return -1; return ret; } @@ -175,163 +167,16 @@ int git_libgit2_shutdown(void) int ret; /* Enter the lock */ - while (InterlockedCompareExchange(&_mutex, 1, 0)) { Sleep(0); } + if (init_lock() < 0) + return -1; /* Only do work on a 1 -> 0 transition of the refcount */ - if ((ret = git_atomic_dec(&git__n_inits)) == 0) { + if ((ret = git_atomic_dec(&git__n_inits)) == 0) shutdown_common(); - FlsFree(_fls_index); - } - /* Exit the lock */ - InterlockedExchange(&_mutex, 0); - - return ret; -} - -git_global_st *git__global_state(void) -{ - git_global_st *ptr; - - assert(git_atomic_get(&git__n_inits) > 0); - - if ((ptr = FlsGetValue(_fls_index)) != NULL) - return ptr; - - ptr = git__calloc(1, sizeof(git_global_st)); - if (!ptr) - return NULL; - - if (git_buf_init(&ptr->error_buf, 0) < 0) - return NULL; - - FlsSetValue(_fls_index, ptr); - return ptr; -} - -#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; - -static void cb__free_status(void *st) -{ - git__global_state_cleanup(st); - git__free(st); -} - -static void init_once(void) -{ - pthread_key_create(&_tls_key, &cb__free_status); - init_error = init_common(); -} - -int git_libgit2_init(void) -{ - int ret, err; - - if ((err = pthread_mutex_lock(&_init_mutex)) != 0) - return err; - - ret = git_atomic_inc(&git__n_inits); - 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) -{ - void *ptr = NULL; - pthread_once_t new_once = PTHREAD_ONCE_INIT; - int error, ret; - - if ((error = pthread_mutex_lock(&_init_mutex)) != 0) - return error; - - if ((ret = git_atomic_dec(&git__n_inits)) != 0) - goto out; - - /* Shut down any subsystems that have global state */ - shutdown_common(); - - ptr = pthread_getspecific(_tls_key); - pthread_setspecific(_tls_key, NULL); - - git__global_state_cleanup(ptr); - git__free(ptr); - - pthread_key_delete(_tls_key); - _once_init = new_once; - -out: - if ((error = pthread_mutex_unlock(&_init_mutex)) != 0) - return error; - - return ret; -} - -git_global_st *git__global_state(void) -{ - git_global_st *ptr; - - assert(git_atomic_get(&git__n_inits) > 0); - - if ((ptr = pthread_getspecific(_tls_key)) != NULL) - return ptr; - - ptr = git__calloc(1, sizeof(git_global_st)); - if (!ptr) - return NULL; - - if (git_buf_init(&ptr->error_buf, 0) < 0) - return NULL; - - pthread_setspecific(_tls_key, ptr); - return ptr; -} - -#else - -static git_global_st __state; - -int git_libgit2_init(void) -{ - int ret; - - /* Only init subsystems the first time */ - if ((ret = git_atomic_inc(&git__n_inits)) != 1) - return ret; - - if ((ret = init_common()) < 0) - return ret; - - return 1; -} - -int git_libgit2_shutdown(void) -{ - int ret; - - /* Shut down any subsystems that have global state */ - if ((ret = git_atomic_dec(&git__n_inits)) == 0) { - shutdown_common(); - git__global_state_cleanup(&__state); - memset(&__state, 0, sizeof(__state)); - } + if (init_unlock() < 0) + return -1; return ret; } - -git_global_st *git__global_state(void) -{ - return &__state; -} - -#endif /* GIT_THREADS */ |
