diff options
-rw-r--r-- | include/git2/threads.h | 4 | ||||
-rw-r--r-- | src/global.c | 45 | ||||
-rw-r--r-- | src/global.h | 11 | ||||
-rw-r--r-- | src/hash.h | 2 | ||||
-rw-r--r-- | src/hash/hash_generic.h | 1 | ||||
-rw-r--r-- | src/hash/hash_openssl.h | 1 | ||||
-rw-r--r-- | src/hash/hash_ppc.h | 1 | ||||
-rw-r--r-- | src/hash/hash_win32.c | 89 |
8 files changed, 98 insertions, 56 deletions
diff --git a/include/git2/threads.h b/include/git2/threads.h index 567a10487..f448f6a4d 100644 --- a/include/git2/threads.h +++ b/include/git2/threads.h @@ -27,8 +27,10 @@ GIT_BEGIN_DECL * * If libgit2 has been built without GIT_THREADS * support, this function is a no-op. + * + * @return 0 or an error code */ -GIT_EXTERN(void) git_threads_init(void); +GIT_EXTERN(int) git_threads_init(void); /** * Shutdown the threading system. diff --git a/src/global.c b/src/global.c index 22127faf7..de7e42d02 100644 --- a/src/global.c +++ b/src/global.c @@ -6,6 +6,7 @@ */ #include "common.h" #include "global.h" +#include "hash.h" #include "git2/threads.h" #include "thread-utils.h" @@ -38,19 +39,39 @@ git_mutex git__mwindow_mutex; * functions are not available in that case. */ +/* + * `git_threads_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_threads_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 _tls_index; static int _tls_init = 0; -void git_threads_init(void) +int git_threads_init(void) { + int error; + if (_tls_init) - return; + return 0; _tls_index = TlsAlloc(); - _tls_init = 1; git_mutex_init(&git__mwindow_mutex); + + /* Initialize any other subsystems that have global state */ + if ((error = git_hash_global_init()) >= 0) + _tls_init = 1; + + if (error == 0) + _tls_init = 1; + + GIT_MEMORY_BARRIER; + + return error; } void git_threads_shutdown(void) @@ -88,13 +109,22 @@ static void cb__free_status(void *st) git__free(st); } -void git_threads_init(void) +int git_threads_init(void) { + int error = 0; + if (_tls_init) - return; + return 0; pthread_key_create(&_tls_key, &cb__free_status); - _tls_init = 1; + + /* Initialize any other subsystems that have global state */ + if ((error = git_hash_global_init()) >= 0) + _tls_init = 1; + + GIT_MEMORY_BARRIER; + + return error; } void git_threads_shutdown(void) @@ -125,9 +155,10 @@ git_global_st *git__global_state(void) static git_global_st __state; -void git_threads_init(void) +int git_threads_init(void) { /* noop */ + return 0; } void git_threads_shutdown(void) diff --git a/src/global.h b/src/global.h index b9f8b6773..1025cf7bc 100644 --- a/src/global.h +++ b/src/global.h @@ -10,14 +10,15 @@ #include "mwindow.h" #include "hash.h" +#if defined(GIT_THREADS) && defined(_MSC_VER) +# define GIT_MEMORY_BARRIER MemoryBarrier() +#elif defined(GIT_THREADS) +# define GIT_MEMORY_BARRIER __sync_synchronize() +#endif + typedef struct { git_error *last_error; git_error error_t; - -#ifdef WIN32_SHA1 - git_hash_prov hash_prov; -#endif - } git_global_st; git_global_st *git__global_state(void); diff --git a/src/hash.h b/src/hash.h index e3f1f3f66..0e543edbe 100644 --- a/src/hash.h +++ b/src/hash.h @@ -12,6 +12,8 @@ typedef struct git_hash_prov git_hash_prov; typedef struct git_hash_ctx git_hash_ctx; +int git_hash_global_init(void); + int git_hash_ctx_init(git_hash_ctx *ctx); void git_hash_ctx_cleanup(git_hash_ctx *ctx); diff --git a/src/hash/hash_generic.h b/src/hash/hash_generic.h index c5891c164..92c9cb9ca 100644 --- a/src/hash/hash_generic.h +++ b/src/hash/hash_generic.h @@ -16,6 +16,7 @@ struct git_hash_ctx { unsigned int W[16]; }; +#define git_hash_global_init() 0 #define git_hash_ctx_init(ctx) git_hash_init(ctx) #define git_hash_ctx_cleanup(ctx) diff --git a/src/hash/hash_openssl.h b/src/hash/hash_openssl.h index 0d37f135b..39c47aece 100644 --- a/src/hash/hash_openssl.h +++ b/src/hash/hash_openssl.h @@ -16,6 +16,7 @@ struct git_hash_ctx { SHA_CTX c; }; +#define git_hash_global_init() 0 #define git_hash_ctx_init(ctx) git_hash_init(ctx) #define git_hash_ctx_cleanup(ctx) diff --git a/src/hash/hash_ppc.h b/src/hash/hash_ppc.h index df78d9135..df6570e22 100644 --- a/src/hash/hash_ppc.h +++ b/src/hash/hash_ppc.h @@ -20,6 +20,7 @@ struct git_hash_ctx { } buf; }; +#define git_hash_global_init() 0 #define git_hash_ctx_init(ctx) git_hash_init(ctx) #define git_hash_ctx_cleanup(ctx) diff --git a/src/hash/hash_win32.c b/src/hash/hash_win32.c index 1fac45273..c49094295 100644 --- a/src/hash/hash_win32.c +++ b/src/hash/hash_win32.c @@ -13,8 +13,12 @@ #include <wincrypt.h> #include <strsafe.h> +static struct git_hash_prov hash_prov = {0}; + +/* Hash initialization */ + /* Initialize CNG, if available */ -GIT_INLINE(int) hash_cng_prov_init(git_hash_prov *prov) +GIT_INLINE(int) hash_cng_prov_init(void) { OSVERSIONINFOEX version_test = {0}; DWORD version_test_mask; @@ -43,67 +47,67 @@ GIT_INLINE(int) hash_cng_prov_init(git_hash_prov *prov) if ((dll_path_len = GetSystemDirectory(dll_path, MAX_PATH)) == 0 || dll_path_len > MAX_PATH || StringCchCat(dll_path, MAX_PATH, "\\") < 0 || StringCchCat(dll_path, MAX_PATH, GIT_HASH_CNG_DLL_NAME) < 0 || - (prov->prov.cng.dll = LoadLibrary(dll_path)) == NULL) + (hash_prov.prov.cng.dll = LoadLibrary(dll_path)) == NULL) return -1; /* Load the function addresses */ - if ((prov->prov.cng.open_algorithm_provider = (hash_win32_cng_open_algorithm_provider_fn)GetProcAddress(prov->prov.cng.dll, "BCryptOpenAlgorithmProvider")) == NULL || - (prov->prov.cng.get_property = (hash_win32_cng_get_property_fn)GetProcAddress(prov->prov.cng.dll, "BCryptGetProperty")) == NULL || - (prov->prov.cng.create_hash = (hash_win32_cng_create_hash_fn)GetProcAddress(prov->prov.cng.dll, "BCryptCreateHash")) == NULL || - (prov->prov.cng.finish_hash = (hash_win32_cng_finish_hash_fn)GetProcAddress(prov->prov.cng.dll, "BCryptFinishHash")) == NULL || - (prov->prov.cng.hash_data = (hash_win32_cng_hash_data_fn)GetProcAddress(prov->prov.cng.dll, "BCryptHashData")) == NULL || - (prov->prov.cng.destroy_hash = (hash_win32_cng_destroy_hash_fn)GetProcAddress(prov->prov.cng.dll, "BCryptDestroyHash")) == NULL || - (prov->prov.cng.close_algorithm_provider = (hash_win32_cng_close_algorithm_provider_fn)GetProcAddress(prov->prov.cng.dll, "BCryptCloseAlgorithmProvider")) == NULL) { - FreeLibrary(prov->prov.cng.dll); + if ((hash_prov.prov.cng.open_algorithm_provider = (hash_win32_cng_open_algorithm_provider_fn)GetProcAddress(hash_prov.prov.cng.dll, "BCryptOpenAlgorithmProvider")) == NULL || + (hash_prov.prov.cng.get_property = (hash_win32_cng_get_property_fn)GetProcAddress(hash_prov.prov.cng.dll, "BCryptGetProperty")) == NULL || + (hash_prov.prov.cng.create_hash = (hash_win32_cng_create_hash_fn)GetProcAddress(hash_prov.prov.cng.dll, "BCryptCreateHash")) == NULL || + (hash_prov.prov.cng.finish_hash = (hash_win32_cng_finish_hash_fn)GetProcAddress(hash_prov.prov.cng.dll, "BCryptFinishHash")) == NULL || + (hash_prov.prov.cng.hash_data = (hash_win32_cng_hash_data_fn)GetProcAddress(hash_prov.prov.cng.dll, "BCryptHashData")) == NULL || + (hash_prov.prov.cng.destroy_hash = (hash_win32_cng_destroy_hash_fn)GetProcAddress(hash_prov.prov.cng.dll, "BCryptDestroyHash")) == NULL || + (hash_prov.prov.cng.close_algorithm_provider = (hash_win32_cng_close_algorithm_provider_fn)GetProcAddress(hash_prov.prov.cng.dll, "BCryptCloseAlgorithmProvider")) == NULL) { + FreeLibrary(hash_prov.prov.cng.dll); return -1; } /* Load the SHA1 algorithm */ - if (prov->prov.cng.open_algorithm_provider(&prov->prov.cng.handle, GIT_HASH_CNG_HASH_TYPE, NULL, GIT_HASH_CNG_HASH_REUSABLE) < 0) { - FreeLibrary(prov->prov.cng.dll); + if (hash_prov.prov.cng.open_algorithm_provider(&hash_prov.prov.cng.handle, GIT_HASH_CNG_HASH_TYPE, NULL, GIT_HASH_CNG_HASH_REUSABLE) < 0) { + FreeLibrary(hash_prov.prov.cng.dll); return -1; } /* Get storage space for the hash object */ - if (prov->prov.cng.get_property(prov->prov.cng.handle, GIT_HASH_CNG_HASH_OBJECT_LEN, (PBYTE)&prov->prov.cng.hash_object_size, sizeof(DWORD), &size_len, 0) < 0) { - prov->prov.cng.close_algorithm_provider(prov->prov.cng.handle, 0); - FreeLibrary(prov->prov.cng.dll); + if (hash_prov.prov.cng.get_property(hash_prov.prov.cng.handle, GIT_HASH_CNG_HASH_OBJECT_LEN, (PBYTE)&hash_prov.prov.cng.hash_object_size, sizeof(DWORD), &size_len, 0) < 0) { + hash_prov.prov.cng.close_algorithm_provider(hash_prov.prov.cng.handle, 0); + FreeLibrary(hash_prov.prov.cng.dll); return -1; } - prov->type = CNG; + hash_prov.type = CNG; return 0; } /* Initialize CryptoAPI */ -GIT_INLINE(int) hash_cryptoapi_prov_init(git_hash_prov *prov) +GIT_INLINE(int) hash_cryptoapi_prov_init() { - if (!CryptAcquireContext(&prov->prov.cryptoapi.handle, NULL, 0, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) + if (!CryptAcquireContext(&hash_prov.prov.cryptoapi.handle, NULL, 0, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) return -1; - prov->type = CRYPTOAPI; + hash_prov.type = CRYPTOAPI; return 0; } -static int hash_win32_prov_init(git_hash_prov *prov) +int git_hash_global_init() { int error = 0; - assert(prov->type == INVALID); + if (hash_prov.type != INVALID) + return 0; - /* Try to load CNG */ - if ((error = hash_cng_prov_init(prov)) < 0) - error = hash_cryptoapi_prov_init(prov); + if ((error = hash_cng_prov_init()) < 0) + error = hash_cryptoapi_prov_init(); - return error; + return error; } /* CryptoAPI: available in Windows XP and newer */ -GIT_INLINE(int) hash_ctx_cryptoapi_init(git_hash_ctx *ctx, git_hash_prov *prov) +GIT_INLINE(int) hash_ctx_cryptoapi_init(git_hash_ctx *ctx) { ctx->type = CRYPTOAPI; - ctx->prov = prov; + ctx->prov = &hash_prov; return git_hash_init(ctx); } @@ -156,18 +160,18 @@ GIT_INLINE(void) hash_ctx_cryptoapi_cleanup(git_hash_ctx *ctx) /* CNG: Available in Windows Server 2008 and newer */ -GIT_INLINE(int) hash_ctx_cng_init(git_hash_ctx *ctx, git_hash_prov *prov) +GIT_INLINE(int) hash_ctx_cng_init(git_hash_ctx *ctx) { - if ((ctx->ctx.cng.hash_object = git__malloc(prov->prov.cng.hash_object_size)) == NULL) + if ((ctx->ctx.cng.hash_object = git__malloc(hash_prov.prov.cng.hash_object_size)) == NULL) return -1; - if (prov->prov.cng.create_hash(prov->prov.cng.handle, &ctx->ctx.cng.hash_handle, ctx->ctx.cng.hash_object, prov->prov.cng.hash_object_size, NULL, 0, 0) < 0) { + if (hash_prov.prov.cng.create_hash(hash_prov.prov.cng.handle, &ctx->ctx.cng.hash_handle, ctx->ctx.cng.hash_object, hash_prov.prov.cng.hash_object_size, NULL, 0, 0) < 0) { git__free(ctx->ctx.cng.hash_object); return -1; } ctx->type = CNG; - ctx->prov = prov; + ctx->prov = &hash_prov; return 0; } @@ -216,22 +220,21 @@ GIT_INLINE(void) hash_ctx_cng_cleanup(git_hash_ctx *ctx) int git_hash_ctx_init(git_hash_ctx *ctx) { - git_global_st *global_state; - git_hash_prov *hash_prov; - - assert(ctx); - - memset(ctx, 0x0, sizeof(git_hash_ctx)); + int error = 0; - if ((global_state = git__global_state()) == NULL) - return -1; + assert(ctx); - hash_prov = &global_state->hash_prov; + /* + * When compiled with GIT_THREADS, the global hash_prov data is + * initialized with git_threads_init. Otherwise, it must be initialized + * at first use. + */ + if (hash_prov.type == INVALID && (error = git_hash_global_init()) < 0) + return error; - if (hash_prov->type == INVALID && hash_win32_prov_init(hash_prov) < 0) - return -1; + memset(ctx, 0x0, sizeof(git_hash_ctx)); - return (hash_prov->type == CNG) ? hash_ctx_cng_init(ctx, hash_prov) : hash_ctx_cryptoapi_init(ctx, hash_prov); + return (hash_prov.type == CNG) ? hash_ctx_cng_init(ctx) : hash_ctx_cryptoapi_init(ctx); } int git_hash_init(git_hash_ctx *ctx) |