Commit 7ebefd22e79e92542d68743af933c1a188f4d5a3

Edward Thomson 2012-11-13T10:10:40

move hash library func ptrs to global global

diff --git a/include/git2/threads.h b/include/git2/threads.h
index 567a104..f448f6a 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 22127fa..de7e42d 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 b9f8b67..1025cf7 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 e3f1f3f..0e543ed 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 c5891c1..92c9cb9 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 0d37f13..39c47ae 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 df78d91..df6570e 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 1fac452..c490942 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)