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