Skip to content

Commit 44626dc

Browse files
ahaczewskigitster
authored andcommitted
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 <[email protected]> Signed-off-by: Johannes Sixt <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent b6f714f commit 44626dc

File tree

6 files changed

+217
-11
lines changed

6 files changed

+217
-11
lines changed

Makefile

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -478,6 +478,7 @@ LIB_H += commit.h
478478
LIB_H += compat/bswap.h
479479
LIB_H += compat/cygwin.h
480480
LIB_H += compat/mingw.h
481+
LIB_H += compat/win32/pthread.h
481482
LIB_H += csum-file.h
482483
LIB_H += decorate.h
483484
LIB_H += delta.h
@@ -996,15 +997,15 @@ ifeq ($(uname_S),Windows)
996997
NO_REGEX = YesPlease
997998
NO_CURL = YesPlease
998999
NO_PYTHON = YesPlease
999-
NO_PTHREADS = YesPlease
10001000
BLK_SHA1 = YesPlease
1001+
THREADED_DELTA_SEARCH = YesPlease
10011002

10021003
CC = compat/vcbuild/scripts/clink.pl
10031004
AR = compat/vcbuild/scripts/lib.pl
10041005
CFLAGS =
10051006
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\"
10081009
BASIC_LDFLAGS = -IGNORE:4217 -IGNORE:4049 -NOLOGO -SUBSYSTEM:CONSOLE -NODEFAULTLIB:MSVCRT.lib
10091010
EXTLIBS = advapi32.lib shell32.lib wininet.lib ws2_32.lib
10101011
lib =
@@ -1048,9 +1049,11 @@ ifneq (,$(findstring MINGW,$(uname_S)))
10481049
NO_REGEX = YesPlease
10491050
NO_PYTHON = YesPlease
10501051
BLK_SHA1 = YesPlease
1052+
THREADED_DELTA_SEARCH = YesPlease
10511053
COMPAT_CFLAGS += -D__USE_MINGW_ACCESS -DNOGDI -Icompat -Icompat/fnmatch
10521054
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
10541057
EXTLIBS += -lws2_32
10551058
X = .exe
10561059
ifneq (,$(wildcard ../THIS_IS_MSYSGIT))
@@ -1060,10 +1063,8 @@ ifneq (,$(wildcard ../THIS_IS_MSYSGIT))
10601063
EXTLIBS += /mingw/lib/libz.a
10611064
NO_R_TO_GCC_LINKER = YesPlease
10621065
INTERNAL_QSORT = YesPlease
1063-
THREADED_DELTA_SEARCH = YesPlease
10641066
else
10651067
NO_CURL = YesPlease
1066-
NO_PTHREADS = YesPlease
10671068
endif
10681069
endif
10691070

builtin-pack-objects.c

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1256,15 +1256,15 @@ static int delta_cacheable(unsigned long src_size, unsigned long trg_size,
12561256

12571257
#ifdef THREADED_DELTA_SEARCH
12581258

1259-
static pthread_mutex_t read_mutex = PTHREAD_MUTEX_INITIALIZER;
1259+
static pthread_mutex_t read_mutex;
12601260
#define read_lock() pthread_mutex_lock(&read_mutex)
12611261
#define read_unlock() pthread_mutex_unlock(&read_mutex)
12621262

1263-
static pthread_mutex_t cache_mutex = PTHREAD_MUTEX_INITIALIZER;
1263+
static pthread_mutex_t cache_mutex;
12641264
#define cache_lock() pthread_mutex_lock(&cache_mutex)
12651265
#define cache_unlock() pthread_mutex_unlock(&cache_mutex)
12661266

1267-
static pthread_mutex_t progress_mutex = PTHREAD_MUTEX_INITIALIZER;
1267+
static pthread_mutex_t progress_mutex;
12681268
#define progress_lock() pthread_mutex_lock(&progress_mutex)
12691269
#define progress_unlock() pthread_mutex_unlock(&progress_mutex)
12701270

@@ -1591,7 +1591,26 @@ struct thread_params {
15911591
unsigned *processed;
15921592
};
15931593

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+
}
15951614

15961615
static void *threaded_find_deltas(void *arg)
15971616
{
@@ -1630,10 +1649,13 @@ static void ll_find_deltas(struct object_entry **list, unsigned list_size,
16301649
struct thread_params *p;
16311650
int i, ret, active_threads = 0;
16321651

1652+
init_threaded_search();
1653+
16331654
if (!delta_search_threads) /* --threads=0 means autodetect */
16341655
delta_search_threads = online_cpus();
16351656
if (delta_search_threads <= 1) {
16361657
find_deltas(list, &list_size, window, depth, processed);
1658+
cleanup_threaded_search();
16371659
return;
16381660
}
16391661
if (progress > pack_to_stdout)
@@ -1748,6 +1770,7 @@ static void ll_find_deltas(struct object_entry **list, unsigned list_size,
17481770
active_threads--;
17491771
}
17501772
}
1773+
cleanup_threaded_search();
17511774
free(p);
17521775
}
17531776

compat/mingw.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
#include <conio.h>
44
#include "../strbuf.h"
55

6-
static int err_win_to_posix(DWORD winerr)
6+
int err_win_to_posix(DWORD winerr)
77
{
88
int error = ENOSYS;
99
switch(winerr) {

compat/mingw.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,3 +310,8 @@ struct mingw_dirent
310310
#define readdir(x) mingw_readdir(x)
311311
struct dirent *mingw_readdir(DIR *dir);
312312
#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

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
/*
2+
* Copyright (C) 2009 Andrzej K. Haczewski <[email protected]>
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

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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 <[email protected]>
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 */

0 commit comments

Comments
 (0)