xref: /minix3/external/bsd/libevent/dist/evthread_win32.c (revision 0a6a1f1d05b60e214de2f05a7310ddd1f0e590e7)
1 /*	$NetBSD: evthread_win32.c,v 1.1.1.2 2015/01/29 06:38:09 spz 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.2 2015/01/29 06:38:09 spz Exp $");
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 
49 #define SPIN_COUNT 2000
50 
51 static void *
evthread_win32_lock_create(unsigned locktype)52 evthread_win32_lock_create(unsigned locktype)
53 {
54 	CRITICAL_SECTION *lock = mm_malloc(sizeof(CRITICAL_SECTION));
55 	if (!lock)
56 		return NULL;
57 	if (InitializeCriticalSectionAndSpinCount(lock, SPIN_COUNT) == 0) {
58 		mm_free(lock);
59 		return NULL;
60 	}
61 	return lock;
62 }
63 
64 static void
evthread_win32_lock_free(void * _lock,unsigned locktype)65 evthread_win32_lock_free(void *_lock, unsigned locktype)
66 {
67 	CRITICAL_SECTION *lock = _lock;
68 	DeleteCriticalSection(lock);
69 	mm_free(lock);
70 }
71 
72 static int
evthread_win32_lock(unsigned mode,void * _lock)73 evthread_win32_lock(unsigned mode, void *_lock)
74 {
75 	CRITICAL_SECTION *lock = _lock;
76 	if ((mode & EVTHREAD_TRY)) {
77 		return ! TryEnterCriticalSection(lock);
78 	} else {
79 		EnterCriticalSection(lock);
80 		return 0;
81 	}
82 }
83 
84 static int
evthread_win32_unlock(unsigned mode,void * _lock)85 evthread_win32_unlock(unsigned mode, void *_lock)
86 {
87 	CRITICAL_SECTION *lock = _lock;
88 	LeaveCriticalSection(lock);
89 	return 0;
90 }
91 
92 static unsigned long
evthread_win32_get_id(void)93 evthread_win32_get_id(void)
94 {
95 	return (unsigned long) GetCurrentThreadId();
96 }
97 
98 #ifdef WIN32_HAVE_CONDITION_VARIABLES
99 static void WINAPI (*InitializeConditionVariable_fn)(PCONDITION_VARIABLE)
100 	= NULL;
101 static BOOL WINAPI (*SleepConditionVariableCS_fn)(
102 	PCONDITION_VARIABLE, PCRITICAL_SECTION, DWORD) = NULL;
103 static void WINAPI (*WakeAllConditionVariable_fn)(PCONDITION_VARIABLE) = NULL;
104 static void WINAPI (*WakeConditionVariable_fn)(PCONDITION_VARIABLE) = NULL;
105 
106 static int
evthread_win32_condvar_init(void)107 evthread_win32_condvar_init(void)
108 {
109 	HANDLE lib;
110 
111 	lib = GetModuleHandle(TEXT("kernel32.dll"));
112 	if (lib == NULL)
113 		return 0;
114 
115 #define LOAD(name)				\
116 	name##_fn = GetProcAddress(lib, #name)
117 	LOAD(InitializeConditionVariable);
118 	LOAD(SleepConditionVariableCS);
119 	LOAD(WakeAllConditionVariable);
120 	LOAD(WakeConditionVariable);
121 
122 	return InitializeConditionVariable_fn && SleepConditionVariableCS_fn &&
123 	    WakeAllConditionVariable_fn && WakeConditionVariable_fn;
124 }
125 
126 /* XXXX Even if we can build this, we don't necessarily want to: the functions
127  * in question didn't exist before Vista, so we'd better LoadProc them. */
128 static void *
evthread_win32_condvar_alloc(unsigned condflags)129 evthread_win32_condvar_alloc(unsigned condflags)
130 {
131 	CONDITION_VARIABLE *cond = mm_malloc(sizeof(CONDITION_VARIABLE));
132 	if (!cond)
133 		return NULL;
134 	InitializeConditionVariable_fn(cond);
135 	return cond;
136 }
137 
138 static void
evthread_win32_condvar_free(void * _cond)139 evthread_win32_condvar_free(void *_cond)
140 {
141 	CONDITION_VARIABLE *cond = _cond;
142 	/* There doesn't _seem_ to be a cleaup fn here... */
143 	mm_free(cond);
144 }
145 
146 static int
evthread_win32_condvar_signal(void * _cond,int broadcast)147 evthread_win32_condvar_signal(void *_cond, int broadcast)
148 {
149 	CONDITION_VARIABLE *cond = _cond;
150 	if (broadcast)
151 		WakeAllConditionVariable_fn(cond);
152 	else
153 		WakeConditionVariable_fn(cond);
154 	return 0;
155 }
156 
157 static int
evthread_win32_condvar_wait(void * _cond,void * _lock,const struct timeval * tv)158 evthread_win32_condvar_wait(void *_cond, void *_lock, const struct timeval *tv)
159 {
160 	CONDITION_VARIABLE *cond = _cond;
161 	CRITICAL_SECTION *lock = _lock;
162 	DWORD ms, err;
163 	BOOL result;
164 
165 	if (tv)
166 		ms = evutil_tv_to_msec(tv);
167 	else
168 		ms = INFINITE;
169 	result = SleepConditionVariableCS_fn(cond, lock, ms);
170 	if (result) {
171 		if (GetLastError() == WAIT_TIMEOUT)
172 			return 1;
173 		else
174 			return -1;
175 	} else {
176 		return 0;
177 	}
178 }
179 #endif
180 
181 struct evthread_win32_cond {
182 	HANDLE event;
183 
184 	CRITICAL_SECTION lock;
185 	int n_waiting;
186 	int n_to_wake;
187 	int generation;
188 };
189 
190 static void *
evthread_win32_cond_alloc(unsigned flags)191 evthread_win32_cond_alloc(unsigned flags)
192 {
193 	struct evthread_win32_cond *cond;
194 	if (!(cond = mm_malloc(sizeof(struct evthread_win32_cond))))
195 		return NULL;
196 	if (InitializeCriticalSectionAndSpinCount(&cond->lock, SPIN_COUNT)==0) {
197 		mm_free(cond);
198 		return NULL;
199 	}
200 	if ((cond->event = CreateEvent(NULL,TRUE,FALSE,NULL)) == NULL) {
201 		DeleteCriticalSection(&cond->lock);
202 		mm_free(cond);
203 		return NULL;
204 	}
205 	cond->n_waiting = cond->n_to_wake = cond->generation = 0;
206 	return cond;
207 }
208 
209 static void
evthread_win32_cond_free(void * _cond)210 evthread_win32_cond_free(void *_cond)
211 {
212 	struct evthread_win32_cond *cond = _cond;
213 	DeleteCriticalSection(&cond->lock);
214 	CloseHandle(cond->event);
215 	mm_free(cond);
216 }
217 
218 static int
evthread_win32_cond_signal(void * _cond,int broadcast)219 evthread_win32_cond_signal(void *_cond, int broadcast)
220 {
221 	struct evthread_win32_cond *cond = _cond;
222 	EnterCriticalSection(&cond->lock);
223 	if (broadcast)
224 		cond->n_to_wake = cond->n_waiting;
225 	else
226 		++cond->n_to_wake;
227 	cond->generation++;
228 	SetEvent(cond->event);
229 	LeaveCriticalSection(&cond->lock);
230 	return 0;
231 }
232 
233 static int
evthread_win32_cond_wait(void * _cond,void * _lock,const struct timeval * tv)234 evthread_win32_cond_wait(void *_cond, void *_lock, const struct timeval *tv)
235 {
236 	struct evthread_win32_cond *cond = _cond;
237 	CRITICAL_SECTION *lock = _lock;
238 	int generation_at_start;
239 	int waiting = 1;
240 	int result = -1;
241 	DWORD ms = INFINITE, ms_orig = INFINITE, startTime, endTime;
242 	if (tv)
243 		ms_orig = ms = evutil_tv_to_msec(tv);
244 
245 	EnterCriticalSection(&cond->lock);
246 	++cond->n_waiting;
247 	generation_at_start = cond->generation;
248 	LeaveCriticalSection(&cond->lock);
249 
250 	LeaveCriticalSection(lock);
251 
252 	startTime = GetTickCount();
253 	do {
254 		DWORD res;
255 		res = WaitForSingleObject(cond->event, ms);
256 		EnterCriticalSection(&cond->lock);
257 		if (cond->n_to_wake &&
258 		    cond->generation != generation_at_start) {
259 			--cond->n_to_wake;
260 			--cond->n_waiting;
261 			result = 0;
262 			waiting = 0;
263 			goto out;
264 		} else if (res != WAIT_OBJECT_0) {
265 			result = (res==WAIT_TIMEOUT) ? 1 : -1;
266 			--cond->n_waiting;
267 			waiting = 0;
268 			goto out;
269 		} else if (ms != INFINITE) {
270 			endTime = GetTickCount();
271 			if (startTime + ms_orig <= endTime) {
272 				result = 1; /* Timeout */
273 				--cond->n_waiting;
274 				waiting = 0;
275 				goto out;
276 			} else {
277 				ms = startTime + ms_orig - endTime;
278 			}
279 		}
280 		/* If we make it here, we are still waiting. */
281 		if (cond->n_to_wake == 0) {
282 			/* There is nobody else who should wake up; reset
283 			 * the event. */
284 			ResetEvent(cond->event);
285 		}
286 	out:
287 		LeaveCriticalSection(&cond->lock);
288 	} while (waiting);
289 
290 	EnterCriticalSection(lock);
291 
292 	EnterCriticalSection(&cond->lock);
293 	if (!cond->n_waiting)
294 		ResetEvent(cond->event);
295 	LeaveCriticalSection(&cond->lock);
296 
297 	return result;
298 }
299 
300 int
evthread_use_windows_threads(void)301 evthread_use_windows_threads(void)
302 {
303 	struct evthread_lock_callbacks cbs = {
304 		EVTHREAD_LOCK_API_VERSION,
305 		EVTHREAD_LOCKTYPE_RECURSIVE,
306 		evthread_win32_lock_create,
307 		evthread_win32_lock_free,
308 		evthread_win32_lock,
309 		evthread_win32_unlock
310 	};
311 
312 
313 	struct evthread_condition_callbacks cond_cbs = {
314 		EVTHREAD_CONDITION_API_VERSION,
315 		evthread_win32_cond_alloc,
316 		evthread_win32_cond_free,
317 		evthread_win32_cond_signal,
318 		evthread_win32_cond_wait
319 	};
320 #ifdef WIN32_HAVE_CONDITION_VARIABLES
321 	struct evthread_condition_callbacks condvar_cbs = {
322 		EVTHREAD_CONDITION_API_VERSION,
323 		evthread_win32_condvar_alloc,
324 		evthread_win32_condvar_free,
325 		evthread_win32_condvar_signal,
326 		evthread_win32_condvar_wait
327 	};
328 #endif
329 
330 	evthread_set_lock_callbacks(&cbs);
331 	evthread_set_id_callback(evthread_win32_get_id);
332 #ifdef WIN32_HAVE_CONDITION_VARIABLES
333 	if (evthread_win32_condvar_init()) {
334 		evthread_set_condition_callbacks(&condvar_cbs);
335 		return 0;
336 	}
337 #endif
338 	evthread_set_condition_callbacks(&cond_cbs);
339 
340 	return 0;
341 }
342 
343