Commit a3aa5f4d5dcbe038f1d1c5ff40eed29d27953fbe

Russell Belfer 2013-09-11T12:45:20

Add simple global shutdown hooks Increasingly there are a number of components that want to do some cleanup at global shutdown time (at least if there are not going to be memory leaks). This creates a very simple system of shutdown hooks that will be invoked by git_threads_shutdown. Right now, the maximum number of hooks is hardcoded, but since adding a hook is not a public API, it should be fine and I thought it was better to start off with really simple code.

diff --git a/src/fileops.c b/src/fileops.c
index 126d45f..3b271e6 100644
--- a/src/fileops.c
+++ b/src/fileops.c
@@ -6,6 +6,7 @@
  */
 #include "common.h"
 #include "fileops.h"
+#include "global.h"
 #include <ctype.h>
 #if GIT_WIN32
 #include "win32/findfile.h"
@@ -635,6 +636,13 @@ static git_futils_dirs_guess_cb git_futils__dir_guess[GIT_FUTILS_DIR__MAX] = {
 	git_futils_guess_xdg_dirs,
 };
 
+static void git_futils_dirs_global_shutdown(void)
+{
+	int i;
+	for (i = 0; i < GIT_FUTILS_DIR__MAX; ++i)
+		git_buf_free(&git_futils__dirs[i]);
+}
+
 int git_futils_dirs_global_init(void)
 {
 	git_futils_dir_t i;
@@ -644,6 +652,8 @@ int git_futils_dirs_global_init(void)
 	for (i = 0; !error && i < GIT_FUTILS_DIR__MAX; i++)
 		error = git_futils_dirs_get(&path, i);
 
+	git__on_shutdown(git_futils_dirs_global_shutdown);
+
 	return error;
 }
 
@@ -726,13 +736,6 @@ int git_futils_dirs_set(git_futils_dir_t which, const char *search_path)
 	return git_buf_oom(&git_futils__dirs[which]) ? -1 : 0;
 }
 
-void git_futils_dirs_free(void)
-{
-	int i;
-	for (i = 0; i < GIT_FUTILS_DIR__MAX; ++i)
-		git_buf_free(&git_futils__dirs[i]);
-}
-
 static int git_futils_find_in_dirlist(
 	git_buf *path, const char *name, git_futils_dir_t which, const char *label)
 {
diff --git a/src/fileops.h b/src/fileops.h
index 02f79b9..16bc58e 100644
--- a/src/fileops.h
+++ b/src/fileops.h
@@ -354,11 +354,6 @@ extern int git_futils_dirs_get_str(
 extern int git_futils_dirs_set(git_futils_dir_t which, const char *paths);
 
 /**
- * Release / reset all search paths
- */
-extern void git_futils_dirs_free(void);
-
-/**
  * Create a "fake" symlink (text file containing the target path).
  *
  * @param new symlink file to be created
diff --git a/src/global.c b/src/global.c
index b504e5e..4f024f1 100644
--- a/src/global.c
+++ b/src/global.c
@@ -14,6 +14,28 @@
 
 git_mutex git__mwindow_mutex;
 
+#define MAX_SHUTDOWN_CB 8
+
+git_global_shutdown_fn git__shutdown_callbacks[MAX_SHUTDOWN_CB];
+git_atomic git__n_shutdown_callbacks;
+
+void git__on_shutdown(git_global_shutdown_fn callback)
+{
+	int count = git_atomic_inc(&git__n_shutdown_callbacks);
+	assert(count <= MAX_SHUTDOWN_CB);
+	git__shutdown_callbacks[count - 1] = callback;
+}
+
+static void git__shutdown(void)
+{
+	int pos;
+
+	while ((pos = git_atomic_dec(&git__n_shutdown_callbacks)) >= 0) {
+		if (git__shutdown_callbacks[pos])
+			git__shutdown_callbacks[pos]();
+	}
+}
+
 /**
  * Handle the global state with TLS
  *
@@ -79,9 +101,7 @@ int git_threads_init(void)
 void git_threads_shutdown(void)
 {
 	/* Shut down any subsystems that have global state */
-	win32_pthread_shutdown();
-	git_futils_dirs_free();
-	git_hash_global_shutdown();
+	git__shutdown();
 
 	TlsFree(_tls_index);
 	_tls_init = 0;
@@ -140,6 +160,9 @@ int git_threads_init(void)
 
 void git_threads_shutdown(void)
 {
+	/* Shut down any subsystems that have global state */
+	git__shutdown();
+
 	if (_tls_init) {
 		void *ptr = pthread_getspecific(_tls_key);
 		pthread_setspecific(_tls_key, NULL);
@@ -149,10 +172,6 @@ void git_threads_shutdown(void)
 	pthread_key_delete(_tls_key);
 	_tls_init = 0;
 	git_mutex_free(&git__mwindow_mutex);
-
-	/* Shut down any subsystems that have global state */
-	git_hash_global_shutdown();
-	git_futils_dirs_free();
 }
 
 git_global_st *git__global_state(void)
@@ -179,15 +198,14 @@ static git_global_st __state;
 
 int git_threads_init(void)
 {
-	/* noop */ 
+	/* noop */
 	return 0;
 }
 
 void git_threads_shutdown(void)
 {
 	/* Shut down any subsystems that have global state */
-	git_hash_global_shutdown();
-	git_futils_dirs_free();
+	git__shutdown();
 }
 
 git_global_st *git__global_state(void)
diff --git a/src/global.h b/src/global.h
index badbc08..7782503 100644
--- a/src/global.h
+++ b/src/global.h
@@ -21,4 +21,8 @@ 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);
+
 #endif
diff --git a/src/hash.h b/src/hash.h
index 5b84898..c47f335 100644
--- a/src/hash.h
+++ b/src/hash.h
@@ -13,8 +13,6 @@ typedef struct git_hash_prov git_hash_prov;
 typedef struct git_hash_ctx git_hash_ctx;
 
 int git_hash_global_init(void);
-void git_hash_global_shutdown(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 6b60c98..daeb1cd 100644
--- a/src/hash/hash_generic.h
+++ b/src/hash/hash_generic.h
@@ -17,7 +17,6 @@ struct git_hash_ctx {
 };
 
 #define git_hash_global_init() 0
-#define git_hash_global_shutdown() /* noop */
 #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 f83279a..9a55d47 100644
--- a/src/hash/hash_openssl.h
+++ b/src/hash/hash_openssl.h
@@ -17,7 +17,6 @@ struct git_hash_ctx {
 };
 
 #define git_hash_global_init() 0
-#define git_hash_global_shutdown() /* noop */
 #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 095ceb3..bb22313 100644
--- a/src/hash/hash_win32.c
+++ b/src/hash/hash_win32.c
@@ -89,7 +89,15 @@ GIT_INLINE(void) hash_cryptoapi_prov_shutdown(void)
 	hash_prov.type = INVALID;
 }
 
-int git_hash_global_init()
+static void git_hash_global_shutdown(void)
+{
+	if (hash_prov.type == CNG)
+		hash_cng_prov_shutdown();
+	else if(hash_prov.type == CRYPTOAPI)
+		hash_cryptoapi_prov_shutdown();
+}
+
+int git_hash_global_init(void)
 {
 	int error = 0;
 
@@ -99,15 +107,9 @@ int git_hash_global_init()
 	if ((error = hash_cng_prov_init()) < 0)
 		error = hash_cryptoapi_prov_init();
 
-	return error;	
-}
+	git__on_shutdown(git_hash_global_shutdown);
 
-void git_hash_global_shutdown()
-{
-	if (hash_prov.type == CNG)
-		hash_cng_prov_shutdown();
-	else if(hash_prov.type == CRYPTOAPI)
-		hash_cryptoapi_prov_shutdown();
+	return error;
 }
 
 /* CryptoAPI: available in Windows XP and newer */
diff --git a/src/win32/pthread.c b/src/win32/pthread.c
index d50ace6..8c7ef28 100644
--- a/src/win32/pthread.c
+++ b/src/win32/pthread.c
@@ -217,6 +217,14 @@ int pthread_rwlock_destroy(pthread_rwlock_t *lock)
 }
 
 
+static void win32_pthread_shutdown(void)
+{
+	if (win32_kernel32_dll) {
+		FreeLibrary(win32_kernel32_dll);
+		win32_kernel32_dll = NULL;
+	}
+}
+
 int win32_pthread_initialize(void)
 {
 	if (win32_kernel32_dll)
@@ -239,15 +247,7 @@ int win32_pthread_initialize(void)
 	win32_srwlock_release_exclusive = (win32_srwlock_fn)
 		GetProcAddress(win32_kernel32_dll, "ReleaseSRWLockExclusive");
 
-	return 0;
-}
-
-int win32_pthread_shutdown(void)
-{
-	if (win32_kernel32_dll) {
-		FreeLibrary(win32_kernel32_dll);
-		win32_kernel32_dll = NULL;
-	}
+	git__on_shutdown(win32_pthread_shutdown);
 
 	return 0;
 }
diff --git a/src/win32/pthread.h b/src/win32/pthread.h
index 2ba2ca5..af5b121 100644
--- a/src/win32/pthread.h
+++ b/src/win32/pthread.h
@@ -69,6 +69,5 @@ int pthread_rwlock_wrunlock(pthread_rwlock_t *);
 int pthread_rwlock_destroy(pthread_rwlock_t *);
 
 extern int win32_pthread_initialize(void);
-extern int win32_pthread_shutdown(void);
 
 #endif