xref: /netbsd-src/external/bsd/libevent/dist/evthread_win32.c (revision 805a1ce9000bc0ec0951bf35f7e52b85cafb37e2)
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