--- Makefile | 2 +- builtin-pack-objects.c | 42 +++++++++- compat/winthread.h | 219 ++++++++++++++++++++++++++++++++++++++++++++++++ preload-index.c | 12 +++ 4 files changed, 272 insertions(+), 3 deletions(-) create mode 100644 compat/winthread.h diff --git a/Makefile b/Makefile index 28d6ecf..126ab43 100644 --- a/Makefile +++ b/Makefile @@ -940,7 +940,7 @@ ifdef MSVC OBJECT_CREATION_USES_RENAMES = UnfortunatelyNeedsTo NO_REGEX = YesPlease NO_CURL = YesPlease - NO_PTHREADS = YesPlease + THREADED_DELTA_SEARCH = YesPlease BLK_SHA1 = YesPlease CC = compat/vcbuild/scripts/clink.pl diff --git a/builtin-pack-objects.c b/builtin-pack-objects.c index 02f9246..a8a4f59 100644 --- a/builtin-pack-objects.c +++ b/builtin-pack-objects.c @@ -18,8 +18,12 @@ #include "refs.h" #ifdef THREADED_DELTA_SEARCH -#include "thread-utils.h" -#include <pthread.h> +# include "thread-utils.h" +# ifndef _WIN32 +# include <pthread.h> +# else +# include <winthread.h> +# endif #endif static const char pack_usage[] = @@ -1592,7 +1596,11 @@ struct thread_params { static pthread_cond_t progress_cond = PTHREAD_COND_INITIALIZER; +#ifndef _WIN32 static void *threaded_find_deltas(void *arg) +#else +static DWORD WINAPI threaded_find_deltas(LPVOID arg) +#endif { struct thread_params *me = arg; @@ -1620,7 +1628,11 @@ static void *threaded_find_deltas(void *arg) pthread_mutex_unlock(&me->mutex); } /* leave ->working 1 so that this doesn't get more work assigned */ +#ifndef _WIN32 return NULL; +#else + return 0; +#endif } static void ll_find_deltas(struct object_entry **list, unsigned list_size, @@ -2327,6 +2339,18 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) #ifdef THREADED_DELTA_SEARCH if (!delta_search_threads) /* --threads=0 means autodetect */ delta_search_threads = online_cpus(); + +#ifdef _WIN32 + /* + * Windows require initialization of mutex (CRITICAL_SECTION) + * and conditional variable. + */ + pthread_mutex_init(&read_mutex); + pthread_mutex_init(&cache_mutex); + pthread_mutex_init(&progress_mutex); + win32_cond_init(&progress_cond); +#endif + #endif prepare_packed_git(); @@ -2345,7 +2369,11 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) stop_progress(&progress_state); if (non_empty && !nr_result) +#if defined(THREADED_DELTA_SEARCH) && defined(_WIN32) + goto cleanup; +#else return 0; +#endif if (nr_result) prepare_pack(window, depth); write_pack_file(); @@ -2353,5 +2381,15 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) fprintf(stderr, "Total %"PRIu32" (delta %"PRIu32")," " reused %"PRIu32" (delta %"PRIu32")\n", written, written_delta, reused, reused_delta); + +#if defined(THREADED_DELTA_SEARCH) && defined(_WIN32) +cleanup: + /* cleanup Windows threads thingies */ + win32_cond_destroy(&progress_cond); + pthread_mutex_destroy(&read_mutex); + pthread_mutex_destroy(&cache_mutex); + pthread_mutex_destroy(&progress_mutex); +#endif + return 0; } diff --git a/compat/winthread.h b/compat/winthread.h new file mode 100644 index 0000000..32c9010 --- /dev/null +++ b/compat/winthread.h @@ -0,0 +1,219 @@ +/* + * Header used to "adapt" pthread-based POSIX code to Windows API threads. + * + * Copyright (C) 2009 Andrzej K. Haczewski <ahaczewski@xxxxxxxxx> + */ + +#ifndef WINTHREAD_H +#define WINTHREAD_H + +#define WIN32_LEAN_AND_MEAN +#include <windows.h> + +/* Implement simple condition variable for Windows threads, based on ACE implementation */ +typedef struct win32_cond { + LONG waiters; + CRITICAL_SECTION waiters_lock; + HANDLE sema; +} win32_cond_t; + +#define PTHREAD_COND_INITIALIZER { 0, { 0 }, NULL } + +static __inline int win32_cond_init(win32_cond_t *cond) +{ + cond->waiters = 0; + + InitializeCriticalSection(&cond->waiters_lock); + + cond->sema = CreateSemaphore(NULL, 0, LONG_MAX, NULL); + if (NULL == cond->sema) + return -1; + return 0; +} + +static __inline int win32_cond_destroy(win32_cond_t *cond) +{ + CloseHandle(cond->sema); + cond->sema = NULL; + + DeleteCriticalSection(&cond->waiters_lock); + + return 0; +} + +static __inline int win32_cond_wait(win32_cond_t *cond, CRITICAL_SECTION *mutex) +{ + DWORD result; + int ret = 0; + + /* we're waiting... */ + EnterCriticalSection(&cond->waiters_lock); + ++cond->waiters; + LeaveCriticalSection(&cond->waiters_lock); + + /* unlock external mutex and wait for signal */ + LeaveCriticalSection(mutex); + result = WaitForSingleObject(cond->sema, INFINITE); + + if (0 != result) + ret = -1; + + /* one waiter less */ + EnterCriticalSection(&cond->waiters_lock); + --cond->waiters; + LeaveCriticalSection(&cond->waiters_lock); + + /* lock external mutex again */ + EnterCriticalSection(mutex); + + return ret; +} + +static __inline int win32_cond_signal(win32_cond_t *cond) +{ + int have_waiters; + + EnterCriticalSection(&cond->waiters_lock); + have_waiters = cond->waiters > 0; + LeaveCriticalSection(&cond->waiters_lock); + + if (have_waiters) + return ReleaseSemaphore(cond->sema, 1, NULL) ? 0 : -1; + else + return 0; +} + +#define pthread_t HANDLE +#define pthread_mutex_t CRITICAL_SECTION +#define pthread_cond_t win32_cond_t + +#define PTHREAD_MUTEX_INITIALIZER { 0 } + +#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 + +#define pthread_cond_init(a,b) win32_cond_init((a)) +#define pthread_cond_destroy win32_cond_destroy +#define pthread_cond_wait(a,b) win32_cond_wait((a), (b)) +#define pthread_cond_signal win32_cond_signal + +#define pthread_create(a,b,c,d) ((NULL == (*(a) = CreateThread(NULL, 0, (c), (d), 0, NULL))) ? (errno = err_win_to_posix(), -1) : (errno = 0, 0)) +#define pthread_join(a,b) ((WAIT_OBJECT_0 == WaitForSingleObject((a), INFINITE)) ? 0 : -1) + +/* almost copy-paste code of mingw.c */ +static int err_win_to_posix() +{ + int error = ENOSYS; + switch(GetLastError()) { + case ERROR_ACCESS_DENIED: error = EACCES; break; + case ERROR_ACCOUNT_DISABLED: error = EACCES; break; + case ERROR_ACCOUNT_RESTRICTION: error = EACCES; break; + case ERROR_ALREADY_ASSIGNED: error = EBUSY; break; + case ERROR_ALREADY_EXISTS: error = EEXIST; break; + case ERROR_ARITHMETIC_OVERFLOW: error = ERANGE; break; + case ERROR_BAD_COMMAND: error = EIO; break; + case ERROR_BAD_DEVICE: error = ENODEV; break; + case ERROR_BAD_DRIVER_LEVEL: error = ENXIO; break; + case ERROR_BAD_EXE_FORMAT: error = ENOEXEC; break; + case ERROR_BAD_FORMAT: error = ENOEXEC; break; + case ERROR_BAD_LENGTH: error = EINVAL; break; + case ERROR_BAD_PATHNAME: error = ENOENT; break; + case ERROR_BAD_PIPE: error = EPIPE; break; + case ERROR_BAD_UNIT: error = ENODEV; break; + case ERROR_BAD_USERNAME: error = EINVAL; break; + case ERROR_BROKEN_PIPE: error = EPIPE; break; + case ERROR_BUFFER_OVERFLOW: error = ENAMETOOLONG; break; + case ERROR_BUSY: error = EBUSY; break; + case ERROR_BUSY_DRIVE: error = EBUSY; break; + case ERROR_CALL_NOT_IMPLEMENTED: error = ENOSYS; break; + case ERROR_CANNOT_MAKE: error = EACCES; break; + case ERROR_CANTOPEN: error = EIO; break; + case ERROR_CANTREAD: error = EIO; break; + case ERROR_CANTWRITE: error = EIO; break; + case ERROR_CRC: error = EIO; break; + case ERROR_CURRENT_DIRECTORY: error = EACCES; break; + case ERROR_DEVICE_IN_USE: error = EBUSY; break; + case ERROR_DEV_NOT_EXIST: error = ENODEV; break; + case ERROR_DIRECTORY: error = EINVAL; break; + case ERROR_DIR_NOT_EMPTY: error = ENOTEMPTY; break; + case ERROR_DISK_CHANGE: error = EIO; break; + case ERROR_DISK_FULL: error = ENOSPC; break; + case ERROR_DRIVE_LOCKED: error = EBUSY; break; + case ERROR_ENVVAR_NOT_FOUND: error = EINVAL; break; + case ERROR_EXE_MARKED_INVALID: error = ENOEXEC; break; + case ERROR_FILENAME_EXCED_RANGE: error = ENAMETOOLONG; break; + case ERROR_FILE_EXISTS: error = EEXIST; break; + case ERROR_FILE_INVALID: error = ENODEV; break; + case ERROR_FILE_NOT_FOUND: error = ENOENT; break; + case ERROR_GEN_FAILURE: error = EIO; break; + case ERROR_HANDLE_DISK_FULL: error = ENOSPC; break; + case ERROR_INSUFFICIENT_BUFFER: error = ENOMEM; break; + case ERROR_INVALID_ACCESS: error = EACCES; break; + case ERROR_INVALID_ADDRESS: error = EFAULT; break; + case ERROR_INVALID_BLOCK: error = EFAULT; break; + case ERROR_INVALID_DATA: error = EINVAL; break; + case ERROR_INVALID_DRIVE: error = ENODEV; break; + case ERROR_INVALID_EXE_SIGNATURE: error = ENOEXEC; break; + case ERROR_INVALID_FLAGS: error = EINVAL; break; + case ERROR_INVALID_FUNCTION: error = ENOSYS; break; + case ERROR_INVALID_HANDLE: error = EBADF; break; + case ERROR_INVALID_LOGON_HOURS: error = EACCES; break; + case ERROR_INVALID_NAME: error = EINVAL; break; + case ERROR_INVALID_OWNER: error = EINVAL; break; + case ERROR_INVALID_PARAMETER: error = EINVAL; break; + case ERROR_INVALID_PASSWORD: error = EPERM; break; + case ERROR_INVALID_PRIMARY_GROUP: error = EINVAL; break; + case ERROR_INVALID_SIGNAL_NUMBER: error = EINVAL; break; + case ERROR_INVALID_TARGET_HANDLE: error = EIO; break; + case ERROR_INVALID_WORKSTATION: error = EACCES; break; + case ERROR_IO_DEVICE: error = EIO; break; + case ERROR_IO_INCOMPLETE: error = EINTR; break; + case ERROR_LOCKED: error = EBUSY; break; + case ERROR_LOCK_VIOLATION: error = EACCES; break; + case ERROR_LOGON_FAILURE: error = EACCES; break; + case ERROR_MAPPED_ALIGNMENT: error = EINVAL; break; + case ERROR_META_EXPANSION_TOO_LONG: error = E2BIG; break; + case ERROR_MORE_DATA: error = EPIPE; break; + case ERROR_NEGATIVE_SEEK: error = ESPIPE; break; + case ERROR_NOACCESS: error = EFAULT; break; + case ERROR_NONE_MAPPED: error = EINVAL; break; + case ERROR_NOT_ENOUGH_MEMORY: error = ENOMEM; break; + case ERROR_NOT_READY: error = EAGAIN; break; + case ERROR_NOT_SAME_DEVICE: error = EXDEV; break; + case ERROR_NO_DATA: error = EPIPE; break; + case ERROR_NO_MORE_SEARCH_HANDLES: error = EIO; break; + case ERROR_NO_PROC_SLOTS: error = EAGAIN; break; + case ERROR_NO_SUCH_PRIVILEGE: error = EACCES; break; + case ERROR_OPEN_FAILED: error = EIO; break; + case ERROR_OPEN_FILES: error = EBUSY; break; + case ERROR_OPERATION_ABORTED: error = EINTR; break; + case ERROR_OUTOFMEMORY: error = ENOMEM; break; + case ERROR_PASSWORD_EXPIRED: error = EACCES; break; + case ERROR_PATH_BUSY: error = EBUSY; break; + case ERROR_PATH_NOT_FOUND: error = ENOENT; break; + case ERROR_PIPE_BUSY: error = EBUSY; break; + case ERROR_PIPE_CONNECTED: error = EPIPE; break; + case ERROR_PIPE_LISTENING: error = EPIPE; break; + case ERROR_PIPE_NOT_CONNECTED: error = EPIPE; break; + case ERROR_PRIVILEGE_NOT_HELD: error = EACCES; break; + case ERROR_READ_FAULT: error = EIO; break; + case ERROR_SEEK: error = EIO; break; + case ERROR_SEEK_ON_DEVICE: error = ESPIPE; break; + case ERROR_SHARING_BUFFER_EXCEEDED: error = ENFILE; break; + case ERROR_SHARING_VIOLATION: error = EACCES; break; + case ERROR_STACK_OVERFLOW: error = ENOMEM; break; + case ERROR_SWAPERROR: error = ENOENT; break; + case ERROR_TOO_MANY_MODULES: error = EMFILE; break; + case ERROR_TOO_MANY_OPEN_FILES: error = EMFILE; break; + case ERROR_UNRECOGNIZED_MEDIA: error = ENXIO; break; + case ERROR_UNRECOGNIZED_VOLUME: error = ENODEV; break; + case ERROR_WAIT_NO_CHILDREN: error = ECHILD; break; + case ERROR_WRITE_FAULT: error = EIO; break; + case ERROR_WRITE_PROTECT: error = EROFS; break; + } + return error; +} + +#endif /* WINTHREAD_H */ diff --git a/preload-index.c b/preload-index.c index 9289933..6d69a8d 100644 --- a/preload-index.c +++ b/preload-index.c @@ -10,7 +10,11 @@ static void preload_index(struct index_state *index, const char **pathspec) } #else +#ifndef _WIN32 #include <pthread.h> +#else +#include <winthread.h> +#endif /* * Mostly randomly chosen maximum thread counts: we @@ -28,7 +32,11 @@ struct thread_data { int offset, nr; }; +#ifndef _WIN32 static void *preload_thread(void *_data) +#else +static DWORD WINAPI preload_thread(LPVOID _data) +#endif { int nr; struct thread_data *p = _data; @@ -59,7 +67,11 @@ static void *preload_thread(void *_data) continue; ce_mark_uptodate(ce); } while (--nr > 0); +#ifndef _WIN32 return NULL; +#else + return 0; +#endif } static void preload_index(struct index_state *index, const char **pathspec) -- 1.6.5.2 -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html