michael@0: /* michael@0: * Copyright 2009-2012 Niels Provos and Nick Mathewson michael@0: * michael@0: * Redistribution and use in source and binary forms, with or without michael@0: * modification, are permitted provided that the following conditions michael@0: * are met: michael@0: * 1. Redistributions of source code must retain the above copyright michael@0: * notice, this list of conditions and the following disclaimer. michael@0: * 2. Redistributions in binary form must reproduce the above copyright michael@0: * notice, this list of conditions and the following disclaimer in the michael@0: * documentation and/or other materials provided with the distribution. michael@0: * 3. The name of the author may not be used to endorse or promote products michael@0: * derived from this software without specific prior written permission. michael@0: * michael@0: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR michael@0: * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES michael@0: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. michael@0: * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, michael@0: * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT michael@0: * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, michael@0: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY michael@0: * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT michael@0: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF michael@0: * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. michael@0: */ michael@0: #include "event2/event-config.h" michael@0: michael@0: #ifdef WIN32 michael@0: #ifndef _WIN32_WINNT michael@0: /* Minimum required for InitializeCriticalSectionAndSpinCount */ michael@0: #define _WIN32_WINNT 0x0403 michael@0: #endif michael@0: #include michael@0: #define WIN32_LEAN_AND_MEAN michael@0: #include michael@0: #undef WIN32_LEAN_AND_MEAN michael@0: #include michael@0: #endif michael@0: michael@0: struct event_base; michael@0: #include "event2/thread.h" michael@0: michael@0: #include "mm-internal.h" michael@0: #include "evthread-internal.h" michael@0: michael@0: #define SPIN_COUNT 2000 michael@0: michael@0: static void * michael@0: evthread_win32_lock_create(unsigned locktype) michael@0: { michael@0: CRITICAL_SECTION *lock = mm_malloc(sizeof(CRITICAL_SECTION)); michael@0: if (!lock) michael@0: return NULL; michael@0: if (InitializeCriticalSectionAndSpinCount(lock, SPIN_COUNT) == 0) { michael@0: mm_free(lock); michael@0: return NULL; michael@0: } michael@0: return lock; michael@0: } michael@0: michael@0: static void michael@0: evthread_win32_lock_free(void *_lock, unsigned locktype) michael@0: { michael@0: CRITICAL_SECTION *lock = _lock; michael@0: DeleteCriticalSection(lock); michael@0: mm_free(lock); michael@0: } michael@0: michael@0: static int michael@0: evthread_win32_lock(unsigned mode, void *_lock) michael@0: { michael@0: CRITICAL_SECTION *lock = _lock; michael@0: if ((mode & EVTHREAD_TRY)) { michael@0: return ! TryEnterCriticalSection(lock); michael@0: } else { michael@0: EnterCriticalSection(lock); michael@0: return 0; michael@0: } michael@0: } michael@0: michael@0: static int michael@0: evthread_win32_unlock(unsigned mode, void *_lock) michael@0: { michael@0: CRITICAL_SECTION *lock = _lock; michael@0: LeaveCriticalSection(lock); michael@0: return 0; michael@0: } michael@0: michael@0: static unsigned long michael@0: evthread_win32_get_id(void) michael@0: { michael@0: return (unsigned long) GetCurrentThreadId(); michael@0: } michael@0: michael@0: #ifdef WIN32_HAVE_CONDITION_VARIABLES michael@0: static void WINAPI (*InitializeConditionVariable_fn)(PCONDITION_VARIABLE) michael@0: = NULL; michael@0: static BOOL WINAPI (*SleepConditionVariableCS_fn)( michael@0: PCONDITION_VARIABLE, PCRITICAL_SECTION, DWORD) = NULL; michael@0: static void WINAPI (*WakeAllConditionVariable_fn)(PCONDITION_VARIABLE) = NULL; michael@0: static void WINAPI (*WakeConditionVariable_fn)(PCONDITION_VARIABLE) = NULL; michael@0: michael@0: static int michael@0: evthread_win32_condvar_init(void) michael@0: { michael@0: HANDLE lib; michael@0: michael@0: lib = GetModuleHandle(TEXT("kernel32.dll")); michael@0: if (lib == NULL) michael@0: return 0; michael@0: michael@0: #define LOAD(name) \ michael@0: name##_fn = GetProcAddress(lib, #name) michael@0: LOAD(InitializeConditionVariable); michael@0: LOAD(SleepConditionVariable); michael@0: LOAD(WakeAllConditionVariable); michael@0: LOAD(WakeConditionVariable); michael@0: michael@0: return InitializeConditionVariable_fn && SleepConditionVariableCS_fn && michael@0: WakeAllConditionVariable_fn && WakeConditionVariable_fn; michael@0: } michael@0: michael@0: /* XXXX Even if we can build this, we don't necessarily want to: the functions michael@0: * in question didn't exist before Vista, so we'd better LoadProc them. */ michael@0: static void * michael@0: evthread_win32_condvar_alloc(unsigned condflags) michael@0: { michael@0: CONDITION_VARIABLE *cond = mm_malloc(sizeof(CONDITION_VARIABLE)); michael@0: if (!cond) michael@0: return NULL; michael@0: InitializeConditionVariable_fn(cond); michael@0: return cond; michael@0: } michael@0: michael@0: static void michael@0: evthread_win32_condvar_free(void *_cond) michael@0: { michael@0: CONDITION_VARIABLE *cond = _cond; michael@0: /* There doesn't _seem_ to be a cleaup fn here... */ michael@0: mm_free(cond); michael@0: } michael@0: michael@0: static int michael@0: evthread_win32_condvar_signal(void *_cond, int broadcast) michael@0: { michael@0: CONDITION_VARIABLE *cond = _cond; michael@0: if (broadcast) michael@0: WakeAllConditionVariable_fn(cond); michael@0: else michael@0: WakeConditionVariable_fn(cond); michael@0: return 0; michael@0: } michael@0: michael@0: static int michael@0: evthread_win32_condvar_wait(void *_cond, void *_lock, const struct timeval *tv) michael@0: { michael@0: CONDITION_VARIABLE *cond = _cond; michael@0: CRITICAL_SECTION *lock = _lock; michael@0: DWORD ms, err; michael@0: BOOL result; michael@0: michael@0: if (tv) michael@0: ms = evutil_tv_to_msec(tv); michael@0: else michael@0: ms = INFINITE; michael@0: result = SleepConditionVariableCS_fn(cond, lock, ms); michael@0: if (result) { michael@0: if (GetLastError() == WAIT_TIMEOUT) michael@0: return 1; michael@0: else michael@0: return -1; michael@0: } else { michael@0: return 0; michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: struct evthread_win32_cond { michael@0: HANDLE event; michael@0: michael@0: CRITICAL_SECTION lock; michael@0: int n_waiting; michael@0: int n_to_wake; michael@0: int generation; michael@0: }; michael@0: michael@0: static void * michael@0: evthread_win32_cond_alloc(unsigned flags) michael@0: { michael@0: struct evthread_win32_cond *cond; michael@0: if (!(cond = mm_malloc(sizeof(struct evthread_win32_cond)))) michael@0: return NULL; michael@0: if (InitializeCriticalSectionAndSpinCount(&cond->lock, SPIN_COUNT)==0) { michael@0: mm_free(cond); michael@0: return NULL; michael@0: } michael@0: if ((cond->event = CreateEvent(NULL,TRUE,FALSE,NULL)) == NULL) { michael@0: DeleteCriticalSection(&cond->lock); michael@0: mm_free(cond); michael@0: return NULL; michael@0: } michael@0: cond->n_waiting = cond->n_to_wake = cond->generation = 0; michael@0: return cond; michael@0: } michael@0: michael@0: static void michael@0: evthread_win32_cond_free(void *_cond) michael@0: { michael@0: struct evthread_win32_cond *cond = _cond; michael@0: DeleteCriticalSection(&cond->lock); michael@0: CloseHandle(cond->event); michael@0: mm_free(cond); michael@0: } michael@0: michael@0: static int michael@0: evthread_win32_cond_signal(void *_cond, int broadcast) michael@0: { michael@0: struct evthread_win32_cond *cond = _cond; michael@0: EnterCriticalSection(&cond->lock); michael@0: if (broadcast) michael@0: cond->n_to_wake = cond->n_waiting; michael@0: else michael@0: ++cond->n_to_wake; michael@0: cond->generation++; michael@0: SetEvent(cond->event); michael@0: LeaveCriticalSection(&cond->lock); michael@0: return 0; michael@0: } michael@0: michael@0: static int michael@0: evthread_win32_cond_wait(void *_cond, void *_lock, const struct timeval *tv) michael@0: { michael@0: struct evthread_win32_cond *cond = _cond; michael@0: CRITICAL_SECTION *lock = _lock; michael@0: int generation_at_start; michael@0: int waiting = 1; michael@0: int result = -1; michael@0: DWORD ms = INFINITE, ms_orig = INFINITE, startTime, endTime; michael@0: if (tv) michael@0: ms_orig = ms = evutil_tv_to_msec(tv); michael@0: michael@0: EnterCriticalSection(&cond->lock); michael@0: ++cond->n_waiting; michael@0: generation_at_start = cond->generation; michael@0: LeaveCriticalSection(&cond->lock); michael@0: michael@0: LeaveCriticalSection(lock); michael@0: michael@0: startTime = GetTickCount(); michael@0: do { michael@0: DWORD res; michael@0: res = WaitForSingleObject(cond->event, ms); michael@0: EnterCriticalSection(&cond->lock); michael@0: if (cond->n_to_wake && michael@0: cond->generation != generation_at_start) { michael@0: --cond->n_to_wake; michael@0: --cond->n_waiting; michael@0: result = 0; michael@0: waiting = 0; michael@0: goto out; michael@0: } else if (res != WAIT_OBJECT_0) { michael@0: result = (res==WAIT_TIMEOUT) ? 1 : -1; michael@0: --cond->n_waiting; michael@0: waiting = 0; michael@0: goto out; michael@0: } else if (ms != INFINITE) { michael@0: endTime = GetTickCount(); michael@0: if (startTime + ms_orig <= endTime) { michael@0: result = 1; /* Timeout */ michael@0: --cond->n_waiting; michael@0: waiting = 0; michael@0: goto out; michael@0: } else { michael@0: ms = startTime + ms_orig - endTime; michael@0: } michael@0: } michael@0: /* If we make it here, we are still waiting. */ michael@0: if (cond->n_to_wake == 0) { michael@0: /* There is nobody else who should wake up; reset michael@0: * the event. */ michael@0: ResetEvent(cond->event); michael@0: } michael@0: out: michael@0: LeaveCriticalSection(&cond->lock); michael@0: } while (waiting); michael@0: michael@0: EnterCriticalSection(lock); michael@0: michael@0: EnterCriticalSection(&cond->lock); michael@0: if (!cond->n_waiting) michael@0: ResetEvent(cond->event); michael@0: LeaveCriticalSection(&cond->lock); michael@0: michael@0: return result; michael@0: } michael@0: michael@0: int michael@0: evthread_use_windows_threads(void) michael@0: { michael@0: struct evthread_lock_callbacks cbs = { michael@0: EVTHREAD_LOCK_API_VERSION, michael@0: EVTHREAD_LOCKTYPE_RECURSIVE, michael@0: evthread_win32_lock_create, michael@0: evthread_win32_lock_free, michael@0: evthread_win32_lock, michael@0: evthread_win32_unlock michael@0: }; michael@0: michael@0: michael@0: struct evthread_condition_callbacks cond_cbs = { michael@0: EVTHREAD_CONDITION_API_VERSION, michael@0: evthread_win32_cond_alloc, michael@0: evthread_win32_cond_free, michael@0: evthread_win32_cond_signal, michael@0: evthread_win32_cond_wait michael@0: }; michael@0: #ifdef WIN32_HAVE_CONDITION_VARIABLES michael@0: struct evthread_condition_callbacks condvar_cbs = { michael@0: EVTHREAD_CONDITION_API_VERSION, michael@0: evthread_win32_condvar_alloc, michael@0: evthread_win32_condvar_free, michael@0: evthread_win32_condvar_signal, michael@0: evthread_win32_condvar_wait michael@0: }; michael@0: #endif michael@0: michael@0: evthread_set_lock_callbacks(&cbs); michael@0: evthread_set_id_callback(evthread_win32_get_id); michael@0: #ifdef WIN32_HAVE_CONDITION_VARIABLES michael@0: if (evthread_win32_condvar_init()) { michael@0: evthread_set_condition_callbacks(&condvar_cbs); michael@0: return 0; michael@0: } michael@0: #endif michael@0: evthread_set_condition_callbacks(&cond_cbs); michael@0: michael@0: return 0; michael@0: } michael@0: