1 /* $NetBSD: evthread_win32.c,v 1.5 2020/05/25 20:47:33 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 *
evthread_win32_lock_create(unsigned locktype)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
evthread_win32_lock_free(void * lock_,unsigned locktype)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
evthread_win32_lock(unsigned mode,void * lock_)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
evthread_win32_unlock(unsigned mode,void * lock_)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
evthread_win32_get_id(void)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
evthread_win32_condvar_init(void)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 *
evthread_win32_condvar_alloc(unsigned condflags)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
evthread_win32_condvar_free(void * cond_)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
evthread_win32_condvar_signal(void * cond,int broadcast)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
evthread_win32_condvar_wait(void * cond_,void * lock_,const struct timeval * tv)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 *
evthread_win32_cond_alloc(unsigned flags)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
evthread_win32_cond_free(void * cond_)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
evthread_win32_cond_signal(void * cond_,int broadcast)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
evthread_win32_cond_wait(void * cond_,void * lock_,const struct timeval * tv)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
evthread_use_windows_threads(void)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