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#include "evconfig-private.h" 28 29#ifdef _WIN32 30#ifndef _WIN32_WINNT 31/* Minimum required for InitializeCriticalSectionAndSpinCount */ 32#define _WIN32_WINNT 0x0403 33#endif 34#include <winsock2.h> 35#define WIN32_LEAN_AND_MEAN 36#include <windows.h> 37#undef WIN32_LEAN_AND_MEAN 38#include <sys/locking.h> 39#endif 40 41struct event_base; 42#include "event2/thread.h" 43 44#include "mm-internal.h" 45#include "evthread-internal.h" 46#include "time-internal.h" 47 48#define SPIN_COUNT 2000 49 50static void * 51evthread_win32_lock_create(unsigned locktype) 52{ 53 CRITICAL_SECTION *lock = mm_malloc(sizeof(CRITICAL_SECTION)); 54 if (!lock) 55 return NULL; 56 if (InitializeCriticalSectionAndSpinCount(lock, SPIN_COUNT) == 0) { 57 mm_free(lock); 58 return NULL; 59 } 60 return lock; 61} 62 63static void 64evthread_win32_lock_free(void *lock_, unsigned locktype) 65{ 66 CRITICAL_SECTION *lock = lock_; 67 DeleteCriticalSection(lock); 68 mm_free(lock); 69} 70 71static int 72evthread_win32_lock(unsigned mode, void *lock_) 73{ 74 CRITICAL_SECTION *lock = lock_; 75 if ((mode & EVTHREAD_TRY)) { 76 return ! TryEnterCriticalSection(lock); 77 } else { 78 EnterCriticalSection(lock); 79 return 0; 80 } 81} 82 83static int 84evthread_win32_unlock(unsigned mode, void *lock_) 85{ 86 CRITICAL_SECTION *lock = lock_; 87 LeaveCriticalSection(lock); 88 return 0; 89} 90 91static unsigned long 92evthread_win32_get_id(void) 93{ 94 return (unsigned long) GetCurrentThreadId(); 95} 96 97#ifdef WIN32_HAVE_CONDITION_VARIABLES 98static void WINAPI (*InitializeConditionVariable_fn)(PCONDITION_VARIABLE) 99 = NULL; 100static BOOL WINAPI (*SleepConditionVariableCS_fn)( 101 PCONDITION_VARIABLE, PCRITICAL_SECTION, DWORD) = NULL; 102static void WINAPI (*WakeAllConditionVariable_fn)(PCONDITION_VARIABLE) = NULL; 103static void WINAPI (*WakeConditionVariable_fn)(PCONDITION_VARIABLE) = NULL; 104 105static int 106evthread_win32_condvar_init(void) 107{ 108 HANDLE lib; 109 110 lib = GetModuleHandle(TEXT("kernel32.dll")); 111 if (lib == NULL) 112 return 0; 113 114#define LOAD(name) \ 115 name##_fn = GetProcAddress(lib, #name) 116 LOAD(InitializeConditionVariable); 117 LOAD(SleepConditionVariableCS); 118 LOAD(WakeAllConditionVariable); 119 LOAD(WakeConditionVariable); 120 121 return InitializeConditionVariable_fn && SleepConditionVariableCS_fn && 122 WakeAllConditionVariable_fn && WakeConditionVariable_fn; 123} 124 125/* XXXX Even if we can build this, we don't necessarily want to: the functions 126 * in question didn't exist before Vista, so we'd better LoadProc them. */ 127static void * 128evthread_win32_condvar_alloc(unsigned condflags) 129{ 130 CONDITION_VARIABLE *cond = mm_malloc(sizeof(CONDITION_VARIABLE)); 131 if (!cond) 132 return NULL; 133 InitializeConditionVariable_fn(cond); 134 return cond; 135} 136 137static void 138evthread_win32_condvar_free(void *cond_) 139{ 140 CONDITION_VARIABLE *cond = cond_; 141 /* There doesn't _seem_ to be a cleaup fn here... */ 142 mm_free(cond); 143} 144 145static int 146evthread_win32_condvar_signal(void *cond, int broadcast) 147{ 148 CONDITION_VARIABLE *cond = cond_; 149 if (broadcast) 150 WakeAllConditionVariable_fn(cond); 151 else 152 WakeConditionVariable_fn(cond); 153 return 0; 154} 155 156static int 157evthread_win32_condvar_wait(void *cond_, void *lock_, const struct timeval *tv) 158{ 159 CONDITION_VARIABLE *cond = cond_; 160 CRITICAL_SECTION *lock = lock_; 161 DWORD ms, err; 162 BOOL result; 163 164 if (tv) 165 ms = evutil_tv_to_msec_(tv); 166 else 167 ms = INFINITE; 168 result = SleepConditionVariableCS_fn(cond, lock, ms); 169 if (result) { 170 if (GetLastError() == WAIT_TIMEOUT) 171 return 1; 172 else 173 return -1; 174 } else { 175 return 0; 176 } 177} 178#endif 179 180struct evthread_win32_cond { 181 HANDLE event; 182 183 CRITICAL_SECTION lock; 184 int n_waiting; 185 int n_to_wake; 186 int generation; 187}; 188 189static void * 190evthread_win32_cond_alloc(unsigned flags) 191{ 192 struct evthread_win32_cond *cond; 193 if (!(cond = mm_malloc(sizeof(struct evthread_win32_cond)))) 194 return NULL; 195 if (InitializeCriticalSectionAndSpinCount(&cond->lock, SPIN_COUNT)==0) { 196 mm_free(cond); 197 return NULL; 198 } 199 if ((cond->event = CreateEvent(NULL,TRUE,FALSE,NULL)) == NULL) { 200 DeleteCriticalSection(&cond->lock); 201 mm_free(cond); 202 return NULL; 203 } 204 cond->n_waiting = cond->n_to_wake = cond->generation = 0; 205 return cond; 206} 207 208static void 209evthread_win32_cond_free(void *cond_) 210{ 211 struct evthread_win32_cond *cond = cond_; 212 DeleteCriticalSection(&cond->lock); 213 CloseHandle(cond->event); 214 mm_free(cond); 215} 216 217static int 218evthread_win32_cond_signal(void *cond_, int broadcast) 219{ 220 struct evthread_win32_cond *cond = cond_; 221 EnterCriticalSection(&cond->lock); 222 if (broadcast) 223 cond->n_to_wake = cond->n_waiting; 224 else 225 ++cond->n_to_wake; 226 cond->generation++; 227 SetEvent(cond->event); 228 LeaveCriticalSection(&cond->lock); 229 return 0; 230} 231 232static int 233evthread_win32_cond_wait(void *cond_, void *lock_, const struct timeval *tv) 234{ 235 struct evthread_win32_cond *cond = cond_; 236 CRITICAL_SECTION *lock = lock_; 237 int generation_at_start; 238 int waiting = 1; 239 int result = -1; 240 DWORD ms = INFINITE, ms_orig = INFINITE, startTime, endTime; 241 if (tv) 242 ms_orig = ms = evutil_tv_to_msec_(tv); 243 244 EnterCriticalSection(&cond->lock); 245 ++cond->n_waiting; 246 generation_at_start = cond->generation; 247 LeaveCriticalSection(&cond->lock); 248 249 LeaveCriticalSection(lock); 250 251 startTime = GetTickCount(); 252 do { 253 DWORD res; 254 res = WaitForSingleObject(cond->event, ms); 255 EnterCriticalSection(&cond->lock); 256 if (cond->n_to_wake && 257 cond->generation != generation_at_start) { 258 --cond->n_to_wake; 259 --cond->n_waiting; 260 result = 0; 261 waiting = 0; 262 goto out; 263 } else if (res != WAIT_OBJECT_0) { 264 result = (res==WAIT_TIMEOUT) ? 1 : -1; 265 --cond->n_waiting; 266 waiting = 0; 267 goto out; 268 } else if (ms != INFINITE) { 269 endTime = GetTickCount(); 270 if (startTime + ms_orig <= endTime) { 271 result = 1; /* Timeout */ 272 --cond->n_waiting; 273 waiting = 0; 274 goto out; 275 } else { 276 ms = startTime + ms_orig - endTime; 277 } 278 } 279 /* If we make it here, we are still waiting. */ 280 if (cond->n_to_wake == 0) { 281 /* There is nobody else who should wake up; reset 282 * the event. */ 283 ResetEvent(cond->event); 284 } 285 out: 286 LeaveCriticalSection(&cond->lock); 287 } while (waiting); 288 289 EnterCriticalSection(lock); 290 291 EnterCriticalSection(&cond->lock); 292 if (!cond->n_waiting) 293 ResetEvent(cond->event); 294 LeaveCriticalSection(&cond->lock); 295 296 return result; 297} 298 299int 300evthread_use_windows_threads(void) 301{ 302 struct evthread_lock_callbacks cbs = { 303 EVTHREAD_LOCK_API_VERSION, 304 EVTHREAD_LOCKTYPE_RECURSIVE, 305 evthread_win32_lock_create, 306 evthread_win32_lock_free, 307 evthread_win32_lock, 308 evthread_win32_unlock 309 }; 310 311 312 struct evthread_condition_callbacks cond_cbs = { 313 EVTHREAD_CONDITION_API_VERSION, 314 evthread_win32_cond_alloc, 315 evthread_win32_cond_free, 316 evthread_win32_cond_signal, 317 evthread_win32_cond_wait 318 }; 319#ifdef WIN32_HAVE_CONDITION_VARIABLES 320 struct evthread_condition_callbacks condvar_cbs = { 321 EVTHREAD_CONDITION_API_VERSION, 322 evthread_win32_condvar_alloc, 323 evthread_win32_condvar_free, 324 evthread_win32_condvar_signal, 325 evthread_win32_condvar_wait 326 }; 327#endif 328 329 evthread_set_lock_callbacks(&cbs); 330 evthread_set_id_callback(evthread_win32_get_id); 331#ifdef WIN32_HAVE_CONDITION_VARIABLES 332 if (evthread_win32_condvar_init()) { 333 evthread_set_condition_callbacks(&condvar_cbs); 334 return 0; 335 } 336#endif 337 evthread_set_condition_callbacks(&cond_cbs); 338 339 return 0; 340} 341 342