diff options
Diffstat (limited to 'compat/win32')
-rw-r--r-- | compat/win32/alloca.h | 1 | ||||
-rw-r--r-- | compat/win32/dirent.c | 92 | ||||
-rw-r--r-- | compat/win32/dirent.h | 20 | ||||
-rw-r--r-- | compat/win32/pthread.c | 110 | ||||
-rw-r--r-- | compat/win32/pthread.h | 47 | ||||
-rw-r--r-- | compat/win32/syslog.c | 78 | ||||
-rw-r--r-- | compat/win32/syslog.h | 20 |
7 files changed, 351 insertions, 17 deletions
diff --git a/compat/win32/alloca.h b/compat/win32/alloca.h new file mode 100644 index 0000000000..c0d7985b7e --- /dev/null +++ b/compat/win32/alloca.h @@ -0,0 +1 @@ +#include <malloc.h> diff --git a/compat/win32/dirent.c b/compat/win32/dirent.c new file mode 100644 index 0000000000..52420ec7d4 --- /dev/null +++ b/compat/win32/dirent.c @@ -0,0 +1,92 @@ +#include "../../git-compat-util.h" + +struct DIR { + struct dirent dd_dir; /* includes d_type */ + HANDLE dd_handle; /* FindFirstFile handle */ + int dd_stat; /* 0-based index */ +}; + +static inline void finddata2dirent(struct dirent *ent, WIN32_FIND_DATAW *fdata) +{ + /* convert UTF-16 name to UTF-8 */ + xwcstoutf(ent->d_name, fdata->cFileName, sizeof(ent->d_name)); + + /* Set file type, based on WIN32_FIND_DATA */ + if (fdata->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + ent->d_type = DT_DIR; + else + ent->d_type = DT_REG; +} + +DIR *opendir(const char *name) +{ + wchar_t pattern[MAX_PATH + 2]; /* + 2 for '/' '*' */ + WIN32_FIND_DATAW fdata; + HANDLE h; + int len; + DIR *dir; + + /* convert name to UTF-16 and check length < MAX_PATH */ + if ((len = xutftowcs_path(pattern, name)) < 0) + return NULL; + + /* append optional '/' and wildcard '*' */ + if (len && !is_dir_sep(pattern[len - 1])) + pattern[len++] = '/'; + pattern[len++] = '*'; + pattern[len] = 0; + + /* open find handle */ + h = FindFirstFileW(pattern, &fdata); + if (h == INVALID_HANDLE_VALUE) { + DWORD err = GetLastError(); + errno = (err == ERROR_DIRECTORY) ? ENOTDIR : err_win_to_posix(err); + return NULL; + } + + /* initialize DIR structure and copy first dir entry */ + dir = xmalloc(sizeof(DIR)); + dir->dd_handle = h; + dir->dd_stat = 0; + finddata2dirent(&dir->dd_dir, &fdata); + return dir; +} + +struct dirent *readdir(DIR *dir) +{ + if (!dir) { + errno = EBADF; /* No set_errno for mingw */ + return NULL; + } + + /* if first entry, dirent has already been set up by opendir */ + if (dir->dd_stat) { + /* get next entry and convert from WIN32_FIND_DATA to dirent */ + WIN32_FIND_DATAW fdata; + if (FindNextFileW(dir->dd_handle, &fdata)) { + finddata2dirent(&dir->dd_dir, &fdata); + } else { + DWORD lasterr = GetLastError(); + /* POSIX says you shouldn't set errno when readdir can't + find any more files; so, if another error we leave it set. */ + if (lasterr != ERROR_NO_MORE_FILES) + errno = err_win_to_posix(lasterr); + return NULL; + } + } + + ++dir->dd_stat; + return &dir->dd_dir; +} + +int closedir(DIR *dir) +{ + if (!dir) { + errno = EBADF; + return -1; + } + + FindClose(dir->dd_handle); + free(dir); + return 0; +} diff --git a/compat/win32/dirent.h b/compat/win32/dirent.h new file mode 100644 index 0000000000..058207e4bf --- /dev/null +++ b/compat/win32/dirent.h @@ -0,0 +1,20 @@ +#ifndef DIRENT_H +#define DIRENT_H + +typedef struct DIR DIR; + +#define DT_UNKNOWN 0 +#define DT_DIR 1 +#define DT_REG 2 +#define DT_LNK 3 + +struct dirent { + unsigned char d_type; /* file type to prevent lstat after readdir */ + char d_name[MAX_PATH * 3]; /* file name (* 3 for UTF-8 conversion) */ +}; + +DIR *opendir(const char *dirname); +struct dirent *readdir(DIR *dir); +int closedir(DIR *dir); + +#endif /* DIRENT_H */ diff --git a/compat/win32/pthread.c b/compat/win32/pthread.c index 631c0a46ea..e18f5c6e2e 100644 --- a/compat/win32/pthread.c +++ b/compat/win32/pthread.c @@ -1,7 +1,7 @@ /* * Copyright (C) 2009 Andrzej K. Haczewski <ahaczewski@gmail.com> * - * DISCLAMER: The implementation is Git-specific, it is subset of original + * DISCLAIMER: 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. @@ -16,6 +16,7 @@ static unsigned __stdcall win32_start_routine(void *arg) { pthread_t *thread = arg; + thread->tid = GetCurrentThreadId(); thread->arg = thread->start_routine(thread->arg); return 0; } @@ -49,27 +50,48 @@ int win32_pthread_join(pthread_t *thread, void **value_ptr) } } +pthread_t pthread_self(void) +{ + pthread_t t = { NULL }; + t.tid = GetCurrentThreadId(); + return t; +} + int pthread_cond_init(pthread_cond_t *cond, const void *unused) { cond->waiters = 0; + cond->was_broadcast = 0; + InitializeCriticalSection(&cond->waiters_lock); cond->sema = CreateSemaphore(NULL, 0, LONG_MAX, NULL); if (!cond->sema) die("CreateSemaphore() failed"); + + cond->continue_broadcast = CreateEvent(NULL, /* security */ + FALSE, /* auto-reset */ + FALSE, /* not signaled */ + NULL); /* name */ + if (!cond->continue_broadcast) + die("CreateEvent() failed"); + return 0; } int pthread_cond_destroy(pthread_cond_t *cond) { CloseHandle(cond->sema); - cond->sema = NULL; - + CloseHandle(cond->continue_broadcast); + DeleteCriticalSection(&cond->waiters_lock); return 0; } int pthread_cond_wait(pthread_cond_t *cond, CRITICAL_SECTION *mutex) { - InterlockedIncrement(&cond->waiters); + int last_waiter; + + EnterCriticalSection(&cond->waiters_lock); + cond->waiters++; + LeaveCriticalSection(&cond->waiters_lock); /* * Unlock external mutex and wait for signal. @@ -82,22 +104,52 @@ int pthread_cond_wait(pthread_cond_t *cond, CRITICAL_SECTION *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); - + /* + * Decrease waiters count. If we are the last waiter, then we must + * notify the broadcasting thread that it can continue. + * But if we continued due to cond_signal, we do not have to do that + * because the signaling thread knows that only one waiter continued. + */ + EnterCriticalSection(&cond->waiters_lock); + cond->waiters--; + last_waiter = cond->was_broadcast && cond->waiters == 0; + LeaveCriticalSection(&cond->waiters_lock); + + if (last_waiter) { + /* + * cond_broadcast was issued while mutex was held. This means + * that all other waiters have continued, but are contending + * for the mutex at the end of this function because the + * broadcasting thread did not leave cond_broadcast, yet. + * (This is so that it can be sure that each waiter has + * consumed exactly one slice of the semaphor.) + * The last waiter must tell the broadcasting thread that it + * can go on. + */ + SetEvent(cond->continue_broadcast); + /* + * Now we go on to contend with all other waiters for + * the mutex. Auf in den Kampf! + */ + } /* lock external mutex again */ EnterCriticalSection(mutex); return 0; } +/* + * IMPORTANT: This implementation requires that pthread_cond_signal + * is called while the mutex is held that is used in the corresponding + * pthread_cond_wait calls! + */ 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; + int have_waiters; + + EnterCriticalSection(&cond->waiters_lock); + have_waiters = cond->waiters > 0; + LeaveCriticalSection(&cond->waiters_lock); /* * Signal only when there are waiters @@ -108,3 +160,37 @@ int pthread_cond_signal(pthread_cond_t *cond) else return 0; } + +/* + * DOUBLY IMPORTANT: This implementation requires that pthread_cond_broadcast + * is called while the mutex is held that is used in the corresponding + * pthread_cond_wait calls! + */ +int pthread_cond_broadcast(pthread_cond_t *cond) +{ + EnterCriticalSection(&cond->waiters_lock); + + if ((cond->was_broadcast = cond->waiters > 0)) { + /* wake up all waiters */ + ReleaseSemaphore(cond->sema, cond->waiters, NULL); + LeaveCriticalSection(&cond->waiters_lock); + /* + * At this point all waiters continue. Each one takes its + * slice of the semaphor. Now it's our turn to wait: Since + * the external mutex is held, no thread can leave cond_wait, + * yet. For this reason, we can be sure that no thread gets + * a chance to eat *more* than one slice. OTOH, it means + * that the last waiter must send us a wake-up. + */ + WaitForSingleObject(cond->continue_broadcast, INFINITE); + /* + * Since the external mutex is held, no thread can enter + * cond_wait, and, hence, it is safe to reset this flag + * without cond->waiters_lock held. + */ + cond->was_broadcast = 0; + } else { + LeaveCriticalSection(&cond->waiters_lock); + } + return 0; +} diff --git a/compat/win32/pthread.h b/compat/win32/pthread.h index b8e1bcb046..8ad187344f 100644 --- a/compat/win32/pthread.h +++ b/compat/win32/pthread.h @@ -18,11 +18,17 @@ */ #define pthread_mutex_t CRITICAL_SECTION -#define pthread_mutex_init(a,b) InitializeCriticalSection((a)) +#define pthread_mutex_init(a,b) (InitializeCriticalSection((a)), 0) #define pthread_mutex_destroy(a) DeleteCriticalSection((a)) #define pthread_mutex_lock EnterCriticalSection #define pthread_mutex_unlock LeaveCriticalSection +typedef int pthread_mutexattr_t; +#define pthread_mutexattr_init(a) (*(a) = 0) +#define pthread_mutexattr_destroy(a) do {} while (0) +#define pthread_mutexattr_settype(a, t) 0 +#define PTHREAD_MUTEX_RECURSIVE 0 + /* * Implement simple condition variable for Windows threads, based on ACE * implementation. @@ -32,17 +38,18 @@ * See also: http://www.cse.wustl.edu/~schmidt/win32-cv-1.html */ typedef struct { - volatile LONG waiters; + LONG waiters; + int was_broadcast; + CRITICAL_SECTION waiters_lock; HANDLE sema; + HANDLE continue_broadcast; } 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); +extern int pthread_cond_broadcast(pthread_cond_t *cond); /* * Simple thread creation implementation using pthread API @@ -51,6 +58,7 @@ typedef struct { HANDLE handle; void *(*start_routine)(void*); void *arg; + DWORD tid; } pthread_t; extern int pthread_create(pthread_t *thread, const void *unused, @@ -64,4 +72,33 @@ extern int pthread_create(pthread_t *thread, const void *unused, extern int win32_pthread_join(pthread_t *thread, void **value_ptr); +#define pthread_equal(t1, t2) ((t1).tid == (t2).tid) +extern pthread_t pthread_self(void); + +static inline int pthread_exit(void *ret) +{ + ExitThread((DWORD)ret); +} + +typedef DWORD pthread_key_t; +static inline int pthread_key_create(pthread_key_t *keyp, void (*destructor)(void *value)) +{ + return (*keyp = TlsAlloc()) == TLS_OUT_OF_INDEXES ? EAGAIN : 0; +} + +static inline int pthread_key_delete(pthread_key_t key) +{ + return TlsFree(key) ? 0 : EINVAL; +} + +static inline int pthread_setspecific(pthread_key_t key, const void *value) +{ + return TlsSetValue(key, (void *)value) ? 0 : EINVAL; +} + +static inline void *pthread_getspecific(pthread_key_t key) +{ + return TlsGetValue(key); +} + #endif /* PTHREAD_H */ diff --git a/compat/win32/syslog.c b/compat/win32/syslog.c new file mode 100644 index 0000000000..d015e436d5 --- /dev/null +++ b/compat/win32/syslog.c @@ -0,0 +1,78 @@ +#include "../../git-compat-util.h" + +static HANDLE ms_eventlog; + +void openlog(const char *ident, int logopt, int facility) +{ + if (ms_eventlog) + return; + + ms_eventlog = RegisterEventSourceA(NULL, ident); + + if (!ms_eventlog) + warning("RegisterEventSource() failed: %lu", GetLastError()); +} + +void syslog(int priority, const char *fmt, ...) +{ + WORD logtype; + char *str, *pos; + int str_len; + va_list ap; + + if (!ms_eventlog) + return; + + va_start(ap, fmt); + str_len = vsnprintf(NULL, 0, fmt, ap); + va_end(ap); + + if (str_len < 0) { + warning("vsnprintf failed: '%s'", strerror(errno)); + return; + } + + str = malloc(str_len + 1); + if (!str) { + warning("malloc failed: '%s'", strerror(errno)); + return; + } + + va_start(ap, fmt); + vsnprintf(str, str_len + 1, fmt, ap); + va_end(ap); + + while ((pos = strstr(str, "%1")) != NULL) { + str = realloc(str, ++str_len + 1); + if (!str) { + warning("realloc failed: '%s'", strerror(errno)); + return; + } + memmove(pos + 2, pos + 1, strlen(pos)); + pos[1] = ' '; + } + + switch (priority) { + case LOG_EMERG: + case LOG_ALERT: + case LOG_CRIT: + case LOG_ERR: + logtype = EVENTLOG_ERROR_TYPE; + break; + + case LOG_WARNING: + logtype = EVENTLOG_WARNING_TYPE; + break; + + case LOG_NOTICE: + case LOG_INFO: + case LOG_DEBUG: + default: + logtype = EVENTLOG_INFORMATION_TYPE; + break; + } + + ReportEventA(ms_eventlog, logtype, 0, 0, NULL, 1, 0, + (const char **)&str, NULL); + free(str); +} diff --git a/compat/win32/syslog.h b/compat/win32/syslog.h new file mode 100644 index 0000000000..70daa7c08b --- /dev/null +++ b/compat/win32/syslog.h @@ -0,0 +1,20 @@ +#ifndef SYSLOG_H +#define SYSLOG_H + +#define LOG_PID 0x01 + +#define LOG_EMERG 0 +#define LOG_ALERT 1 +#define LOG_CRIT 2 +#define LOG_ERR 3 +#define LOG_WARNING 4 +#define LOG_NOTICE 5 +#define LOG_INFO 6 +#define LOG_DEBUG 7 + +#define LOG_DAEMON (3<<3) + +void openlog(const char *ident, int logopt, int facility); +void syslog(int priority, const char *fmt, ...); + +#endif /* SYSLOG_H */ |