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