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 41 struct 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 50 static void * 51 evthread_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 63 static void 64 evthread_win32_lock_free(void *lock_, unsigned locktype) 65 { 66 CRITICAL_SECTION *lock = lock_; 67 DeleteCriticalSection(lock); 68 mm_free(lock); 69 } 70 71 static int 72 evthread_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 83 static int 84 evthread_win32_unlock(unsigned mode, void *lock_) 85 { 86 CRITICAL_SECTION *lock = lock_; 87 LeaveCriticalSection(lock); 88 return 0; 89 } 90 91 static unsigned long 92 evthread_win32_get_id(void) 93 { 94 return (unsigned long) GetCurrentThreadId(); 95 } 96 97 #ifdef WIN32_HAVE_CONDITION_VARIABLES 98 static void WINAPI (*InitializeConditionVariable_fn)(PCONDITION_VARIABLE) 99 = NULL; 100 static BOOL WINAPI (*SleepConditionVariableCS_fn)( 101 PCONDITION_VARIABLE, PCRITICAL_SECTION, DWORD) = NULL; 102 static void WINAPI (*WakeAllConditionVariable_fn)(PCONDITION_VARIABLE) = NULL; 103 static void WINAPI (*WakeConditionVariable_fn)(PCONDITION_VARIABLE) = NULL; 104 105 static int 106 evthread_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. */ 127 static void * 128 evthread_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 137 static void 138 evthread_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 145 static int 146 evthread_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 156 static int 157 evthread_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 180 struct 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 189 static void * 190 evthread_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 208 static void 209 evthread_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 217 static int 218 evthread_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 232 static int 233 evthread_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 299 int 300 evthread_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