MSVC: Windows-native implementation for subset of Pthreads API
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>
Andrzej K. Haczewski committed
Jan 15, 2010 at 21:12 UTC
44626dc7d562d23b54d969bb73ddeda12d5602e9
6 files changed
+217
-11
Makefile
+7
-6
@@ -478,6 +478,7 @@ LIB_H += commit.h
478
LIB_H += compat/bswap.h
479
LIB_H += compat/cygwin.h
480
LIB_H += compat/mingw.h
481
+LIB_H += compat/win32/pthread.h
482
LIB_H += csum-file.h
483
LIB_H += decorate.h
484
LIB_H += delta.h
@@ -996,15 +997,15 @@ ifeq ($(uname_S),Windows)
997
NO_REGEX = YesPlease
998
NO_CURL = YesPlease
999
NO_PYTHON = YesPlease
999
- NO_PTHREADS = YesPlease
1000
BLK_SHA1 = YesPlease
1001
+ THREADED_DELTA_SEARCH = YesPlease
1002
1003
CC = compat/vcbuild/scripts/clink.pl
1004
AR = compat/vcbuild/scripts/lib.pl
1005
CFLAGS =
1006
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
1006
- COMPAT_OBJS = compat/msvc.o compat/fnmatch/fnmatch.o compat/winansi.o
1007
- COMPAT_CFLAGS = -D__USE_MINGW_ACCESS -DNOGDI -DHAVE_STRING_H -DHAVE_ALLOCA_H -Icompat -Icompat/fnmatch -Icompat/regex -Icompat/fnmatch -DSTRIP_EXTENSION=\".exe\"
1007
+ COMPAT_OBJS = compat/msvc.o compat/fnmatch/fnmatch.o compat/winansi.o compat/win32/pthread.o
1008
+ 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\"
1009
BASIC_LDFLAGS = -IGNORE:4217 -IGNORE:4049 -NOLOGO -SUBSYSTEM:CONSOLE -NODEFAULTLIB:MSVCRT.lib
1010
EXTLIBS = advapi32.lib shell32.lib wininet.lib ws2_32.lib
1011
lib =
@@ -1048,9 +1049,11 @@ ifneq (,$(findstring MINGW,$(uname_S)))
1049
NO_REGEX = YesPlease
1050
NO_PYTHON = YesPlease
1051
BLK_SHA1 = YesPlease
1052
+ THREADED_DELTA_SEARCH = YesPlease
1053
COMPAT_CFLAGS += -D__USE_MINGW_ACCESS -DNOGDI -Icompat -Icompat/fnmatch
1054
COMPAT_CFLAGS += -DSTRIP_EXTENSION=\".exe\"
1053
- COMPAT_OBJS += compat/mingw.o compat/fnmatch/fnmatch.o compat/winansi.o
1055
+ COMPAT_OBJS += compat/mingw.o compat/fnmatch/fnmatch.o compat/winansi.o \
1056
+ compat/win32/pthread.o
1057
EXTLIBS += -lws2_32
1058
X = .exe
1059
ifneq (,$(wildcard ../THIS_IS_MSYSGIT))
@@ -1060,10 +1063,8 @@ ifneq (,$(wildcard ../THIS_IS_MSYSGIT))
1063
EXTLIBS += /mingw/lib/libz.a
1064
NO_R_TO_GCC_LINKER = YesPlease
1065
INTERNAL_QSORT = YesPlease
1063
- THREADED_DELTA_SEARCH = YesPlease
1066
else
1067
NO_CURL = YesPlease
1066
- NO_PTHREADS = YesPlease
1068
endif
1069
endif
1070
builtin-pack-objects.c
+27
-4
@@ -1256,15 +1256,15 @@ static int delta_cacheable(unsigned long src_size, unsigned long trg_size,
1256
1257
#ifdef THREADED_DELTA_SEARCH
1258
1259
-static pthread_mutex_t read_mutex = PTHREAD_MUTEX_INITIALIZER;
1259
+static pthread_mutex_t read_mutex;
1260
#define read_lock() pthread_mutex_lock(&read_mutex)
1261
#define read_unlock() pthread_mutex_unlock(&read_mutex)
1262
1263
-static pthread_mutex_t cache_mutex = PTHREAD_MUTEX_INITIALIZER;
1263
+static pthread_mutex_t cache_mutex;
1264
#define cache_lock() pthread_mutex_lock(&cache_mutex)
1265
#define cache_unlock() pthread_mutex_unlock(&cache_mutex)
1266
1267
-static pthread_mutex_t progress_mutex = PTHREAD_MUTEX_INITIALIZER;
1267
+static pthread_mutex_t progress_mutex;
1268
#define progress_lock() pthread_mutex_lock(&progress_mutex)
1269
#define progress_unlock() pthread_mutex_unlock(&progress_mutex)
1270
@@ -1591,7 +1591,26 @@ struct thread_params {
1591
unsigned *processed;
1592
};
1593
1594
-static pthread_cond_t progress_cond = PTHREAD_COND_INITIALIZER;
1594
+static pthread_cond_t progress_cond;
1595
+
1596
+/*
1597
+ * Mutex and conditional variable can't be statically-initialized on Windows.
1598
+ */
1599
+static void init_threaded_search(void)
1600
+{
1601
+ pthread_mutex_init(&read_mutex, NULL);
1602
+ pthread_mutex_init(&cache_mutex, NULL);
1603
+ pthread_mutex_init(&progress_mutex, NULL);
1604
+ pthread_cond_init(&progress_cond, NULL);
1605
+}
1606
+
1607
+static void cleanup_threaded_search(void)
1608
+{
1609
+ pthread_cond_destroy(&progress_cond);
1610
+ pthread_mutex_destroy(&read_mutex);
1611
+ pthread_mutex_destroy(&cache_mutex);
1612
+ pthread_mutex_destroy(&progress_mutex);
1613
+}
1614
1615
static void *threaded_find_deltas(void *arg)
1616
{
@@ -1630,10 +1649,13 @@ static void ll_find_deltas(struct object_entry **list, unsigned list_size,
1649
struct thread_params *p;
1650
int i, ret, active_threads = 0;
1651
1652
+ init_threaded_search();
1653
+
1654
if (!delta_search_threads) /* --threads=0 means autodetect */
1655
delta_search_threads = online_cpus();
1656
if (delta_search_threads <= 1) {
1657
find_deltas(list, &list_size, window, depth, processed);
1658
+ cleanup_threaded_search();
1659
return;
1660
}
1661
if (progress > pack_to_stdout)
@@ -1748,6 +1770,7 @@ static void ll_find_deltas(struct object_entry **list, unsigned list_size,
1770
active_threads--;
1771
}
1772
}
1773
+ cleanup_threaded_search();
1774
free(p);
1775
}
1776
compat/mingw.c
+1
-1
@@ -3,7 +3,7 @@
3
#include <conio.h>
4
#include "../strbuf.h"
5
6
-static int err_win_to_posix(DWORD winerr)
6
+int err_win_to_posix(DWORD winerr)
7
{
8
int error = ENOSYS;
9
switch(winerr) {
compat/mingw.h
+5
@@ -310,3 +310,8 @@ struct mingw_dirent
310
#define readdir(x) mingw_readdir(x)
311
struct dirent *mingw_readdir(DIR *dir);
312
#endif // !NO_MINGW_REPLACE_READDIR
313
+
314
+/*
315
+ * Used by Pthread API implementation for Windows
316
+ */
317
+extern int err_win_to_posix(DWORD winerr);
compat/win32/pthread.c
new
+110
@@ -0,0 +1,110 @@
1
+/*
2
+ * Copyright (C) 2009 Andrzej K. Haczewski <ahaczewski@gmail.com>
3
+ *
4
+ * DISCLAMER: The implementation is Git-specific, it is subset of original
5
+ * Pthreads API, without lots of other features that Git doesn't use.
6
+ * Git also makes sure that the passed arguments are valid, so there's
7
+ * no need for double-checking.
8
+ */
9
+
10
+#include "../../git-compat-util.h"
11
+#include "pthread.h"
12
+
13
+#include <errno.h>
14
+#include <limits.h>
15
+
16
+static unsigned __stdcall win32_start_routine(void *arg)
17
+{
18
+ pthread_t *thread = arg;
19
+ thread->arg = thread->start_routine(thread->arg);
20
+ return 0;
21
+}
22
+
23
+int pthread_create(pthread_t *thread, const void *unused,
24
+ void *(*start_routine)(void*), void *arg)
25
+{
26
+ thread->arg = arg;
27
+ thread->start_routine = start_routine;
28
+ thread->handle = (HANDLE)
29
+ _beginthreadex(NULL, 0, win32_start_routine, thread, 0, NULL);
30
+
31
+ if (!thread->handle)
32
+ return errno;
33
+ else
34
+ return 0;
35
+}
36
+
37
+int win32_pthread_join(pthread_t *thread, void **value_ptr)
38
+{
39
+ DWORD result = WaitForSingleObject(thread->handle, INFINITE);
40
+ switch (result) {
41
+ case WAIT_OBJECT_0:
42
+ if (value_ptr)
43
+ *value_ptr = thread->arg;
44
+ return 0;
45
+ case WAIT_ABANDONED:
46
+ return EINVAL;
47
+ default:
48
+ return err_win_to_posix(GetLastError());
49
+ }
50
+}
51
+
52
+int pthread_cond_init(pthread_cond_t *cond, const void *unused)
53
+{
54
+ cond->waiters = 0;
55
+
56
+ cond->sema = CreateSemaphore(NULL, 0, LONG_MAX, NULL);
57
+ if (!cond->sema)
58
+ die("CreateSemaphore() failed");
59
+ return 0;
60
+}
61
+
62
+int pthread_cond_destroy(pthread_cond_t *cond)
63
+{
64
+ CloseHandle(cond->sema);
65
+ cond->sema = NULL;
66
+
67
+ return 0;
68
+}
69
+
70
+int pthread_cond_wait(pthread_cond_t *cond, CRITICAL_SECTION *mutex)
71
+{
72
+ InterlockedIncrement(&cond->waiters);
73
+
74
+ /*
75
+ * Unlock external mutex and wait for signal.
76
+ * NOTE: we've held mutex locked long enough to increment
77
+ * waiters count above, so there's no problem with
78
+ * leaving mutex unlocked before we wait on semaphore.
79
+ */
80
+ LeaveCriticalSection(mutex);
81
+
82
+ /* let's wait - ignore return value */
83
+ WaitForSingleObject(cond->sema, INFINITE);
84
+
85
+ /* we're done waiting, so make sure we decrease waiters count */
86
+ InterlockedDecrement(&cond->waiters);
87
+
88
+ /* lock external mutex again */
89
+ EnterCriticalSection(mutex);
90
+
91
+ return 0;
92
+}
93
+
94
+int pthread_cond_signal(pthread_cond_t *cond)
95
+{
96
+ /*
97
+ * Access to waiters count is atomic; see "Interlocked Variable Access"
98
+ * http://msdn.microsoft.com/en-us/library/ms684122(VS.85).aspx
99
+ */
100
+ int have_waiters = cond->waiters > 0;
101
+
102
+ /*
103
+ * Signal only when there are waiters
104
+ */
105
+ if (have_waiters)
106
+ return ReleaseSemaphore(cond->sema, 1, NULL) ?
107
+ 0 : err_win_to_posix(GetLastError());
108
+ else
109
+ return 0;
110
+}
compat/win32/pthread.h
new
+67
@@ -0,0 +1,67 @@
1
+/*
2
+ * Header used to adapt pthread-based POSIX code to Windows API threads.
3
+ *
4
+ * Copyright (C) 2009 Andrzej K. Haczewski <ahaczewski@gmail.com>
5
+ */
6
+
7
+#ifndef PTHREAD_H
8
+#define PTHREAD_H
9
+
10
+#ifndef WIN32_LEAN_AND_MEAN
11
+#define WIN32_LEAN_AND_MEAN
12
+#endif
13
+
14
+#include <windows.h>
15
+
16
+/*
17
+ * Defines that adapt Windows API threads to pthreads API
18
+ */
19
+#define pthread_mutex_t CRITICAL_SECTION
20
+
21
+#define pthread_mutex_init(a,b) InitializeCriticalSection((a))
22
+#define pthread_mutex_destroy(a) DeleteCriticalSection((a))
23
+#define pthread_mutex_lock EnterCriticalSection
24
+#define pthread_mutex_unlock LeaveCriticalSection
25
+
26
+/*
27
+ * Implement simple condition variable for Windows threads, based on ACE
28
+ * implementation.
29
+ *
30
+ * See original implementation: http://bit.ly/1vkDjo
31
+ * ACE homepage: http://www.cse.wustl.edu/~schmidt/ACE.html
32
+ * See also: http://www.cse.wustl.edu/~schmidt/win32-cv-1.html
33
+ */
34
+typedef struct {
35
+ volatile LONG waiters;
36
+ HANDLE sema;
37
+} pthread_cond_t;
38
+
39
+extern int pthread_cond_init(pthread_cond_t *cond, const void *unused);
40
+
41
+extern int pthread_cond_destroy(pthread_cond_t *cond);
42
+
43
+extern int pthread_cond_wait(pthread_cond_t *cond, CRITICAL_SECTION *mutex);
44
+
45
+extern int pthread_cond_signal(pthread_cond_t *cond);
46
+
47
+/*
48
+ * Simple thread creation implementation using pthread API
49
+ */
50
+typedef struct {
51
+ HANDLE handle;
52
+ void *(*start_routine)(void*);
53
+ void *arg;
54
+} pthread_t;
55
+
56
+extern int pthread_create(pthread_t *thread, const void *unused,
57
+ void *(*start_routine)(void*), void *arg);
58
+
59
+/*
60
+ * To avoid the need of copying a struct, we use small macro wrapper to pass
61
+ * pointer to win32_pthread_join instead.
62
+ */
63
+#define pthread_join(a, b) win32_pthread_join(&(a), (b))
64
+
65
+extern int win32_pthread_join(pthread_t *thread, void **value_ptr);
66
+
67
+#endif /* PTHREAD_H */