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 *
evthread_win32_lock_create(unsigned locktype)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
evthread_win32_lock_free(void * lock_,unsigned locktype)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
evthread_win32_lock(unsigned mode,void * lock_)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
evthread_win32_unlock(unsigned mode,void * lock_)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
evthread_win32_get_id(void)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
evthread_win32_condvar_init(void)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 *
evthread_win32_condvar_alloc(unsigned condflags)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
evthread_win32_condvar_free(void * cond_)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
evthread_win32_condvar_signal(void * cond,int broadcast)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
evthread_win32_condvar_wait(void * cond_,void * lock_,const struct timeval * tv)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 *
evthread_win32_cond_alloc(unsigned flags)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
evthread_win32_cond_free(void * cond_)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
evthread_win32_cond_signal(void * cond_,int broadcast)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
evthread_win32_cond_wait(void * cond_,void * lock_,const struct timeval * tv)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
evthread_use_windows_threads(void)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