1*2b15cb3dSCy Schubert /* 2*2b15cb3dSCy Schubert * Copyright 2009-2012 Niels Provos and Nick Mathewson 3*2b15cb3dSCy Schubert * 4*2b15cb3dSCy Schubert * Redistribution and use in source and binary forms, with or without 5*2b15cb3dSCy Schubert * modification, are permitted provided that the following conditions 6*2b15cb3dSCy Schubert * are met: 7*2b15cb3dSCy Schubert * 1. Redistributions of source code must retain the above copyright 8*2b15cb3dSCy Schubert * notice, this list of conditions and the following disclaimer. 9*2b15cb3dSCy Schubert * 2. Redistributions in binary form must reproduce the above copyright 10*2b15cb3dSCy Schubert * notice, this list of conditions and the following disclaimer in the 11*2b15cb3dSCy Schubert * documentation and/or other materials provided with the distribution. 12*2b15cb3dSCy Schubert * 3. The name of the author may not be used to endorse or promote products 13*2b15cb3dSCy Schubert * derived from this software without specific prior written permission. 14*2b15cb3dSCy Schubert * 15*2b15cb3dSCy Schubert * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16*2b15cb3dSCy Schubert * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17*2b15cb3dSCy Schubert * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18*2b15cb3dSCy Schubert * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19*2b15cb3dSCy Schubert * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20*2b15cb3dSCy Schubert * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21*2b15cb3dSCy Schubert * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22*2b15cb3dSCy Schubert * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23*2b15cb3dSCy Schubert * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24*2b15cb3dSCy Schubert * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25*2b15cb3dSCy Schubert */ 26*2b15cb3dSCy Schubert #include "event2/event-config.h" 27*2b15cb3dSCy Schubert #include "evconfig-private.h" 28*2b15cb3dSCy Schubert 29*2b15cb3dSCy Schubert #ifdef _WIN32 30*2b15cb3dSCy Schubert #ifndef _WIN32_WINNT 31*2b15cb3dSCy Schubert /* Minimum required for InitializeCriticalSectionAndSpinCount */ 32*2b15cb3dSCy Schubert #define _WIN32_WINNT 0x0403 33*2b15cb3dSCy Schubert #endif 34*2b15cb3dSCy Schubert #include <winsock2.h> 35*2b15cb3dSCy Schubert #define WIN32_LEAN_AND_MEAN 36*2b15cb3dSCy Schubert #include <windows.h> 37*2b15cb3dSCy Schubert #undef WIN32_LEAN_AND_MEAN 38*2b15cb3dSCy Schubert #include <sys/locking.h> 39*2b15cb3dSCy Schubert #endif 40*2b15cb3dSCy Schubert 41*2b15cb3dSCy Schubert struct event_base; 42*2b15cb3dSCy Schubert #include "event2/thread.h" 43*2b15cb3dSCy Schubert 44*2b15cb3dSCy Schubert #include "mm-internal.h" 45*2b15cb3dSCy Schubert #include "evthread-internal.h" 46*2b15cb3dSCy Schubert #include "time-internal.h" 47*2b15cb3dSCy Schubert 48*2b15cb3dSCy Schubert #define SPIN_COUNT 2000 49*2b15cb3dSCy Schubert 50*2b15cb3dSCy Schubert static void * 51*2b15cb3dSCy Schubert evthread_win32_lock_create(unsigned locktype) 52*2b15cb3dSCy Schubert { 53*2b15cb3dSCy Schubert CRITICAL_SECTION *lock = mm_malloc(sizeof(CRITICAL_SECTION)); 54*2b15cb3dSCy Schubert if (!lock) 55*2b15cb3dSCy Schubert return NULL; 56*2b15cb3dSCy Schubert if (InitializeCriticalSectionAndSpinCount(lock, SPIN_COUNT) == 0) { 57*2b15cb3dSCy Schubert mm_free(lock); 58*2b15cb3dSCy Schubert return NULL; 59*2b15cb3dSCy Schubert } 60*2b15cb3dSCy Schubert return lock; 61*2b15cb3dSCy Schubert } 62*2b15cb3dSCy Schubert 63*2b15cb3dSCy Schubert static void 64*2b15cb3dSCy Schubert evthread_win32_lock_free(void *lock_, unsigned locktype) 65*2b15cb3dSCy Schubert { 66*2b15cb3dSCy Schubert CRITICAL_SECTION *lock = lock_; 67*2b15cb3dSCy Schubert DeleteCriticalSection(lock); 68*2b15cb3dSCy Schubert mm_free(lock); 69*2b15cb3dSCy Schubert } 70*2b15cb3dSCy Schubert 71*2b15cb3dSCy Schubert static int 72*2b15cb3dSCy Schubert evthread_win32_lock(unsigned mode, void *lock_) 73*2b15cb3dSCy Schubert { 74*2b15cb3dSCy Schubert CRITICAL_SECTION *lock = lock_; 75*2b15cb3dSCy Schubert if ((mode & EVTHREAD_TRY)) { 76*2b15cb3dSCy Schubert return ! TryEnterCriticalSection(lock); 77*2b15cb3dSCy Schubert } else { 78*2b15cb3dSCy Schubert EnterCriticalSection(lock); 79*2b15cb3dSCy Schubert return 0; 80*2b15cb3dSCy Schubert } 81*2b15cb3dSCy Schubert } 82*2b15cb3dSCy Schubert 83*2b15cb3dSCy Schubert static int 84*2b15cb3dSCy Schubert evthread_win32_unlock(unsigned mode, void *lock_) 85*2b15cb3dSCy Schubert { 86*2b15cb3dSCy Schubert CRITICAL_SECTION *lock = lock_; 87*2b15cb3dSCy Schubert LeaveCriticalSection(lock); 88*2b15cb3dSCy Schubert return 0; 89*2b15cb3dSCy Schubert } 90*2b15cb3dSCy Schubert 91*2b15cb3dSCy Schubert static unsigned long 92*2b15cb3dSCy Schubert evthread_win32_get_id(void) 93*2b15cb3dSCy Schubert { 94*2b15cb3dSCy Schubert return (unsigned long) GetCurrentThreadId(); 95*2b15cb3dSCy Schubert } 96*2b15cb3dSCy Schubert 97*2b15cb3dSCy Schubert #ifdef WIN32_HAVE_CONDITION_VARIABLES 98*2b15cb3dSCy Schubert static void WINAPI (*InitializeConditionVariable_fn)(PCONDITION_VARIABLE) 99*2b15cb3dSCy Schubert = NULL; 100*2b15cb3dSCy Schubert static BOOL WINAPI (*SleepConditionVariableCS_fn)( 101*2b15cb3dSCy Schubert PCONDITION_VARIABLE, PCRITICAL_SECTION, DWORD) = NULL; 102*2b15cb3dSCy Schubert static void WINAPI (*WakeAllConditionVariable_fn)(PCONDITION_VARIABLE) = NULL; 103*2b15cb3dSCy Schubert static void WINAPI (*WakeConditionVariable_fn)(PCONDITION_VARIABLE) = NULL; 104*2b15cb3dSCy Schubert 105*2b15cb3dSCy Schubert static int 106*2b15cb3dSCy Schubert evthread_win32_condvar_init(void) 107*2b15cb3dSCy Schubert { 108*2b15cb3dSCy Schubert HANDLE lib; 109*2b15cb3dSCy Schubert 110*2b15cb3dSCy Schubert lib = GetModuleHandle(TEXT("kernel32.dll")); 111*2b15cb3dSCy Schubert if (lib == NULL) 112*2b15cb3dSCy Schubert return 0; 113*2b15cb3dSCy Schubert 114*2b15cb3dSCy Schubert #define LOAD(name) \ 115*2b15cb3dSCy Schubert name##_fn = GetProcAddress(lib, #name) 116*2b15cb3dSCy Schubert LOAD(InitializeConditionVariable); 117*2b15cb3dSCy Schubert LOAD(SleepConditionVariableCS); 118*2b15cb3dSCy Schubert LOAD(WakeAllConditionVariable); 119*2b15cb3dSCy Schubert LOAD(WakeConditionVariable); 120*2b15cb3dSCy Schubert 121*2b15cb3dSCy Schubert return InitializeConditionVariable_fn && SleepConditionVariableCS_fn && 122*2b15cb3dSCy Schubert WakeAllConditionVariable_fn && WakeConditionVariable_fn; 123*2b15cb3dSCy Schubert } 124*2b15cb3dSCy Schubert 125*2b15cb3dSCy Schubert /* XXXX Even if we can build this, we don't necessarily want to: the functions 126*2b15cb3dSCy Schubert * in question didn't exist before Vista, so we'd better LoadProc them. */ 127*2b15cb3dSCy Schubert static void * 128*2b15cb3dSCy Schubert evthread_win32_condvar_alloc(unsigned condflags) 129*2b15cb3dSCy Schubert { 130*2b15cb3dSCy Schubert CONDITION_VARIABLE *cond = mm_malloc(sizeof(CONDITION_VARIABLE)); 131*2b15cb3dSCy Schubert if (!cond) 132*2b15cb3dSCy Schubert return NULL; 133*2b15cb3dSCy Schubert InitializeConditionVariable_fn(cond); 134*2b15cb3dSCy Schubert return cond; 135*2b15cb3dSCy Schubert } 136*2b15cb3dSCy Schubert 137*2b15cb3dSCy Schubert static void 138*2b15cb3dSCy Schubert evthread_win32_condvar_free(void *cond_) 139*2b15cb3dSCy Schubert { 140*2b15cb3dSCy Schubert CONDITION_VARIABLE *cond = cond_; 141*2b15cb3dSCy Schubert /* There doesn't _seem_ to be a cleaup fn here... */ 142*2b15cb3dSCy Schubert mm_free(cond); 143*2b15cb3dSCy Schubert } 144*2b15cb3dSCy Schubert 145*2b15cb3dSCy Schubert static int 146*2b15cb3dSCy Schubert evthread_win32_condvar_signal(void *cond, int broadcast) 147*2b15cb3dSCy Schubert { 148*2b15cb3dSCy Schubert CONDITION_VARIABLE *cond = cond_; 149*2b15cb3dSCy Schubert if (broadcast) 150*2b15cb3dSCy Schubert WakeAllConditionVariable_fn(cond); 151*2b15cb3dSCy Schubert else 152*2b15cb3dSCy Schubert WakeConditionVariable_fn(cond); 153*2b15cb3dSCy Schubert return 0; 154*2b15cb3dSCy Schubert } 155*2b15cb3dSCy Schubert 156*2b15cb3dSCy Schubert static int 157*2b15cb3dSCy Schubert evthread_win32_condvar_wait(void *cond_, void *lock_, const struct timeval *tv) 158*2b15cb3dSCy Schubert { 159*2b15cb3dSCy Schubert CONDITION_VARIABLE *cond = cond_; 160*2b15cb3dSCy Schubert CRITICAL_SECTION *lock = lock_; 161*2b15cb3dSCy Schubert DWORD ms, err; 162*2b15cb3dSCy Schubert BOOL result; 163*2b15cb3dSCy Schubert 164*2b15cb3dSCy Schubert if (tv) 165*2b15cb3dSCy Schubert ms = evutil_tv_to_msec_(tv); 166*2b15cb3dSCy Schubert else 167*2b15cb3dSCy Schubert ms = INFINITE; 168*2b15cb3dSCy Schubert result = SleepConditionVariableCS_fn(cond, lock, ms); 169*2b15cb3dSCy Schubert if (result) { 170*2b15cb3dSCy Schubert if (GetLastError() == WAIT_TIMEOUT) 171*2b15cb3dSCy Schubert return 1; 172*2b15cb3dSCy Schubert else 173*2b15cb3dSCy Schubert return -1; 174*2b15cb3dSCy Schubert } else { 175*2b15cb3dSCy Schubert return 0; 176*2b15cb3dSCy Schubert } 177*2b15cb3dSCy Schubert } 178*2b15cb3dSCy Schubert #endif 179*2b15cb3dSCy Schubert 180*2b15cb3dSCy Schubert struct evthread_win32_cond { 181*2b15cb3dSCy Schubert HANDLE event; 182*2b15cb3dSCy Schubert 183*2b15cb3dSCy Schubert CRITICAL_SECTION lock; 184*2b15cb3dSCy Schubert int n_waiting; 185*2b15cb3dSCy Schubert int n_to_wake; 186*2b15cb3dSCy Schubert int generation; 187*2b15cb3dSCy Schubert }; 188*2b15cb3dSCy Schubert 189*2b15cb3dSCy Schubert static void * 190*2b15cb3dSCy Schubert evthread_win32_cond_alloc(unsigned flags) 191*2b15cb3dSCy Schubert { 192*2b15cb3dSCy Schubert struct evthread_win32_cond *cond; 193*2b15cb3dSCy Schubert if (!(cond = mm_malloc(sizeof(struct evthread_win32_cond)))) 194*2b15cb3dSCy Schubert return NULL; 195*2b15cb3dSCy Schubert if (InitializeCriticalSectionAndSpinCount(&cond->lock, SPIN_COUNT)==0) { 196*2b15cb3dSCy Schubert mm_free(cond); 197*2b15cb3dSCy Schubert return NULL; 198*2b15cb3dSCy Schubert } 199*2b15cb3dSCy Schubert if ((cond->event = CreateEvent(NULL,TRUE,FALSE,NULL)) == NULL) { 200*2b15cb3dSCy Schubert DeleteCriticalSection(&cond->lock); 201*2b15cb3dSCy Schubert mm_free(cond); 202*2b15cb3dSCy Schubert return NULL; 203*2b15cb3dSCy Schubert } 204*2b15cb3dSCy Schubert cond->n_waiting = cond->n_to_wake = cond->generation = 0; 205*2b15cb3dSCy Schubert return cond; 206*2b15cb3dSCy Schubert } 207*2b15cb3dSCy Schubert 208*2b15cb3dSCy Schubert static void 209*2b15cb3dSCy Schubert evthread_win32_cond_free(void *cond_) 210*2b15cb3dSCy Schubert { 211*2b15cb3dSCy Schubert struct evthread_win32_cond *cond = cond_; 212*2b15cb3dSCy Schubert DeleteCriticalSection(&cond->lock); 213*2b15cb3dSCy Schubert CloseHandle(cond->event); 214*2b15cb3dSCy Schubert mm_free(cond); 215*2b15cb3dSCy Schubert } 216*2b15cb3dSCy Schubert 217*2b15cb3dSCy Schubert static int 218*2b15cb3dSCy Schubert evthread_win32_cond_signal(void *cond_, int broadcast) 219*2b15cb3dSCy Schubert { 220*2b15cb3dSCy Schubert struct evthread_win32_cond *cond = cond_; 221*2b15cb3dSCy Schubert EnterCriticalSection(&cond->lock); 222*2b15cb3dSCy Schubert if (broadcast) 223*2b15cb3dSCy Schubert cond->n_to_wake = cond->n_waiting; 224*2b15cb3dSCy Schubert else 225*2b15cb3dSCy Schubert ++cond->n_to_wake; 226*2b15cb3dSCy Schubert cond->generation++; 227*2b15cb3dSCy Schubert SetEvent(cond->event); 228*2b15cb3dSCy Schubert LeaveCriticalSection(&cond->lock); 229*2b15cb3dSCy Schubert return 0; 230*2b15cb3dSCy Schubert } 231*2b15cb3dSCy Schubert 232*2b15cb3dSCy Schubert static int 233*2b15cb3dSCy Schubert evthread_win32_cond_wait(void *cond_, void *lock_, const struct timeval *tv) 234*2b15cb3dSCy Schubert { 235*2b15cb3dSCy Schubert struct evthread_win32_cond *cond = cond_; 236*2b15cb3dSCy Schubert CRITICAL_SECTION *lock = lock_; 237*2b15cb3dSCy Schubert int generation_at_start; 238*2b15cb3dSCy Schubert int waiting = 1; 239*2b15cb3dSCy Schubert int result = -1; 240*2b15cb3dSCy Schubert DWORD ms = INFINITE, ms_orig = INFINITE, startTime, endTime; 241*2b15cb3dSCy Schubert if (tv) 242*2b15cb3dSCy Schubert ms_orig = ms = evutil_tv_to_msec_(tv); 243*2b15cb3dSCy Schubert 244*2b15cb3dSCy Schubert EnterCriticalSection(&cond->lock); 245*2b15cb3dSCy Schubert ++cond->n_waiting; 246*2b15cb3dSCy Schubert generation_at_start = cond->generation; 247*2b15cb3dSCy Schubert LeaveCriticalSection(&cond->lock); 248*2b15cb3dSCy Schubert 249*2b15cb3dSCy Schubert LeaveCriticalSection(lock); 250*2b15cb3dSCy Schubert 251*2b15cb3dSCy Schubert startTime = GetTickCount(); 252*2b15cb3dSCy Schubert do { 253*2b15cb3dSCy Schubert DWORD res; 254*2b15cb3dSCy Schubert res = WaitForSingleObject(cond->event, ms); 255*2b15cb3dSCy Schubert EnterCriticalSection(&cond->lock); 256*2b15cb3dSCy Schubert if (cond->n_to_wake && 257*2b15cb3dSCy Schubert cond->generation != generation_at_start) { 258*2b15cb3dSCy Schubert --cond->n_to_wake; 259*2b15cb3dSCy Schubert --cond->n_waiting; 260*2b15cb3dSCy Schubert result = 0; 261*2b15cb3dSCy Schubert waiting = 0; 262*2b15cb3dSCy Schubert goto out; 263*2b15cb3dSCy Schubert } else if (res != WAIT_OBJECT_0) { 264*2b15cb3dSCy Schubert result = (res==WAIT_TIMEOUT) ? 1 : -1; 265*2b15cb3dSCy Schubert --cond->n_waiting; 266*2b15cb3dSCy Schubert waiting = 0; 267*2b15cb3dSCy Schubert goto out; 268*2b15cb3dSCy Schubert } else if (ms != INFINITE) { 269*2b15cb3dSCy Schubert endTime = GetTickCount(); 270*2b15cb3dSCy Schubert if (startTime + ms_orig <= endTime) { 271*2b15cb3dSCy Schubert result = 1; /* Timeout */ 272*2b15cb3dSCy Schubert --cond->n_waiting; 273*2b15cb3dSCy Schubert waiting = 0; 274*2b15cb3dSCy Schubert goto out; 275*2b15cb3dSCy Schubert } else { 276*2b15cb3dSCy Schubert ms = startTime + ms_orig - endTime; 277*2b15cb3dSCy Schubert } 278*2b15cb3dSCy Schubert } 279*2b15cb3dSCy Schubert /* If we make it here, we are still waiting. */ 280*2b15cb3dSCy Schubert if (cond->n_to_wake == 0) { 281*2b15cb3dSCy Schubert /* There is nobody else who should wake up; reset 282*2b15cb3dSCy Schubert * the event. */ 283*2b15cb3dSCy Schubert ResetEvent(cond->event); 284*2b15cb3dSCy Schubert } 285*2b15cb3dSCy Schubert out: 286*2b15cb3dSCy Schubert LeaveCriticalSection(&cond->lock); 287*2b15cb3dSCy Schubert } while (waiting); 288*2b15cb3dSCy Schubert 289*2b15cb3dSCy Schubert EnterCriticalSection(lock); 290*2b15cb3dSCy Schubert 291*2b15cb3dSCy Schubert EnterCriticalSection(&cond->lock); 292*2b15cb3dSCy Schubert if (!cond->n_waiting) 293*2b15cb3dSCy Schubert ResetEvent(cond->event); 294*2b15cb3dSCy Schubert LeaveCriticalSection(&cond->lock); 295*2b15cb3dSCy Schubert 296*2b15cb3dSCy Schubert return result; 297*2b15cb3dSCy Schubert } 298*2b15cb3dSCy Schubert 299*2b15cb3dSCy Schubert int 300*2b15cb3dSCy Schubert evthread_use_windows_threads(void) 301*2b15cb3dSCy Schubert { 302*2b15cb3dSCy Schubert struct evthread_lock_callbacks cbs = { 303*2b15cb3dSCy Schubert EVTHREAD_LOCK_API_VERSION, 304*2b15cb3dSCy Schubert EVTHREAD_LOCKTYPE_RECURSIVE, 305*2b15cb3dSCy Schubert evthread_win32_lock_create, 306*2b15cb3dSCy Schubert evthread_win32_lock_free, 307*2b15cb3dSCy Schubert evthread_win32_lock, 308*2b15cb3dSCy Schubert evthread_win32_unlock 309*2b15cb3dSCy Schubert }; 310*2b15cb3dSCy Schubert 311*2b15cb3dSCy Schubert 312*2b15cb3dSCy Schubert struct evthread_condition_callbacks cond_cbs = { 313*2b15cb3dSCy Schubert EVTHREAD_CONDITION_API_VERSION, 314*2b15cb3dSCy Schubert evthread_win32_cond_alloc, 315*2b15cb3dSCy Schubert evthread_win32_cond_free, 316*2b15cb3dSCy Schubert evthread_win32_cond_signal, 317*2b15cb3dSCy Schubert evthread_win32_cond_wait 318*2b15cb3dSCy Schubert }; 319*2b15cb3dSCy Schubert #ifdef WIN32_HAVE_CONDITION_VARIABLES 320*2b15cb3dSCy Schubert struct evthread_condition_callbacks condvar_cbs = { 321*2b15cb3dSCy Schubert EVTHREAD_CONDITION_API_VERSION, 322*2b15cb3dSCy Schubert evthread_win32_condvar_alloc, 323*2b15cb3dSCy Schubert evthread_win32_condvar_free, 324*2b15cb3dSCy Schubert evthread_win32_condvar_signal, 325*2b15cb3dSCy Schubert evthread_win32_condvar_wait 326*2b15cb3dSCy Schubert }; 327*2b15cb3dSCy Schubert #endif 328*2b15cb3dSCy Schubert 329*2b15cb3dSCy Schubert evthread_set_lock_callbacks(&cbs); 330*2b15cb3dSCy Schubert evthread_set_id_callback(evthread_win32_get_id); 331*2b15cb3dSCy Schubert #ifdef WIN32_HAVE_CONDITION_VARIABLES 332*2b15cb3dSCy Schubert if (evthread_win32_condvar_init()) { 333*2b15cb3dSCy Schubert evthread_set_condition_callbacks(&condvar_cbs); 334*2b15cb3dSCy Schubert return 0; 335*2b15cb3dSCy Schubert } 336*2b15cb3dSCy Schubert #endif 337*2b15cb3dSCy Schubert evthread_set_condition_callbacks(&cond_cbs); 338*2b15cb3dSCy Schubert 339*2b15cb3dSCy Schubert return 0; 340*2b15cb3dSCy Schubert } 341*2b15cb3dSCy Schubert 342