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