1275970Scy/* 2275970Scy * Copyright 2009-2012 Niels Provos and Nick Mathewson 3275970Scy * 4275970Scy * Redistribution and use in source and binary forms, with or without 5275970Scy * modification, are permitted provided that the following conditions 6275970Scy * are met: 7275970Scy * 1. Redistributions of source code must retain the above copyright 8275970Scy * notice, this list of conditions and the following disclaimer. 9275970Scy * 2. Redistributions in binary form must reproduce the above copyright 10275970Scy * notice, this list of conditions and the following disclaimer in the 11275970Scy * documentation and/or other materials provided with the distribution. 12275970Scy * 3. The name of the author may not be used to endorse or promote products 13275970Scy * derived from this software without specific prior written permission. 14275970Scy * 15275970Scy * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16275970Scy * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17275970Scy * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18275970Scy * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19275970Scy * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20275970Scy * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21275970Scy * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22275970Scy * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23275970Scy * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24275970Scy * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25275970Scy */ 26275970Scy#include "event2/event-config.h" 27275970Scy#include "evconfig-private.h" 28275970Scy 29275970Scy#ifdef _WIN32 30275970Scy#ifndef _WIN32_WINNT 31275970Scy/* Minimum required for InitializeCriticalSectionAndSpinCount */ 32275970Scy#define _WIN32_WINNT 0x0403 33275970Scy#endif 34275970Scy#include <winsock2.h> 35275970Scy#define WIN32_LEAN_AND_MEAN 36275970Scy#include <windows.h> 37275970Scy#undef WIN32_LEAN_AND_MEAN 38275970Scy#include <sys/locking.h> 39275970Scy#endif 40275970Scy 41275970Scystruct event_base; 42275970Scy#include "event2/thread.h" 43275970Scy 44275970Scy#include "mm-internal.h" 45275970Scy#include "evthread-internal.h" 46275970Scy#include "time-internal.h" 47275970Scy 48275970Scy#define SPIN_COUNT 2000 49275970Scy 50275970Scystatic void * 51275970Scyevthread_win32_lock_create(unsigned locktype) 52275970Scy{ 53275970Scy CRITICAL_SECTION *lock = mm_malloc(sizeof(CRITICAL_SECTION)); 54275970Scy if (!lock) 55275970Scy return NULL; 56275970Scy if (InitializeCriticalSectionAndSpinCount(lock, SPIN_COUNT) == 0) { 57275970Scy mm_free(lock); 58275970Scy return NULL; 59275970Scy } 60275970Scy return lock; 61275970Scy} 62275970Scy 63275970Scystatic void 64275970Scyevthread_win32_lock_free(void *lock_, unsigned locktype) 65275970Scy{ 66275970Scy CRITICAL_SECTION *lock = lock_; 67275970Scy DeleteCriticalSection(lock); 68275970Scy mm_free(lock); 69275970Scy} 70275970Scy 71275970Scystatic int 72275970Scyevthread_win32_lock(unsigned mode, void *lock_) 73275970Scy{ 74275970Scy CRITICAL_SECTION *lock = lock_; 75275970Scy if ((mode & EVTHREAD_TRY)) { 76275970Scy return ! TryEnterCriticalSection(lock); 77275970Scy } else { 78275970Scy EnterCriticalSection(lock); 79275970Scy return 0; 80275970Scy } 81275970Scy} 82275970Scy 83275970Scystatic int 84275970Scyevthread_win32_unlock(unsigned mode, void *lock_) 85275970Scy{ 86275970Scy CRITICAL_SECTION *lock = lock_; 87275970Scy LeaveCriticalSection(lock); 88275970Scy return 0; 89275970Scy} 90275970Scy 91275970Scystatic unsigned long 92275970Scyevthread_win32_get_id(void) 93275970Scy{ 94275970Scy return (unsigned long) GetCurrentThreadId(); 95275970Scy} 96275970Scy 97275970Scy#ifdef WIN32_HAVE_CONDITION_VARIABLES 98275970Scystatic void WINAPI (*InitializeConditionVariable_fn)(PCONDITION_VARIABLE) 99275970Scy = NULL; 100275970Scystatic BOOL WINAPI (*SleepConditionVariableCS_fn)( 101275970Scy PCONDITION_VARIABLE, PCRITICAL_SECTION, DWORD) = NULL; 102275970Scystatic void WINAPI (*WakeAllConditionVariable_fn)(PCONDITION_VARIABLE) = NULL; 103275970Scystatic void WINAPI (*WakeConditionVariable_fn)(PCONDITION_VARIABLE) = NULL; 104275970Scy 105275970Scystatic int 106275970Scyevthread_win32_condvar_init(void) 107275970Scy{ 108275970Scy HANDLE lib; 109275970Scy 110275970Scy lib = GetModuleHandle(TEXT("kernel32.dll")); 111275970Scy if (lib == NULL) 112275970Scy return 0; 113275970Scy 114275970Scy#define LOAD(name) \ 115275970Scy name##_fn = GetProcAddress(lib, #name) 116275970Scy LOAD(InitializeConditionVariable); 117275970Scy LOAD(SleepConditionVariableCS); 118275970Scy LOAD(WakeAllConditionVariable); 119275970Scy LOAD(WakeConditionVariable); 120275970Scy 121275970Scy return InitializeConditionVariable_fn && SleepConditionVariableCS_fn && 122275970Scy WakeAllConditionVariable_fn && WakeConditionVariable_fn; 123275970Scy} 124275970Scy 125275970Scy/* XXXX Even if we can build this, we don't necessarily want to: the functions 126275970Scy * in question didn't exist before Vista, so we'd better LoadProc them. */ 127275970Scystatic void * 128275970Scyevthread_win32_condvar_alloc(unsigned condflags) 129275970Scy{ 130275970Scy CONDITION_VARIABLE *cond = mm_malloc(sizeof(CONDITION_VARIABLE)); 131275970Scy if (!cond) 132275970Scy return NULL; 133275970Scy InitializeConditionVariable_fn(cond); 134275970Scy return cond; 135275970Scy} 136275970Scy 137275970Scystatic void 138275970Scyevthread_win32_condvar_free(void *cond_) 139275970Scy{ 140275970Scy CONDITION_VARIABLE *cond = cond_; 141275970Scy /* There doesn't _seem_ to be a cleaup fn here... */ 142275970Scy mm_free(cond); 143275970Scy} 144275970Scy 145275970Scystatic int 146275970Scyevthread_win32_condvar_signal(void *cond, int broadcast) 147275970Scy{ 148275970Scy CONDITION_VARIABLE *cond = cond_; 149275970Scy if (broadcast) 150275970Scy WakeAllConditionVariable_fn(cond); 151275970Scy else 152275970Scy WakeConditionVariable_fn(cond); 153275970Scy return 0; 154275970Scy} 155275970Scy 156275970Scystatic int 157275970Scyevthread_win32_condvar_wait(void *cond_, void *lock_, const struct timeval *tv) 158275970Scy{ 159275970Scy CONDITION_VARIABLE *cond = cond_; 160275970Scy CRITICAL_SECTION *lock = lock_; 161275970Scy DWORD ms, err; 162275970Scy BOOL result; 163275970Scy 164275970Scy if (tv) 165275970Scy ms = evutil_tv_to_msec_(tv); 166275970Scy else 167275970Scy ms = INFINITE; 168275970Scy result = SleepConditionVariableCS_fn(cond, lock, ms); 169275970Scy if (result) { 170275970Scy if (GetLastError() == WAIT_TIMEOUT) 171275970Scy return 1; 172275970Scy else 173275970Scy return -1; 174275970Scy } else { 175275970Scy return 0; 176275970Scy } 177275970Scy} 178275970Scy#endif 179275970Scy 180275970Scystruct evthread_win32_cond { 181275970Scy HANDLE event; 182275970Scy 183275970Scy CRITICAL_SECTION lock; 184275970Scy int n_waiting; 185275970Scy int n_to_wake; 186275970Scy int generation; 187275970Scy}; 188275970Scy 189275970Scystatic void * 190275970Scyevthread_win32_cond_alloc(unsigned flags) 191275970Scy{ 192275970Scy struct evthread_win32_cond *cond; 193275970Scy if (!(cond = mm_malloc(sizeof(struct evthread_win32_cond)))) 194275970Scy return NULL; 195275970Scy if (InitializeCriticalSectionAndSpinCount(&cond->lock, SPIN_COUNT)==0) { 196275970Scy mm_free(cond); 197275970Scy return NULL; 198275970Scy } 199275970Scy if ((cond->event = CreateEvent(NULL,TRUE,FALSE,NULL)) == NULL) { 200275970Scy DeleteCriticalSection(&cond->lock); 201275970Scy mm_free(cond); 202275970Scy return NULL; 203275970Scy } 204275970Scy cond->n_waiting = cond->n_to_wake = cond->generation = 0; 205275970Scy return cond; 206275970Scy} 207275970Scy 208275970Scystatic void 209275970Scyevthread_win32_cond_free(void *cond_) 210275970Scy{ 211275970Scy struct evthread_win32_cond *cond = cond_; 212275970Scy DeleteCriticalSection(&cond->lock); 213275970Scy CloseHandle(cond->event); 214275970Scy mm_free(cond); 215275970Scy} 216275970Scy 217275970Scystatic int 218275970Scyevthread_win32_cond_signal(void *cond_, int broadcast) 219275970Scy{ 220275970Scy struct evthread_win32_cond *cond = cond_; 221275970Scy EnterCriticalSection(&cond->lock); 222275970Scy if (broadcast) 223275970Scy cond->n_to_wake = cond->n_waiting; 224275970Scy else 225275970Scy ++cond->n_to_wake; 226275970Scy cond->generation++; 227275970Scy SetEvent(cond->event); 228275970Scy LeaveCriticalSection(&cond->lock); 229275970Scy return 0; 230275970Scy} 231275970Scy 232275970Scystatic int 233275970Scyevthread_win32_cond_wait(void *cond_, void *lock_, const struct timeval *tv) 234275970Scy{ 235275970Scy struct evthread_win32_cond *cond = cond_; 236275970Scy CRITICAL_SECTION *lock = lock_; 237275970Scy int generation_at_start; 238275970Scy int waiting = 1; 239275970Scy int result = -1; 240275970Scy DWORD ms = INFINITE, ms_orig = INFINITE, startTime, endTime; 241275970Scy if (tv) 242275970Scy ms_orig = ms = evutil_tv_to_msec_(tv); 243275970Scy 244275970Scy EnterCriticalSection(&cond->lock); 245275970Scy ++cond->n_waiting; 246275970Scy generation_at_start = cond->generation; 247275970Scy LeaveCriticalSection(&cond->lock); 248275970Scy 249275970Scy LeaveCriticalSection(lock); 250275970Scy 251275970Scy startTime = GetTickCount(); 252275970Scy do { 253275970Scy DWORD res; 254275970Scy res = WaitForSingleObject(cond->event, ms); 255275970Scy EnterCriticalSection(&cond->lock); 256275970Scy if (cond->n_to_wake && 257275970Scy cond->generation != generation_at_start) { 258275970Scy --cond->n_to_wake; 259275970Scy --cond->n_waiting; 260275970Scy result = 0; 261275970Scy waiting = 0; 262275970Scy goto out; 263275970Scy } else if (res != WAIT_OBJECT_0) { 264275970Scy result = (res==WAIT_TIMEOUT) ? 1 : -1; 265275970Scy --cond->n_waiting; 266275970Scy waiting = 0; 267275970Scy goto out; 268275970Scy } else if (ms != INFINITE) { 269275970Scy endTime = GetTickCount(); 270275970Scy if (startTime + ms_orig <= endTime) { 271275970Scy result = 1; /* Timeout */ 272275970Scy --cond->n_waiting; 273275970Scy waiting = 0; 274275970Scy goto out; 275275970Scy } else { 276275970Scy ms = startTime + ms_orig - endTime; 277275970Scy } 278275970Scy } 279275970Scy /* If we make it here, we are still waiting. */ 280275970Scy if (cond->n_to_wake == 0) { 281275970Scy /* There is nobody else who should wake up; reset 282275970Scy * the event. */ 283275970Scy ResetEvent(cond->event); 284275970Scy } 285275970Scy out: 286275970Scy LeaveCriticalSection(&cond->lock); 287275970Scy } while (waiting); 288275970Scy 289275970Scy EnterCriticalSection(lock); 290275970Scy 291275970Scy EnterCriticalSection(&cond->lock); 292275970Scy if (!cond->n_waiting) 293275970Scy ResetEvent(cond->event); 294275970Scy LeaveCriticalSection(&cond->lock); 295275970Scy 296275970Scy return result; 297275970Scy} 298275970Scy 299275970Scyint 300275970Scyevthread_use_windows_threads(void) 301275970Scy{ 302275970Scy struct evthread_lock_callbacks cbs = { 303275970Scy EVTHREAD_LOCK_API_VERSION, 304275970Scy EVTHREAD_LOCKTYPE_RECURSIVE, 305275970Scy evthread_win32_lock_create, 306275970Scy evthread_win32_lock_free, 307275970Scy evthread_win32_lock, 308275970Scy evthread_win32_unlock 309275970Scy }; 310275970Scy 311275970Scy 312275970Scy struct evthread_condition_callbacks cond_cbs = { 313275970Scy EVTHREAD_CONDITION_API_VERSION, 314275970Scy evthread_win32_cond_alloc, 315275970Scy evthread_win32_cond_free, 316275970Scy evthread_win32_cond_signal, 317275970Scy evthread_win32_cond_wait 318275970Scy }; 319275970Scy#ifdef WIN32_HAVE_CONDITION_VARIABLES 320275970Scy struct evthread_condition_callbacks condvar_cbs = { 321275970Scy EVTHREAD_CONDITION_API_VERSION, 322275970Scy evthread_win32_condvar_alloc, 323275970Scy evthread_win32_condvar_free, 324275970Scy evthread_win32_condvar_signal, 325275970Scy evthread_win32_condvar_wait 326275970Scy }; 327275970Scy#endif 328275970Scy 329275970Scy evthread_set_lock_callbacks(&cbs); 330275970Scy evthread_set_id_callback(evthread_win32_get_id); 331275970Scy#ifdef WIN32_HAVE_CONDITION_VARIABLES 332275970Scy if (evthread_win32_condvar_init()) { 333275970Scy evthread_set_condition_callbacks(&condvar_cbs); 334275970Scy return 0; 335275970Scy } 336275970Scy#endif 337275970Scy evthread_set_condition_callbacks(&cond_cbs); 338275970Scy 339275970Scy return 0; 340275970Scy} 341275970Scy 342