MSVC: Windows-native implementation for subset of Pthreads API
authorAndrzej K. Haczewski <ahaczewski@gmail.com>
Fri, 15 Jan 2010 20:12:20 +0000 (21:12 +0100)
committerJunio C Hamano <gitster@pobox.com>
Sun, 17 Jan 2010 02:16:06 +0000 (18:16 -0800)
This patch implements native to Windows subset of pthreads API used by Git.
It allows to remove Pthreads for Win32 dependency for MSVC, msysgit and
Cygwin.

[J6t: If the MinGW build was built as part of the msysgit build
environment, then threading was already enabled because the
pthreads-win32 package is available in msysgit. With this patch, we can now
enable threaded code unconditionally.]

Signed-off-by: Andrzej K. Haczewski <ahaczewski@gmail.com>
Signed-off-by: Johannes Sixt <j6t@kdbg.org>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Makefile
builtin-pack-objects.c
compat/mingw.c
compat/mingw.h
compat/win32/pthread.c [new file with mode: 0644]
compat/win32/pthread.h [new file with mode: 0644]

index 69335556602e7093a73cc77529f8219e4eb0c01c..7f5814c7a3276886e6392e19c48ddb63a092d52b 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -478,6 +478,7 @@ LIB_H += commit.h
 LIB_H += compat/bswap.h
 LIB_H += compat/cygwin.h
 LIB_H += compat/mingw.h
+LIB_H += compat/win32/pthread.h
 LIB_H += csum-file.h
 LIB_H += decorate.h
 LIB_H += delta.h
@@ -996,15 +997,15 @@ ifeq ($(uname_S),Windows)
        NO_REGEX = YesPlease
        NO_CURL = YesPlease
        NO_PYTHON = YesPlease
-       NO_PTHREADS = YesPlease
        BLK_SHA1 = YesPlease
+       THREADED_DELTA_SEARCH = YesPlease
 
        CC = compat/vcbuild/scripts/clink.pl
        AR = compat/vcbuild/scripts/lib.pl
        CFLAGS =
        BASIC_CFLAGS = -nologo -I. -I../zlib -Icompat/vcbuild -Icompat/vcbuild/include -DWIN32 -D_CONSOLE -DHAVE_STRING_H -D_CRT_SECURE_NO_WARNINGS -D_CRT_NONSTDC_NO_DEPRECATE
-       COMPAT_OBJS = compat/msvc.o compat/fnmatch/fnmatch.o compat/winansi.o
-       COMPAT_CFLAGS = -D__USE_MINGW_ACCESS -DNOGDI -DHAVE_STRING_H -DHAVE_ALLOCA_H -Icompat -Icompat/fnmatch -Icompat/regex -Icompat/fnmatch -DSTRIP_EXTENSION=\".exe\"
+       COMPAT_OBJS = compat/msvc.o compat/fnmatch/fnmatch.o compat/winansi.o compat/win32/pthread.o
+       COMPAT_CFLAGS = -D__USE_MINGW_ACCESS -DNOGDI -DHAVE_STRING_H -DHAVE_ALLOCA_H -Icompat -Icompat/fnmatch -Icompat/regex -Icompat/fnmatch -Icompat/win32 -DSTRIP_EXTENSION=\".exe\"
        BASIC_LDFLAGS = -IGNORE:4217 -IGNORE:4049 -NOLOGO -SUBSYSTEM:CONSOLE -NODEFAULTLIB:MSVCRT.lib
        EXTLIBS = advapi32.lib shell32.lib wininet.lib ws2_32.lib
        lib =
@@ -1048,9 +1049,11 @@ ifneq (,$(findstring MINGW,$(uname_S)))
        NO_REGEX = YesPlease
        NO_PYTHON = YesPlease
        BLK_SHA1 = YesPlease
+       THREADED_DELTA_SEARCH = YesPlease
        COMPAT_CFLAGS += -D__USE_MINGW_ACCESS -DNOGDI -Icompat -Icompat/fnmatch
        COMPAT_CFLAGS += -DSTRIP_EXTENSION=\".exe\"
-       COMPAT_OBJS += compat/mingw.o compat/fnmatch/fnmatch.o compat/winansi.o
+       COMPAT_OBJS += compat/mingw.o compat/fnmatch/fnmatch.o compat/winansi.o \
+               compat/win32/pthread.o
        EXTLIBS += -lws2_32
        X = .exe
 ifneq (,$(wildcard ../THIS_IS_MSYSGIT))
@@ -1060,10 +1063,8 @@ ifneq (,$(wildcard ../THIS_IS_MSYSGIT))
        EXTLIBS += /mingw/lib/libz.a
        NO_R_TO_GCC_LINKER = YesPlease
        INTERNAL_QSORT = YesPlease
-       THREADED_DELTA_SEARCH = YesPlease
 else
        NO_CURL = YesPlease
-       NO_PTHREADS = YesPlease
 endif
 endif
 
index 4429d53a1e82b81f0a82e34bf2d6ae463aeeadee..890f45cf2070a476c0325033e898e9345091fef1 100644 (file)
@@ -1256,15 +1256,15 @@ static int delta_cacheable(unsigned long src_size, unsigned long trg_size,
 
 #ifdef THREADED_DELTA_SEARCH
 
-static pthread_mutex_t read_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t read_mutex;
 #define read_lock()            pthread_mutex_lock(&read_mutex)
 #define read_unlock()          pthread_mutex_unlock(&read_mutex)
 
-static pthread_mutex_t cache_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t cache_mutex;
 #define cache_lock()           pthread_mutex_lock(&cache_mutex)
 #define cache_unlock()         pthread_mutex_unlock(&cache_mutex)
 
-static pthread_mutex_t progress_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t progress_mutex;
 #define progress_lock()                pthread_mutex_lock(&progress_mutex)
 #define progress_unlock()      pthread_mutex_unlock(&progress_mutex)
 
@@ -1591,7 +1591,26 @@ struct thread_params {
        unsigned *processed;
 };
 
-static pthread_cond_t progress_cond = PTHREAD_COND_INITIALIZER;
+static pthread_cond_t progress_cond;
+
+/*
+ * Mutex and conditional variable can't be statically-initialized on Windows.
+ */
+static void init_threaded_search(void)
+{
+       pthread_mutex_init(&read_mutex, NULL);
+       pthread_mutex_init(&cache_mutex, NULL);
+       pthread_mutex_init(&progress_mutex, NULL);
+       pthread_cond_init(&progress_cond, NULL);
+}
+
+static void cleanup_threaded_search(void)
+{
+       pthread_cond_destroy(&progress_cond);
+       pthread_mutex_destroy(&read_mutex);
+       pthread_mutex_destroy(&cache_mutex);
+       pthread_mutex_destroy(&progress_mutex);
+}
 
 static void *threaded_find_deltas(void *arg)
 {
@@ -1630,10 +1649,13 @@ static void ll_find_deltas(struct object_entry **list, unsigned list_size,
        struct thread_params *p;
        int i, ret, active_threads = 0;
 
+       init_threaded_search();
+
        if (!delta_search_threads)      /* --threads=0 means autodetect */
                delta_search_threads = online_cpus();
        if (delta_search_threads <= 1) {
                find_deltas(list, &list_size, window, depth, processed);
+               cleanup_threaded_search();
                return;
        }
        if (progress > pack_to_stdout)
@@ -1748,6 +1770,7 @@ static void ll_find_deltas(struct object_entry **list, unsigned list_size,
                        active_threads--;
                }
        }
+       cleanup_threaded_search();
        free(p);
 }
 
index 73762473c55ea18e60aa74eab2fab5936a966df0..74ffc1834f5ad07d711da4ee6c2edabd56f7dad4 100644 (file)
@@ -3,7 +3,7 @@
 #include <conio.h>
 #include "../strbuf.h"
 
-static int err_win_to_posix(DWORD winerr)
+int err_win_to_posix(DWORD winerr)
 {
        int error = ENOSYS;
        switch(winerr) {
index afe12ea55469f7a40e35edf8bcf8493e91e0980c..e254fb4e068c3248a1aac33d70e40b620cd91088 100644 (file)
@@ -310,3 +310,8 @@ struct mingw_dirent
 #define readdir(x) mingw_readdir(x)
 struct dirent *mingw_readdir(DIR *dir);
 #endif // !NO_MINGW_REPLACE_READDIR
+
+/*
+ * Used by Pthread API implementation for Windows
+ */
+extern int err_win_to_posix(DWORD winerr);
diff --git a/compat/win32/pthread.c b/compat/win32/pthread.c
new file mode 100644 (file)
index 0000000..631c0a4
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2009 Andrzej K. Haczewski <ahaczewski@gmail.com>
+ *
+ * DISCLAMER: The implementation is Git-specific, it is subset of original
+ * Pthreads API, without lots of other features that Git doesn't use.
+ * Git also makes sure that the passed arguments are valid, so there's
+ * no need for double-checking.
+ */
+
+#include "../../git-compat-util.h"
+#include "pthread.h"
+
+#include <errno.h>
+#include <limits.h>
+
+static unsigned __stdcall win32_start_routine(void *arg)
+{
+       pthread_t *thread = arg;
+       thread->arg = thread->start_routine(thread->arg);
+       return 0;
+}
+
+int pthread_create(pthread_t *thread, const void *unused,
+                  void *(*start_routine)(void*), void *arg)
+{
+       thread->arg = arg;
+       thread->start_routine = start_routine;
+       thread->handle = (HANDLE)
+               _beginthreadex(NULL, 0, win32_start_routine, thread, 0, NULL);
+
+       if (!thread->handle)
+               return errno;
+       else
+               return 0;
+}
+
+int win32_pthread_join(pthread_t *thread, void **value_ptr)
+{
+       DWORD result = WaitForSingleObject(thread->handle, INFINITE);
+       switch (result) {
+               case WAIT_OBJECT_0:
+                       if (value_ptr)
+                               *value_ptr = thread->arg;
+                       return 0;
+               case WAIT_ABANDONED:
+                       return EINVAL;
+               default:
+                       return err_win_to_posix(GetLastError());
+       }
+}
+
+int pthread_cond_init(pthread_cond_t *cond, const void *unused)
+{
+       cond->waiters = 0;
+
+       cond->sema = CreateSemaphore(NULL, 0, LONG_MAX, NULL);
+       if (!cond->sema)
+               die("CreateSemaphore() failed");
+       return 0;
+}
+
+int pthread_cond_destroy(pthread_cond_t *cond)
+{
+       CloseHandle(cond->sema);
+       cond->sema = NULL;
+
+       return 0;
+}
+
+int pthread_cond_wait(pthread_cond_t *cond, CRITICAL_SECTION *mutex)
+{
+       InterlockedIncrement(&cond->waiters);
+
+       /*
+        * Unlock external mutex and wait for signal.
+        * NOTE: we've held mutex locked long enough to increment
+        * waiters count above, so there's no problem with
+        * leaving mutex unlocked before we wait on semaphore.
+        */
+       LeaveCriticalSection(mutex);
+
+       /* let's wait - ignore return value */
+       WaitForSingleObject(cond->sema, INFINITE);
+
+       /* we're done waiting, so make sure we decrease waiters count */
+       InterlockedDecrement(&cond->waiters);
+
+       /* lock external mutex again */
+       EnterCriticalSection(mutex);
+
+       return 0;
+}
+
+int pthread_cond_signal(pthread_cond_t *cond)
+{
+       /*
+        * Access to waiters count is atomic; see "Interlocked Variable Access"
+        * http://msdn.microsoft.com/en-us/library/ms684122(VS.85).aspx
+        */
+       int have_waiters = cond->waiters > 0;
+
+       /*
+        * Signal only when there are waiters
+        */
+       if (have_waiters)
+               return ReleaseSemaphore(cond->sema, 1, NULL) ?
+                       0 : err_win_to_posix(GetLastError());
+       else
+               return 0;
+}
diff --git a/compat/win32/pthread.h b/compat/win32/pthread.h
new file mode 100644 (file)
index 0000000..b8e1bcb
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * Header used to adapt pthread-based POSIX code to Windows API threads.
+ *
+ * Copyright (C) 2009 Andrzej K. Haczewski <ahaczewski@gmail.com>
+ */
+
+#ifndef PTHREAD_H
+#define PTHREAD_H
+
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+
+#include <windows.h>
+
+/*
+ * Defines that adapt Windows API threads to pthreads API
+ */
+#define pthread_mutex_t CRITICAL_SECTION
+
+#define pthread_mutex_init(a,b) InitializeCriticalSection((a))
+#define pthread_mutex_destroy(a) DeleteCriticalSection((a))
+#define pthread_mutex_lock EnterCriticalSection
+#define pthread_mutex_unlock LeaveCriticalSection
+
+/*
+ * Implement simple condition variable for Windows threads, based on ACE
+ * implementation.
+ *
+ * See original implementation: http://bit.ly/1vkDjo
+ * ACE homepage: http://www.cse.wustl.edu/~schmidt/ACE.html
+ * See also: http://www.cse.wustl.edu/~schmidt/win32-cv-1.html
+ */
+typedef struct {
+       volatile LONG waiters;
+       HANDLE sema;
+} pthread_cond_t;
+
+extern int pthread_cond_init(pthread_cond_t *cond, const void *unused);
+
+extern int pthread_cond_destroy(pthread_cond_t *cond);
+
+extern int pthread_cond_wait(pthread_cond_t *cond, CRITICAL_SECTION *mutex);
+
+extern int pthread_cond_signal(pthread_cond_t *cond);
+
+/*
+ * Simple thread creation implementation using pthread API
+ */
+typedef struct {
+       HANDLE handle;
+       void *(*start_routine)(void*);
+       void *arg;
+} pthread_t;
+
+extern int pthread_create(pthread_t *thread, const void *unused,
+                         void *(*start_routine)(void*), void *arg);
+
+/*
+ * To avoid the need of copying a struct, we use small macro wrapper to pass
+ * pointer to win32_pthread_join instead.
+ */
+#define pthread_join(a, b) win32_pthread_join(&(a), (b))
+
+extern int win32_pthread_join(pthread_t *thread, void **value_ptr);
+
+#endif /* PTHREAD_H */