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