diff options
author | Edward Thomson <ethomson@edwardthomson.com> | 2020-10-14 13:44:59 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-10-14 13:44:59 +0100 |
commit | 20450cbe7d161abbb3eb52d96867aeaa6dd99e12 (patch) | |
tree | e38c7f3203a88ed6d95c1326e882e594852b826d | |
parent | e9858645b26adb7aa2dc9034034bc5744b3cd690 (diff) | |
parent | 634c285a0eb016a2d144b35298dabc350d2e7576 (diff) | |
download | libgit2-20450cbe7d161abbb3eb52d96867aeaa6dd99e12.tar.gz |
Merge pull request #5546 from libgit2/ethomson/init
Refactor "global" state
40 files changed, 588 insertions, 493 deletions
diff --git a/src/alloc.c b/src/alloc.c index 51c4d8029..6972e7b59 100644 --- a/src/alloc.c +++ b/src/alloc.c @@ -6,10 +6,16 @@ */ #include "alloc.h" +#include "runtime.h" #include "allocators/stdalloc.h" #include "allocators/win32_crtdbg.h" +#if defined(GIT_MSVC_CRTDBG) +# include "win32/w32_stack.h" +# include "win32/w32_crtdbg_stacktrace.h" +#endif + git_allocator git__allocator; static int setup_default_allocator(void) @@ -21,8 +27,24 @@ static int setup_default_allocator(void) #endif } +#if defined(GIT_MSVC_CRTDBG) +static void allocator_global_shutdown(void) +{ + git_win32__crtdbg_stacktrace_cleanup(); + git_win32__stack_cleanup(); +} +#endif + int git_allocator_global_init(void) { +#if defined(GIT_MSVC_CRTDBG) + git_win32__crtdbg_stacktrace_init(); + git_win32__stack_init(); + + if (git_runtime_shutdown_register(allocator_global_shutdown) < 0) + return -1; +#endif + /* * We don't want to overwrite any allocator which has been set before * the init function is called. diff --git a/src/allocators/win32_crtdbg.c b/src/allocators/win32_crtdbg.c index 1187e2fcd..c726268a8 100644 --- a/src/allocators/win32_crtdbg.c +++ b/src/allocators/win32_crtdbg.c @@ -9,6 +9,7 @@ #if defined(GIT_MSVC_CRTDBG) +#include "win32/w32_stack.h" #include "win32/w32_crtdbg_stacktrace.h" static void *crtdbg__malloc(size_t len, const char *file, int line) diff --git a/src/errors.c b/src/errors.c index 8570226b4..d4da50da8 100644 --- a/src/errors.c +++ b/src/errors.c @@ -7,7 +7,7 @@ #include "common.h" -#include "global.h" +#include "threadstate.h" #include "posix.h" #include "buffer.h" @@ -22,18 +22,18 @@ static git_error g_git_oom_error = { static void set_error_from_buffer(int error_class) { - git_error *error = &GIT_GLOBAL->error_t; - git_buf *buf = &GIT_GLOBAL->error_buf; + git_error *error = &GIT_THREADSTATE->error_t; + git_buf *buf = &GIT_THREADSTATE->error_buf; error->message = buf->ptr; error->klass = error_class; - GIT_GLOBAL->last_error = error; + GIT_THREADSTATE->last_error = error; } static void set_error(int error_class, char *string) { - git_buf *buf = &GIT_GLOBAL->error_buf; + git_buf *buf = &GIT_THREADSTATE->error_buf; git_buf_clear(buf); if (string) { @@ -46,7 +46,7 @@ static void set_error(int error_class, char *string) void git_error_set_oom(void) { - GIT_GLOBAL->last_error = &g_git_oom_error; + GIT_THREADSTATE->last_error = &g_git_oom_error; } void git_error_set(int error_class, const char *fmt, ...) @@ -64,7 +64,7 @@ void git_error_vset(int error_class, const char *fmt, va_list ap) DWORD win32_error_code = (error_class == GIT_ERROR_OS) ? GetLastError() : 0; #endif int error_code = (error_class == GIT_ERROR_OS) ? errno : 0; - git_buf *buf = &GIT_GLOBAL->error_buf; + git_buf *buf = &GIT_THREADSTATE->error_buf; git_buf_clear(buf); if (fmt) { @@ -97,7 +97,7 @@ void git_error_vset(int error_class, const char *fmt, va_list ap) int git_error_set_str(int error_class, const char *string) { - git_buf *buf = &GIT_GLOBAL->error_buf; + git_buf *buf = &GIT_THREADSTATE->error_buf; assert(string); @@ -118,9 +118,9 @@ int git_error_set_str(int error_class, const char *string) void git_error_clear(void) { - if (GIT_GLOBAL->last_error != NULL) { + if (GIT_THREADSTATE->last_error != NULL) { set_error(0, NULL); - GIT_GLOBAL->last_error = NULL; + GIT_THREADSTATE->last_error = NULL; } errno = 0; @@ -131,13 +131,13 @@ void git_error_clear(void) const git_error *git_error_last(void) { - return GIT_GLOBAL->last_error; + return GIT_THREADSTATE->last_error; } int git_error_state_capture(git_error_state *state, int error_code) { - git_error *error = GIT_GLOBAL->last_error; - git_buf *error_buf = &GIT_GLOBAL->error_buf; + git_error *error = GIT_THREADSTATE->last_error; + git_buf *error_buf = &GIT_THREADSTATE->error_buf; memset(state, 0, sizeof(git_error_state)); diff --git a/src/filter.c b/src/filter.c index 09b57dc80..86db5a5e3 100644 --- a/src/filter.c +++ b/src/filter.c @@ -11,7 +11,7 @@ #include "futils.h" #include "hash.h" #include "repository.h" -#include "global.h" +#include "runtime.h" #include "git2/sys/filter.h" #include "git2/config.h" #include "blob.h" @@ -206,7 +206,7 @@ int git_filter_global_init(void) GIT_FILTER_IDENT, ident, GIT_FILTER_IDENT_PRIORITY) < 0) error = -1; - git__on_shutdown(git_filter_global_shutdown); + error = git_runtime_shutdown_register(git_filter_global_shutdown); done: if (error) { diff --git a/src/futils.c b/src/futils.c index 8c0f008b3..2ad8a1b80 100644 --- a/src/futils.c +++ b/src/futils.c @@ -7,8 +7,9 @@ #include "futils.h" -#include "global.h" +#include "runtime.h" #include "strmap.h" +#include "hash.h" #include <ctype.h> #if GIT_WIN32 #include "win32/findfile.h" diff --git a/src/global.c b/src/global.c deleted file mode 100644 index 9fe8cd5d3..000000000 --- a/src/global.c +++ /dev/null @@ -1,363 +0,0 @@ -/* - * Copyright (C) the libgit2 contributors. All rights reserved. - * - * This file is part of libgit2, distributed under the GNU GPL v2 with - * a Linking Exception. For full terms see the included COPYING file. - */ - -#include "global.h" - -#include "alloc.h" -#include "hash.h" -#include "sysdir.h" -#include "filter.h" -#include "merge_driver.h" -#include "pool.h" -#include "streams/registry.h" -#include "streams/mbedtls.h" -#include "streams/openssl.h" -#include "thread-utils.h" -#include "git2/global.h" -#include "transports/ssh.h" - -#if defined(GIT_MSVC_CRTDBG) -#include "win32/w32_stack.h" -#include "win32/w32_crtdbg_stacktrace.h" -#endif - -git_mutex git__mwindow_mutex; - -typedef int (*git_global_init_fn)(void); - -static git_global_init_fn git__init_callbacks[] = { - git_allocator_global_init, - git_hash_global_init, - git_sysdir_global_init, - git_filter_global_init, - git_merge_driver_global_init, - git_transport_ssh_global_init, - git_stream_registry_global_init, - git_openssl_stream_global_init, - git_mbedtls_stream_global_init, - git_mwindow_global_init, - git_pool_global_init -}; - -static git_global_shutdown_fn git__shutdown_callbacks[ARRAY_SIZE(git__init_callbacks)]; - -static git_atomic git__n_shutdown_callbacks; -static git_atomic git__n_inits; -char *git__user_agent; -char *git__ssl_ciphers; - -void git__on_shutdown(git_global_shutdown_fn callback) -{ - int count = git_atomic_inc(&git__n_shutdown_callbacks); - assert(count <= (int) ARRAY_SIZE(git__shutdown_callbacks) && count > 0); - 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; - int ret; - - /* Initialize the CRT debug allocator first, before our first malloc */ -#if defined(GIT_MSVC_CRTDBG) - git_win32__crtdbg_stacktrace_init(); - git_win32__stack_init(); -#endif - - /* Initialize subsystems that have global state */ - for (i = 0; i < ARRAY_SIZE(git__init_callbacks); i++) - if ((ret = git__init_callbacks[i]()) != 0) - break; - - GIT_MEMORY_BARRIER; - - return ret; -} - -static void shutdown_common(void) -{ - int pos; - - /* Shutdown subsystems that have registered */ - for (pos = git_atomic_get(&git__n_shutdown_callbacks); - pos > 0; - pos = git_atomic_dec(&git__n_shutdown_callbacks)) { - - git_global_shutdown_fn cb = git__swap( - git__shutdown_callbacks[pos - 1], NULL); - - if (cb != NULL) - cb(); - } - - git__free(git__user_agent); - git__free(git__ssl_ciphers); -} - -/** - * 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 - * fence exists at the exit of `git_libgit2_init()`. Without this, - * CPU cores are free to reorder cache invalidation of `_tls_init` - * before cache invalidation of the subsystems' newly written global - * state. - */ -#if defined(GIT_THREADS) && defined(GIT_WIN32) - -static DWORD _fls_index; -static volatile LONG _mutex = 0; - -static void WINAPI fls_free(void *st) -{ - git__global_state_cleanup(st); - git__free(st); -} - -static int synchronized_threads_init(void) -{ - int error; - - if ((_fls_index = FlsAlloc(fls_free)) == FLS_OUT_OF_INDEXES) - return -1; - - git_threads_init(); - - if (git_mutex_init(&git__mwindow_mutex)) - return -1; - - error = init_common(); - - return error; -} - -int git_libgit2_init(void) -{ - int ret; - - /* Enter the lock */ - while (InterlockedCompareExchange(&_mutex, 1, 0)) { Sleep(0); } - - /* 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) - ret = -1; - } - - /* Exit the lock */ - InterlockedExchange(&_mutex, 0); - - return ret; -} - -int git_libgit2_shutdown(void) -{ - int ret; - - /* Enter the lock */ - while (InterlockedCompareExchange(&_mutex, 1, 0)) { Sleep(0); } - - /* Only do work on a 1 -> 0 transition of the refcount */ - if ((ret = git_atomic_dec(&git__n_inits)) == 0) { - shutdown_common(); - - FlsFree(_fls_index); - git_mutex_free(&git__mwindow_mutex); - -#if defined(GIT_MSVC_CRTDBG) - git_win32__crtdbg_stacktrace_cleanup(); - git_win32__stack_cleanup(); -#endif - } - - /* 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; - - git_buf_init(&ptr->error_buf, 0); - - 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) -{ - if ((init_error = git_mutex_init(&git__mwindow_mutex)) != 0) - return; - - 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); - git_mutex_free(&git__mwindow_mutex); - _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; - - git_buf_init(&ptr->error_buf, 0); - 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)); - } - - return ret; -} - -git_global_st *git__global_state(void) -{ - return &__state; -} - -#endif /* GIT_THREADS */ diff --git a/src/global.h b/src/global.h deleted file mode 100644 index db41dad1f..000000000 --- a/src/global.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) the libgit2 contributors. All rights reserved. - * - * This file is part of libgit2, distributed under the GNU GPL v2 with - * a Linking Exception. For full terms see the included COPYING file. - */ -#ifndef INCLUDE_global_h__ -#define INCLUDE_global_h__ - -#include "common.h" - -#include "mwindow.h" -#include "hash.h" - -typedef struct { - git_error *last_error; - git_error error_t; - git_buf error_buf; - char oid_fmt[GIT_OID_HEXSZ+1]; - - /* On Windows, this is the current child thread that was started by - * `git_thread_create`. This is used to set the thread's exit code - * when terminated by `git_thread_exit`. It is unused on POSIX. - */ - git_thread *current_thread; -} git_global_st; - -git_global_st *git__global_state(void); - -extern git_mutex git__mwindow_mutex; - -#define GIT_GLOBAL (git__global_state()) - -typedef void (*git_global_shutdown_fn)(void); - -extern void git__on_shutdown(git_global_shutdown_fn callback); - -extern const char *git_libgit2__user_agent(void); -extern const char *git_libgit2__ssl_ciphers(void); - -#endif diff --git a/src/hash/sha1/win32.c b/src/hash/sha1/win32.c index c73695665..b1266cca0 100644 --- a/src/hash/sha1/win32.c +++ b/src/hash/sha1/win32.c @@ -7,7 +7,7 @@ #include "win32.h" -#include "global.h" +#include "runtime.h" #include <wincrypt.h> #include <strsafe.h> @@ -129,7 +129,8 @@ int git_hash_sha1_global_init(void) if ((error = hash_cng_prov_init()) < 0) error = hash_cryptoapi_prov_init(); - git__on_shutdown(sha1_shutdown); + if (!error) + error = git_runtime_shutdown_register(sha1_shutdown); return error; } diff --git a/src/settings.c b/src/libgit2.c index 69ebcb7ab..316d89365 100644 --- a/src/settings.c +++ b/src/libgit2.c @@ -5,7 +5,33 @@ * a Linking Exception. For full terms see the included COPYING file. */ +#include "libgit2.h" + +#include <git2.h> +#include "alloc.h" +#include "cache.h" #include "common.h" +#include "filter.h" +#include "hash.h" +#include "index.h" +#include "merge_driver.h" +#include "pool.h" +#include "mwindow.h" +#include "object.h" +#include "odb.h" +#include "refs.h" +#include "runtime.h" +#include "sysdir.h" +#include "thread-utils.h" +#include "threadstate.h" +#include "git2/global.h" +#include "streams/registry.h" +#include "streams/mbedtls.h" +#include "streams/openssl.h" +#include "transports/smart.h" +#include "transports/http.h" +#include "transports/ssh.h" +#include "win32/w32_stack.h" #ifdef GIT_OPENSSL # include <openssl/err.h> @@ -15,19 +41,53 @@ # include <mbedtls/error.h> #endif -#include <git2.h> -#include "alloc.h" -#include "sysdir.h" -#include "cache.h" -#include "global.h" -#include "object.h" -#include "odb.h" -#include "refs.h" -#include "index.h" -#include "transports/smart.h" -#include "transports/http.h" -#include "streams/openssl.h" -#include "streams/mbedtls.h" +/* Declarations for tuneable settings */ +extern size_t git_mwindow__window_size; +extern size_t git_mwindow__mapped_limit; +extern size_t git_mwindow__file_limit; +extern size_t git_indexer__max_objects; +extern bool git_disable_pack_keep_file_checks; + +char *git__user_agent; +char *git__ssl_ciphers; + +static void libgit2_settings_global_shutdown(void) +{ + git__free(git__user_agent); + git__free(git__ssl_ciphers); +} + +static int git_libgit2_settings_global_init(void) +{ + return git_runtime_shutdown_register(libgit2_settings_global_shutdown); +} + +int git_libgit2_init(void) +{ + static git_runtime_init_fn init_fns[] = { + git_allocator_global_init, + git_threadstate_global_init, + git_threads_global_init, + git_hash_global_init, + git_sysdir_global_init, + git_filter_global_init, + git_merge_driver_global_init, + git_transport_ssh_global_init, + git_stream_registry_global_init, + git_openssl_stream_global_init, + git_mbedtls_stream_global_init, + git_mwindow_global_init, + git_pool_global_init, + git_libgit2_settings_global_init + }; + + return git_runtime_init(init_fns, ARRAY_SIZE(init_fns)); +} + +int git_libgit2_shutdown(void) +{ + return git_runtime_shutdown(); +} int git_libgit2_version(int *major, int *minor, int *rev) { @@ -56,13 +116,6 @@ int git_libgit2_features(void) ; } -/* Declarations for tuneable settings */ -extern size_t git_mwindow__window_size; -extern size_t git_mwindow__mapped_limit; -extern size_t git_mwindow__file_limit; -extern size_t git_indexer__max_objects; -extern bool git_disable_pack_keep_file_checks; - static int config_level_to_sysdir(int config_level) { int val = -1; @@ -88,9 +141,6 @@ static int config_level_to_sysdir(int config_level) return val; } -extern char *git__user_agent; -extern char *git__ssl_ciphers; - const char *git_libgit2__user_agent(void) { return git__user_agent; diff --git a/src/libgit2.h b/src/libgit2.h new file mode 100644 index 000000000..6f92a8318 --- /dev/null +++ b/src/libgit2.h @@ -0,0 +1,13 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ +#ifndef INCLUDE_libgit2_h__ +#define INCLUDE_libgit2_h__ + +extern const char *git_libgit2__user_agent(void); +extern const char *git_libgit2__ssl_ciphers(void); + +#endif diff --git a/src/merge_driver.c b/src/merge_driver.c index 666349b15..b1f574801 100644 --- a/src/merge_driver.c +++ b/src/merge_driver.c @@ -8,7 +8,7 @@ #include "merge_driver.h" #include "vector.h" -#include "global.h" +#include "runtime.h" #include "merge.h" #include "git2/merge.h" #include "git2/sys/merge.h" @@ -209,7 +209,7 @@ int git_merge_driver_global_init(void) merge_driver_name__binary, &git_merge_driver__binary)) < 0) goto done; - git__on_shutdown(git_merge_driver_global_shutdown); + error = git_runtime_shutdown_register(git_merge_driver_global_shutdown); done: if (error < 0) diff --git a/src/mwindow.c b/src/mwindow.c index ac26452f4..a852d6bf2 100644 --- a/src/mwindow.c +++ b/src/mwindow.c @@ -10,7 +10,7 @@ #include "vector.h" #include "futils.h" #include "map.h" -#include "global.h" +#include "runtime.h" #include "strmap.h" #include "pack.h" @@ -29,26 +29,36 @@ size_t git_mwindow__window_size = DEFAULT_WINDOW_SIZE; size_t git_mwindow__mapped_limit = DEFAULT_MAPPED_LIMIT; size_t git_mwindow__file_limit = DEFAULT_FILE_LIMIT; +/* Mutex to control access */ +git_mutex git__mwindow_mutex; + /* Whenever you want to read or modify this, grab git__mwindow_mutex */ git_mwindow_ctl git_mwindow__mem_ctl; /* Global list of mwindow files, to open packs once across repos */ git_strmap *git__pack_cache = NULL; -static void git_mwindow_files_free(void) +static void git_mwindow_global_shutdown(void) { git_strmap *tmp = git__pack_cache; + git_mutex_free(&git__mwindow_mutex); + git__pack_cache = NULL; git_strmap_free(tmp); } int git_mwindow_global_init(void) { + int error; + assert(!git__pack_cache); - git__on_shutdown(git_mwindow_files_free); - return git_strmap_new(&git__pack_cache); + if ((error = git_mutex_init(&git__mwindow_mutex)) < 0 || + (error = git_strmap_new(&git__pack_cache)) < 0) + return error; + + return git_runtime_shutdown_register(git_mwindow_global_shutdown); } int git_mwindow_get_pack(struct git_pack_file **out, const char *path) diff --git a/src/mwindow.h b/src/mwindow.h index 1a391b055..7519fc36d 100644 --- a/src/mwindow.h +++ b/src/mwindow.h @@ -13,6 +13,8 @@ #include "map.h" #include "vector.h" +extern git_mutex git__mwindow_mutex; + typedef struct git_mwindow { struct git_mwindow *next; git_map window_map; @@ -14,7 +14,7 @@ #include "posix.h" #include "buffer.h" #include "http_parser.h" -#include "global.h" +#include "runtime.h" #define DEFAULT_PORT_HTTP "80" #define DEFAULT_PORT_HTTPS "443" diff --git a/src/netops.c b/src/netops.c index 04ae824cc..1ef230293 100644 --- a/src/netops.c +++ b/src/netops.c @@ -13,7 +13,7 @@ #include "posix.h" #include "buffer.h" #include "http_parser.h" -#include "global.h" +#include "runtime.h" int gitno_recv(gitno_buffer *buf) { @@ -9,7 +9,7 @@ #include "git2/oid.h" #include "repository.h" -#include "global.h" +#include "threadstate.h" #include <string.h> #include <limits.h> @@ -107,7 +107,7 @@ int git_oid_pathfmt(char *str, const git_oid *oid) char *git_oid_tostr_s(const git_oid *oid) { - char *str = GIT_GLOBAL->oid_fmt; + char *str = GIT_THREADSTATE->oid_fmt; git_oid_nfmt(str, GIT_OID_HEXSZ + 1, oid); return str; } diff --git a/src/runtime.c b/src/runtime.c new file mode 100644 index 000000000..56110c49a --- /dev/null +++ b/src/runtime.c @@ -0,0 +1,147 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ + +#include "common.h" +#include "runtime.h" + +static git_runtime_shutdown_fn shutdown_callback[32]; +static git_atomic shutdown_callback_count; + +static git_atomic init_count; + +static int init_common(git_runtime_init_fn init_fns[], size_t cnt) +{ + size_t i; + int ret; + + /* Initialize subsystems that have global state */ + for (i = 0; i < cnt; i++) { + if ((ret = init_fns[i]()) != 0) + break; + } + + GIT_MEMORY_BARRIER; + + return ret; +} + +static void shutdown_common(void) +{ + git_runtime_shutdown_fn cb; + int pos; + + for (pos = git_atomic_get(&shutdown_callback_count); + pos > 0; + pos = git_atomic_dec(&shutdown_callback_count)) { + cb = git__swap(shutdown_callback[pos - 1], NULL); + + if (cb != NULL) + cb(); + } +} + +int git_runtime_shutdown_register(git_runtime_shutdown_fn callback) +{ + int count = git_atomic_inc(&shutdown_callback_count); + + if (count > (int)ARRAY_SIZE(shutdown_callback) || count == 0) { + git_error_set(GIT_ERROR_INVALID, + "too many shutdown callbacks registered"); + git_atomic_dec(&shutdown_callback_count); + return -1; + } + + shutdown_callback[count - 1] = callback; + + return 0; +} + +#if defined(GIT_THREADS) && defined(GIT_WIN32) + +/* + * 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; + +GIT_INLINE(int) init_lock(void) +{ + while (InterlockedCompareExchange(&init_spinlock, 1, 0)) { Sleep(0); } + return 0; +} + +GIT_INLINE(int) init_unlock(void) +{ + InterlockedExchange(&init_spinlock, 0); + return 0; +} + +#elif defined(GIT_THREADS) && defined(_POSIX_THREADS) + +/* + * 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 init_mutex = PTHREAD_MUTEX_INITIALIZER; + +GIT_INLINE(int) init_lock(void) +{ + return pthread_mutex_lock(&init_mutex) == 0 ? 0 : -1; +} + +GIT_INLINE(int) init_unlock(void) +{ + return pthread_mutex_unlock(&init_mutex) == 0 ? 0 : -1; +} + +#elif defined(GIT_THREADS) +# error unknown threading model +#else + +# define mutex_lock() 0 +# define mutex_unlock() 0 + +#endif + +int git_runtime_init(git_runtime_init_fn init_fns[], size_t cnt) +{ + int ret; + + if (init_lock() < 0) + return -1; + + /* Only do work on a 0 -> 1 transition of the refcount */ + if ((ret = git_atomic_inc(&init_count)) == 1) { + if (init_common(init_fns, cnt) < 0) + ret = -1; + } + + if (init_unlock() < 0) + return -1; + + return ret; +} + +int git_runtime_shutdown(void) +{ + int ret; + + /* Enter the lock */ + if (init_lock() < 0) + return -1; + + /* Only do work on a 1 -> 0 transition of the refcount */ + if ((ret = git_atomic_dec(&init_count)) == 0) + shutdown_common(); + + /* Exit the lock */ + if (init_unlock() < 0) + return -1; + + return ret; +} diff --git a/src/runtime.h b/src/runtime.h new file mode 100644 index 000000000..be2e37a60 --- /dev/null +++ b/src/runtime.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ +#ifndef INCLUDE_runtime_h__ +#define INCLUDE_runtime_h__ + +#include "common.h" + +typedef int (*git_runtime_init_fn)(void); +typedef void (*git_runtime_shutdown_fn)(void); + +/** + * Start up a new runtime. If this is the first time that this + * function is called within the context of the current library + * or executable, then the given `init_fns` will be invoked. If + * it is not the first time, they will be ignored. + * + * The given initialization functions _may_ register shutdown + * handlers using `git_runtime_shutdown_register` to be notified + * when the runtime is shutdown. + * + * @param init_fns The list of initialization functions to call + * @param cnt The number of init_fns + * @return The number of initializations performed (including this one) or an error + */ +int git_runtime_init(git_runtime_init_fn init_fns[], size_t cnt); + +/** + * Shut down the runtime. If this is the last shutdown call, + * such that there are no remaining `init` calls, then any + * shutdown hooks that have been registered will be invoked. + * + * The number of oustanding initializations will be returned. + * If this number is 0, then the runtime is shutdown. + * + * @return The number of outstanding initializations (after this one) or an error + */ +int git_runtime_shutdown(void); + +/** + * Register a shutdown handler for this runtime. This should be done + * by a function invoked by `git_runtime_init` to ensure that the + * appropriate locks are taken. + * + * @param callback The shutdown handler callback + * @return 0 or an error code + */ +int git_runtime_shutdown_register(git_runtime_shutdown_fn callback); + +#endif diff --git a/src/settings.h b/src/settings.h new file mode 100644 index 000000000..dc42ce939 --- /dev/null +++ b/src/settings.h @@ -0,0 +1,11 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ + +extern int git_settings_global_init(void); + +extern const char *git_libgit2__user_agent(void); +extern const char *git_libgit2__ssl_ciphers(void); diff --git a/src/streams/mbedtls.c b/src/streams/mbedtls.c index cbe2f681a..00daa5521 100644 --- a/src/streams/mbedtls.c +++ b/src/streams/mbedtls.c @@ -11,7 +11,7 @@ #include <ctype.h> -#include "global.h" +#include "runtime.h" #include "stream.h" #include "streams/socket.h" #include "netops.h" @@ -152,9 +152,7 @@ int git_mbedtls_stream_global_init(void) if (!loaded && crtpath != NULL && stat(crtpath, &statbuf) == 0 && S_ISDIR(statbuf.st_mode)) loaded = (git_mbedtls__set_cert_location(crtpath, 1) == 0); - git__on_shutdown(shutdown_ssl); - - return 0; + return git_runtime_shutdown_register(shutdown_ssl); cleanup: mbedtls_ctr_drbg_free(ctr_drbg); diff --git a/src/streams/openssl.c b/src/streams/openssl.c index 6a490d17d..0a0c2c5e5 100644 --- a/src/streams/openssl.c +++ b/src/streams/openssl.c @@ -11,7 +11,8 @@ #include <ctype.h> -#include "global.h" +#include "runtime.h" +#include "settings.h" #include "posix.h" #include "stream.h" #include "streams/socket.h" @@ -285,9 +286,7 @@ int git_openssl_stream_global_init(void) if (init_bio_method() < 0) goto error; - git__on_shutdown(shutdown_ssl); - - return 0; + return git_runtime_shutdown_register(shutdown_ssl); error: git_error_set(GIT_ERROR_NET, "could not initialize openssl: %s", @@ -324,8 +323,8 @@ int git_openssl_set_locking(void) } CRYPTO_set_locking_callback(openssl_locking_function); - git__on_shutdown(shutdown_ssl_locking); - return 0; + return git_runtime_shutdown_register(shutdown_ssl_locking); + #elif !defined(OPENSSL_LEGACY_API) return 0; #else diff --git a/src/streams/registry.c b/src/streams/registry.c index 284431207..b3bf17a4e 100644 --- a/src/streams/registry.c +++ b/src/streams/registry.c @@ -9,7 +9,7 @@ #include "streams/registry.h" -#include "global.h" +#include "runtime.h" #include "streams/tls.h" #include "streams/mbedtls.h" #include "streams/openssl.h" @@ -33,8 +33,7 @@ int git_stream_registry_global_init(void) if (git_rwlock_init(&stream_registry.lock) < 0) return -1; - git__on_shutdown(shutdown_stream_registry); - return 0; + return git_runtime_shutdown_register(shutdown_stream_registry); } GIT_INLINE(void) stream_registration_cpy( diff --git a/src/streams/tls.c b/src/streams/tls.c index 6a251717b..255a4a0f6 100644 --- a/src/streams/tls.c +++ b/src/streams/tls.c @@ -8,7 +8,6 @@ #include "git2/errors.h" #include "common.h" -#include "global.h" #include "streams/registry.h" #include "streams/tls.h" #include "streams/mbedtls.h" diff --git a/src/sysdir.c b/src/sysdir.c index 6dc78c8fd..401b4a55e 100644 --- a/src/sysdir.c +++ b/src/sysdir.c @@ -7,7 +7,7 @@ #include "sysdir.h" -#include "global.h" +#include "runtime.h" #include "buffer.h" #include "path.h" #include <ctype.h> @@ -189,9 +189,7 @@ int git_sysdir_global_init(void) for (i = 0; !error && i < ARRAY_SIZE(git_sysdir__dirs); i++) error = git_sysdir__dirs[i].guess(&git_sysdir__dirs[i].buf); - git__on_shutdown(git_sysdir_global_shutdown); - - return error; + return git_runtime_shutdown_register(git_sysdir_global_shutdown); } static int git_sysdir_check_selector(git_sysdir_t which) diff --git a/src/thread-utils.h b/src/thread-utils.h index 3311672b4..b4e44b468 100644 --- a/src/thread-utils.h +++ b/src/thread-utils.h @@ -235,6 +235,8 @@ GIT_INLINE(int64_t) git_atomic64_get(git_atomic64 *a) #else +GIT_INLINE(int) git_threads_global_init(void) { return 0; } + #define git_thread unsigned int #define git_thread_create(thread, start_routine, arg) 0 #define git_thread_join(id, status) (void)0 diff --git a/src/threadstate.c b/src/threadstate.c new file mode 100644 index 000000000..8e17124a2 --- /dev/null +++ b/src/threadstate.c @@ -0,0 +1,155 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ + +#include "threadstate.h" +#include "runtime.h" + +static void threadstate_dispose(git_threadstate *threadstate); + +/** + * Handle the thread-local state + * + * `git_threadstate_global_init` will be called as part + * of `git_libgit2_init` (which itself must be called + * before calling any other function in the library). + * + * This function allocates a TLS index (using pthreads + * or fiber-local storage in Win32) to store the per- + * thread state. + * + * Any internal method that requires thread-local state + * will then call `git_threadstate_get()` which returns a + * pointer to the thread-local state structure; this + * structure is lazily allocated on each thread. + * + * This mechanism will register a shutdown handler + * (`git_threadstate_global_shutdown`) which will free the + * TLS index. This shutdown handler will be called by + * `git_libgit2_shutdown`. + * + * If libgit2 is built without threading support, the + * `git_threadstate_get()` call returns a pointer to a single, + * statically allocated global state. The `git_thread_` + * functions are not available in that case. + */ + +#if defined(GIT_THREADS) && defined(GIT_WIN32) + +static DWORD fls_index; + +static void git_threadstate_global_shutdown(void) +{ + FlsFree(fls_index); +} + +static void WINAPI fls_free(void *threadstate) +{ + threadstate_dispose(threadstate); + git__free(threadstate); +} + +int git_threadstate_global_init(void) +{ + if ((fls_index = FlsAlloc(fls_free)) == FLS_OUT_OF_INDEXES) + return -1; + + return git_runtime_shutdown_register(git_threadstate_global_shutdown); +} + +git_threadstate *git_threadstate_get(void) +{ + git_threadstate *threadstate; + + if ((threadstate = FlsGetValue(fls_index)) != NULL) + return threadstate; + + if ((threadstate = git__calloc(1, sizeof(git_threadstate))) == NULL || + git_buf_init(&threadstate->error_buf, 0) < 0) + return NULL; + + FlsSetValue(fls_index, threadstate); + return threadstate; +} + +#elif defined(GIT_THREADS) && defined(_POSIX_THREADS) + +static pthread_key_t tls_key; + +static void git_threadstate_global_shutdown(void) +{ + git_threadstate *threadstate; + + threadstate = pthread_getspecific(tls_key); + pthread_setspecific(tls_key, NULL); + + threadstate_dispose(threadstate); + git__free(threadstate); + + pthread_key_delete(tls_key); +} + +static void tls_free(void *threadstate) +{ + threadstate_dispose(threadstate); + git__free(threadstate); +} + +int git_threadstate_global_init(void) +{ + if (pthread_key_create(&tls_key, &tls_free) != 0) + return -1; + + return git_runtime_shutdown_register(git_threadstate_global_shutdown); +} + +git_threadstate *git_threadstate_get(void) +{ + git_threadstate *threadstate; + + if ((threadstate = pthread_getspecific(tls_key)) != NULL) + return threadstate; + + if ((threadstate = git__calloc(1, sizeof(git_threadstate))) == NULL || + git_buf_init(&threadstate->error_buf, 0) < 0) + return NULL; + + pthread_setspecific(tls_key, threadstate); + return threadstate; +} + +#elif defined(GIT_THREADS) +# error unknown threading model +#else + +static git_threadstate threadstate; + +static void git_threadstate_global_shutdown(void) +{ + threadstate_dispose(&threadstate); + memset(&threadstate, 0, sizeof(git_threadstate)); +} + +int git_threadstate_global_init(void) +{ + return git_runtime_shutdown_register(git_tlsdata_global_shutdown); +} + +git_threadstate *git_threadstate_get(void) +{ + return &threadstate; +} + +#endif + +static void threadstate_dispose(git_threadstate *threadstate) +{ + if (!threadstate) + return; + + git__free(threadstate->error_t.message); + threadstate->error_t.message = NULL; +} diff --git a/src/threadstate.h b/src/threadstate.h new file mode 100644 index 000000000..51810a939 --- /dev/null +++ b/src/threadstate.h @@ -0,0 +1,24 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ +#ifndef INCLUDE_threadstate_h__ +#define INCLUDE_threadstate_h__ + +#include "common.h" + +typedef struct { + git_error *last_error; + git_error error_t; + git_buf error_buf; + char oid_fmt[GIT_OID_HEXSZ+1]; +} git_threadstate; + +extern int git_threadstate_global_init(void); +extern git_threadstate *git_threadstate_get(void); + +#define GIT_THREADSTATE (git_threadstate_get()) + +#endif diff --git a/src/trace.c b/src/trace.c index ec6a90aad..3acb64398 100644 --- a/src/trace.c +++ b/src/trace.c @@ -8,7 +8,7 @@ #include "trace.h" #include "buffer.h" -#include "global.h" +#include "runtime.h" #include "git2/trace.h" #ifdef GIT_TRACE diff --git a/src/transports/http.c b/src/transports/http.c index 66731b0ce..fb1740c33 100644 --- a/src/transports/http.c +++ b/src/transports/http.c @@ -14,7 +14,6 @@ #include "buffer.h" #include "net.h" #include "netops.h" -#include "global.h" #include "remote.h" #include "git2/sys/credential.h" #include "smart.h" diff --git a/src/transports/http.h b/src/transports/http.h index c02109cec..5c360b883 100644 --- a/src/transports/http.h +++ b/src/transports/http.h @@ -9,6 +9,7 @@ #define INCLUDE_transports_http_h__ #include "buffer.h" +#include "settings.h" #include "httpclient.h" #define GIT_HTTP_REPLAY_MAX 15 diff --git a/src/transports/httpclient.c b/src/transports/httpclient.c index ee936c81a..6ec22f837 100644 --- a/src/transports/httpclient.c +++ b/src/transports/httpclient.c @@ -10,7 +10,6 @@ #include "http_parser.h" #include "vector.h" #include "trace.h" -#include "global.h" #include "httpclient.h" #include "http.h" #include "auth.h" diff --git a/src/transports/ssh.c b/src/transports/ssh.c index 68b3cbeda..f4ed05bb1 100644 --- a/src/transports/ssh.c +++ b/src/transports/ssh.c @@ -11,7 +11,7 @@ #include <libssh2.h> #endif -#include "global.h" +#include "runtime.h" #include "git2.h" #include "buffer.h" #include "net.h" @@ -934,8 +934,7 @@ int git_transport_ssh_global_init(void) return -1; } - git__on_shutdown(shutdown_ssh); - return 0; + return git_runtime_shutdown_register(shutdown_ssh); #else diff --git a/src/transports/winhttp.c b/src/transports/winhttp.c index f9736cd07..2a1f6f26f 100644 --- a/src/transports/winhttp.c +++ b/src/transports/winhttp.c @@ -17,7 +17,6 @@ #include "smart.h" #include "remote.h" #include "repository.h" -#include "global.h" #include "http.h" #include "git2/sys/credential.h" diff --git a/src/unix/pthread.h b/src/unix/pthread.h index 233561b4e..55f4ae227 100644 --- a/src/unix/pthread.h +++ b/src/unix/pthread.h @@ -12,7 +12,8 @@ typedef struct { pthread_t thread; } git_thread; -#define git_threads_init() (void)0 +GIT_INLINE(int) git_threads_global_init(void) { return 0; } + #define git_thread_create(git_thread_ptr, start_routine, arg) \ pthread_create(&(git_thread_ptr)->thread, NULL, start_routine, arg) #define git_thread_join(git_thread_ptr, status) \ diff --git a/src/win32/posix_w32.c b/src/win32/posix_w32.c index cacf986e8..5a5e92158 100644 --- a/src/win32/posix_w32.c +++ b/src/win32/posix_w32.c @@ -14,7 +14,6 @@ #include "utf-conv.h" #include "repository.h" #include "reparse.h" -#include "global.h" #include "buffer.h" #include <errno.h> #include <io.h> diff --git a/src/win32/thread.c b/src/win32/thread.c index 42dba7f97..51b005bc8 100644 --- a/src/win32/thread.c +++ b/src/win32/thread.c @@ -6,8 +6,7 @@ */ #include "thread.h" - -#include "../global.h" +#include "runtime.h" #define CLEAN_THREAD_EXIT 0x6F012842 @@ -19,6 +18,8 @@ static win32_srwlock_fn win32_srwlock_release_shared; static win32_srwlock_fn win32_srwlock_acquire_exclusive; static win32_srwlock_fn win32_srwlock_release_exclusive; +static DWORD fls_index; + /* The thread procedure stub used to invoke the caller's procedure * and capture the return value for later collection. Windows will * only hold a DWORD, but we need to be able to store an entire @@ -28,14 +29,19 @@ static DWORD WINAPI git_win32__threadproc(LPVOID lpParameter) git_thread *thread = lpParameter; /* Set the current thread for `git_thread_exit` */ - GIT_GLOBAL->current_thread = thread; + FlsSetValue(fls_index, thread); thread->result = thread->proc(thread->param); return CLEAN_THREAD_EXIT; } -int git_threads_init(void) +static void git_threads_global_shutdown(void) +{ + FlsFree(fls_index); +} + +int git_threads_global_init(void) { HMODULE hModule = GetModuleHandleW(L"kernel32"); @@ -52,7 +58,10 @@ int git_threads_init(void) GetProcAddress(hModule, "ReleaseSRWLockExclusive"); } - return 0; + if ((fls_index = FlsAlloc(NULL)) == FLS_OUT_OF_INDEXES) + return -1; + + return git_runtime_shutdown_register(git_threads_global_shutdown); } int git_thread_create( @@ -99,8 +108,11 @@ int git_thread_join( void git_thread_exit(void *value) { - assert(GIT_GLOBAL->current_thread); - GIT_GLOBAL->current_thread->result = value; + git_thread *thread = FlsGetValue(fls_index); + + if (thread) + thread->result = value; + ExitThread(CLEAN_THREAD_EXIT); } diff --git a/src/win32/thread.h b/src/win32/thread.h index 41cbf015b..8305036b4 100644 --- a/src/win32/thread.h +++ b/src/win32/thread.h @@ -35,7 +35,7 @@ typedef struct { } native; } git_rwlock; -int git_threads_init(void); +int git_threads_global_init(void); int git_thread_create(git_thread *GIT_RESTRICT, void *(*) (void *), diff --git a/tests/core/useragent.c b/tests/core/useragent.c index c6c5220b2..2ce935bf5 100644 --- a/tests/core/useragent.c +++ b/tests/core/useragent.c @@ -1,5 +1,5 @@ #include "clar_libgit2.h" -#include "global.h" +#include "settings.h" void test_core_useragent__get(void) { diff --git a/tests/pack/filelimit.c b/tests/pack/filelimit.c index 044679f3b..b39ab7327 100644 --- a/tests/pack/filelimit.c +++ b/tests/pack/filelimit.c @@ -1,6 +1,5 @@ #include "clar_libgit2.h" #include "mwindow.h" -#include "global.h" #include <git2.h> #include "git2/sys/commit.h" diff --git a/tests/threads/basic.c b/tests/threads/basic.c index ed4fd2f9b..2d7ddc26b 100644 --- a/tests/threads/basic.c +++ b/tests/threads/basic.c @@ -54,6 +54,12 @@ static void *return_normally(void *param) { return param; } + +static void *exit_abruptly(void *param) +{ + git_thread_exit(param); + return NULL; +} #endif void test_threads_basic__exit(void) @@ -70,7 +76,7 @@ void test_threads_basic__exit(void) cl_assert_equal_sz(424242, (size_t)result); /* Ensure that the return value of `git_thread_exit` is returned. */ - cl_git_pass(git_thread_create(&thread, return_normally, (void *)232323)); + cl_git_pass(git_thread_create(&thread, exit_abruptly, (void *)232323)); cl_git_pass(git_thread_join(&thread, &result)); cl_assert_equal_sz(232323, (size_t)result); #endif |