Commit 430953417f74dfcdbe030bafc069e1c07edceeb6

Russell Belfer 2013-08-26T14:56:31

Load SRWLock APIs at runtime This loads SRWLock APIs at runtime and in their absence (i.e. on Windows before Vista) falls back on a regular CRITICAL_SECTION that will not permit concurrent readers.

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 019777e..1c70ec2 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -287,13 +287,8 @@ FILE(GLOB SRC_H include/git2.h include/git2/*.h include/git2/sys/*.h)
 
 # On Windows use specific platform sources
 IF (WIN32 AND NOT CYGWIN)
-	ADD_DEFINITIONS(-DWIN32)
+	ADD_DEFINITIONS(-DWIN32 -D_WIN32_WINNT=0x0501)
 	FILE(GLOB SRC_OS src/win32/*.c src/win32/*.h)
-	IF (THREADSAFE)
-		ADD_DEFINITIONS(-D_WIN32_WINNT=0x0600)
-	ELSE()
-		ADD_DEFINITIONS(-D_WIN32_WINNT=0x0501)
-	ENDIF()
 ELSEIF (AMIGA)
 	ADD_DEFINITIONS(-DNO_ADDRINFO -DNO_READDIR_R)
 	FILE(GLOB SRC_OS src/amiga/*.c src/amiga/*.h)
diff --git a/src/global.c b/src/global.c
index a06d0c8..b504e5e 100644
--- a/src/global.c
+++ b/src/global.c
@@ -71,18 +71,22 @@ int git_threads_init(void)
 
 	GIT_MEMORY_BARRIER;
 
+	win32_pthread_initialize();
+
 	return error;
 }
 
 void git_threads_shutdown(void)
 {
+	/* Shut down any subsystems that have global state */
+	win32_pthread_shutdown();
+	git_futils_dirs_free();
+	git_hash_global_shutdown();
+
 	TlsFree(_tls_index);
 	_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_mutex_free(&git__mwindow_mutex);
 }
 
 git_global_st *git__global_state(void)
diff --git a/src/hash/hash_win32.c b/src/hash/hash_win32.c
index 43d54ca..6732f93 100644
--- a/src/hash/hash_win32.c
+++ b/src/hash/hash_win32.c
@@ -46,7 +46,8 @@ GIT_INLINE(int) hash_cng_prov_init(void)
 		return -1;
 
 	/* Load bcrypt.dll explicitly from the system directory */
-	if ((dll_path_len = GetSystemDirectory(dll_path, MAX_PATH)) == 0 || dll_path_len > MAX_PATH ||
+	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 ||
 		(hash_prov.prov.cng.dll = LoadLibrary(dll_path)) == NULL)
diff --git a/src/thread-utils.h b/src/thread-utils.h
index 371dc0b..914c135 100644
--- a/src/thread-utils.h
+++ b/src/thread-utils.h
@@ -40,10 +40,6 @@ typedef git_atomic git_atomic_ssize;
 
 #ifdef GIT_THREADS
 
-#if defined(GIT_WIN32) && _WIN32_WINNT < 0x0600
-#	error "Unsupported Windows version for thread support"
-#endif
-
 #define git_thread pthread_t
 #define git_thread_create(thread, attr, start_routine, arg) \
 	pthread_create(thread, attr, start_routine, arg)
diff --git a/src/win32/pthread.c b/src/win32/pthread.c
index 41cb7a4..8775f63 100644
--- a/src/win32/pthread.c
+++ b/src/win32/pthread.c
@@ -127,9 +127,10 @@ int pthread_cond_signal(pthread_cond_t *cond)
 	return 0;
 }
 
-/* pthread_cond_broadcast is not implemented because doing so with just Win32 events
- * is quite complicated, and no caller in libgit2 uses it yet. */
-
+/* pthread_cond_broadcast is not implemented because doing so with just
+ * Win32 events is quite complicated, and no caller in libgit2 uses it
+ * yet.
+ */
 int pthread_num_processors_np(void)
 {
 	DWORD_PTR p, s;
@@ -142,41 +143,111 @@ int pthread_num_processors_np(void)
 	return n ? n : 1;
 }
 
+
+static HINSTANCE win32_kernel32_dll;
+
+typedef void (WINAPI *win32_srwlock_fn)(SRWLOCK *);
+
+static win32_srwlock_fn win32_srwlock_initialize;
+static win32_srwlock_fn win32_srwlock_acquire_shared;
+static win32_srwlock_fn win32_srwlock_release_shared;
+static win32_srwlock_fn win32_srwlock_acquire_exclusive;
+static win32_srwlock_fn win32_srwlock_release_exclusive;
+
 int pthread_rwlock_init(
 	pthread_rwlock_t *GIT_RESTRICT lock,
 	const pthread_rwlockattr_t *GIT_RESTRICT attr)
 {
 	(void)attr;
-	InitializeSRWLock(lock);
+
+	if (win32_srwlock_initialize)
+		win32_srwlock_initialize(&lock->native.srwl);
+	else
+		InitializeCriticalSection(&lock->native.csec);
+
 	return 0;
 }
 
 int pthread_rwlock_rdlock(pthread_rwlock_t *lock)
 {
-	AcquireSRWLockShared(lock);
+	if (win32_srwlock_acquire_shared)
+		win32_srwlock_acquire_shared(&lock->native.srwl);
+	else
+		EnterCriticalSection(&lock->native.csec);
+
 	return 0;
 }
 
 int pthread_rwlock_rdunlock(pthread_rwlock_t *lock)
 {
-	ReleaseSRWLockShared(lock);
+	if (win32_srwlock_release_shared)
+		win32_srwlock_release_shared(&lock->native.srwl);
+	else
+		LeaveCriticalSection(&lock->native.csec);
+
 	return 0;
 }
 
 int pthread_rwlock_wrlock(pthread_rwlock_t *lock)
 {
-	AcquireSRWLockExclusive(lock);
+	if (win32_srwlock_acquire_exclusive)
+		win32_srwlock_acquire_exclusive(&lock->native.srwl);
+	else
+		EnterCriticalSection(&lock->native.csec);
+
 	return 0;
 }
 
 int pthread_rwlock_wrunlock(pthread_rwlock_t *lock)
 {
-	ReleaseSRWLockExclusive(lock);
+	if (win32_srwlock_release_exclusive)
+		win32_srwlock_release_exclusive(&lock->native.srwl);
+	else
+		LeaveCriticalSection(&lock->native.csec);
+
 	return 0;
 }
 
 int pthread_rwlock_destroy(pthread_rwlock_t *lock)
 {
-	(void)lock;
+	if (!win32_srwlock_initialize)
+		DeleteCriticalSection(&lock->native.csec);
+	git__memzero(lock, sizeof(*lock));
+	return 0;
+}
+
+
+int win32_pthread_initialize(void)
+{
+	if (win32_kernel32_dll)
+		return 0;
+
+	win32_kernel32_dll = LoadLibrary("Kernel32.dll");
+	if (!win32_kernel32_dll) {
+		giterr_set(GITERR_OS, "Could not load Kernel32.dll!");
+		return -1;
+	}
+
+	win32_srwlock_initialize = (win32_srwlock_fn)
+		GetProcAddress(win32_kernel32_dll, "InitializeSRWLock");
+	win32_srwlock_acquire_shared = (win32_srwlock_fn)
+		GetProcAddress(win32_kernel32_dll, "AcquireSRWLockShared");
+	win32_srwlock_release_shared = (win32_srwlock_fn)
+		GetProcAddress(win32_kernel32_dll, "ReleaseSRWLockShared");
+	win32_srwlock_acquire_exclusive = (win32_srwlock_fn)
+		GetProcAddress(win32_kernel32_dll, "AcquireSRWLockExclusive");
+	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;
+	}
+
 	return 0;
 }
diff --git a/src/win32/pthread.h b/src/win32/pthread.h
index 50d8362..e84de47 100644
--- a/src/win32/pthread.h
+++ b/src/win32/pthread.h
@@ -24,10 +24,17 @@ typedef int pthread_rwlockattr_t;
 typedef CRITICAL_SECTION pthread_mutex_t;
 typedef HANDLE pthread_t;
 typedef HANDLE pthread_cond_t;
-typedef SRWLOCK pthread_rwlock_t;
+
+/* typedef struct { void *Ptr; } SRWLOCK; */
+
+typedef struct {
+	union {
+		SRWLOCK srwl;
+		CRITICAL_SECTION csec;
+	} native;
+} pthread_rwlock_t;
 
 #define PTHREAD_MUTEX_INITIALIZER  {(void*)-1}
-#define PTHREAD_RWLOCK_INITIALIZER SRWLOCK_INIT
 
 int pthread_create(
 	pthread_t *GIT_RESTRICT thread,
@@ -61,4 +68,7 @@ int pthread_rwlock_wrlock(pthread_rwlock_t *);
 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