xref: /freebsd/contrib/libevent/evthread_win32.c (revision c6879c6c14eedbd060ba588a3129a6c60ebbe783)
1*c43e99fdSEd Maste /*
2*c43e99fdSEd Maste  * Copyright 2009-2012 Niels Provos and Nick Mathewson
3*c43e99fdSEd Maste  *
4*c43e99fdSEd Maste  * Redistribution and use in source and binary forms, with or without
5*c43e99fdSEd Maste  * modification, are permitted provided that the following conditions
6*c43e99fdSEd Maste  * are met:
7*c43e99fdSEd Maste  * 1. Redistributions of source code must retain the above copyright
8*c43e99fdSEd Maste  *    notice, this list of conditions and the following disclaimer.
9*c43e99fdSEd Maste  * 2. Redistributions in binary form must reproduce the above copyright
10*c43e99fdSEd Maste  *    notice, this list of conditions and the following disclaimer in the
11*c43e99fdSEd Maste  *    documentation and/or other materials provided with the distribution.
12*c43e99fdSEd Maste  * 3. The name of the author may not be used to endorse or promote products
13*c43e99fdSEd Maste  *    derived from this software without specific prior written permission.
14*c43e99fdSEd Maste  *
15*c43e99fdSEd Maste  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16*c43e99fdSEd Maste  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17*c43e99fdSEd Maste  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18*c43e99fdSEd Maste  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19*c43e99fdSEd Maste  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20*c43e99fdSEd Maste  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21*c43e99fdSEd Maste  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22*c43e99fdSEd Maste  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23*c43e99fdSEd Maste  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24*c43e99fdSEd Maste  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25*c43e99fdSEd Maste  */
26*c43e99fdSEd Maste #include "event2/event-config.h"
27*c43e99fdSEd Maste #include "evconfig-private.h"
28*c43e99fdSEd Maste 
29*c43e99fdSEd Maste #ifdef _WIN32
30*c43e99fdSEd Maste #ifndef _WIN32_WINNT
31*c43e99fdSEd Maste /* Minimum required for InitializeCriticalSectionAndSpinCount */
32*c43e99fdSEd Maste #define _WIN32_WINNT 0x0403
33*c43e99fdSEd Maste #endif
34*c43e99fdSEd Maste #include <winsock2.h>
35*c43e99fdSEd Maste #define WIN32_LEAN_AND_MEAN
36*c43e99fdSEd Maste #include <windows.h>
37*c43e99fdSEd Maste #undef WIN32_LEAN_AND_MEAN
38*c43e99fdSEd Maste #include <sys/locking.h>
39*c43e99fdSEd Maste #endif
40*c43e99fdSEd Maste 
41*c43e99fdSEd Maste struct event_base;
42*c43e99fdSEd Maste #include "event2/thread.h"
43*c43e99fdSEd Maste 
44*c43e99fdSEd Maste #include "mm-internal.h"
45*c43e99fdSEd Maste #include "evthread-internal.h"
46*c43e99fdSEd Maste #include "time-internal.h"
47*c43e99fdSEd Maste 
48*c43e99fdSEd Maste #define SPIN_COUNT 2000
49*c43e99fdSEd Maste 
50*c43e99fdSEd Maste static void *
evthread_win32_lock_create(unsigned locktype)51*c43e99fdSEd Maste evthread_win32_lock_create(unsigned locktype)
52*c43e99fdSEd Maste {
53*c43e99fdSEd Maste 	CRITICAL_SECTION *lock = mm_malloc(sizeof(CRITICAL_SECTION));
54*c43e99fdSEd Maste 	if (!lock)
55*c43e99fdSEd Maste 		return NULL;
56*c43e99fdSEd Maste 	if (InitializeCriticalSectionAndSpinCount(lock, SPIN_COUNT) == 0) {
57*c43e99fdSEd Maste 		mm_free(lock);
58*c43e99fdSEd Maste 		return NULL;
59*c43e99fdSEd Maste 	}
60*c43e99fdSEd Maste 	return lock;
61*c43e99fdSEd Maste }
62*c43e99fdSEd Maste 
63*c43e99fdSEd Maste static void
evthread_win32_lock_free(void * lock_,unsigned locktype)64*c43e99fdSEd Maste evthread_win32_lock_free(void *lock_, unsigned locktype)
65*c43e99fdSEd Maste {
66*c43e99fdSEd Maste 	CRITICAL_SECTION *lock = lock_;
67*c43e99fdSEd Maste 	DeleteCriticalSection(lock);
68*c43e99fdSEd Maste 	mm_free(lock);
69*c43e99fdSEd Maste }
70*c43e99fdSEd Maste 
71*c43e99fdSEd Maste static int
evthread_win32_lock(unsigned mode,void * lock_)72*c43e99fdSEd Maste evthread_win32_lock(unsigned mode, void *lock_)
73*c43e99fdSEd Maste {
74*c43e99fdSEd Maste 	CRITICAL_SECTION *lock = lock_;
75*c43e99fdSEd Maste 	if ((mode & EVTHREAD_TRY)) {
76*c43e99fdSEd Maste 		return ! TryEnterCriticalSection(lock);
77*c43e99fdSEd Maste 	} else {
78*c43e99fdSEd Maste 		EnterCriticalSection(lock);
79*c43e99fdSEd Maste 		return 0;
80*c43e99fdSEd Maste 	}
81*c43e99fdSEd Maste }
82*c43e99fdSEd Maste 
83*c43e99fdSEd Maste static int
evthread_win32_unlock(unsigned mode,void * lock_)84*c43e99fdSEd Maste evthread_win32_unlock(unsigned mode, void *lock_)
85*c43e99fdSEd Maste {
86*c43e99fdSEd Maste 	CRITICAL_SECTION *lock = lock_;
87*c43e99fdSEd Maste 	LeaveCriticalSection(lock);
88*c43e99fdSEd Maste 	return 0;
89*c43e99fdSEd Maste }
90*c43e99fdSEd Maste 
91*c43e99fdSEd Maste static unsigned long
evthread_win32_get_id(void)92*c43e99fdSEd Maste evthread_win32_get_id(void)
93*c43e99fdSEd Maste {
94*c43e99fdSEd Maste 	return (unsigned long) GetCurrentThreadId();
95*c43e99fdSEd Maste }
96*c43e99fdSEd Maste 
97*c43e99fdSEd Maste #ifdef WIN32_HAVE_CONDITION_VARIABLES
98*c43e99fdSEd Maste static void WINAPI (*InitializeConditionVariable_fn)(PCONDITION_VARIABLE)
99*c43e99fdSEd Maste 	= NULL;
100*c43e99fdSEd Maste static BOOL WINAPI (*SleepConditionVariableCS_fn)(
101*c43e99fdSEd Maste 	PCONDITION_VARIABLE, PCRITICAL_SECTION, DWORD) = NULL;
102*c43e99fdSEd Maste static void WINAPI (*WakeAllConditionVariable_fn)(PCONDITION_VARIABLE) = NULL;
103*c43e99fdSEd Maste static void WINAPI (*WakeConditionVariable_fn)(PCONDITION_VARIABLE) = NULL;
104*c43e99fdSEd Maste 
105*c43e99fdSEd Maste static int
evthread_win32_condvar_init(void)106*c43e99fdSEd Maste evthread_win32_condvar_init(void)
107*c43e99fdSEd Maste {
108*c43e99fdSEd Maste 	HANDLE lib;
109*c43e99fdSEd Maste 
110*c43e99fdSEd Maste 	lib = GetModuleHandle(TEXT("kernel32.dll"));
111*c43e99fdSEd Maste 	if (lib == NULL)
112*c43e99fdSEd Maste 		return 0;
113*c43e99fdSEd Maste 
114*c43e99fdSEd Maste #define LOAD(name)				\
115*c43e99fdSEd Maste 	name##_fn = GetProcAddress(lib, #name)
116*c43e99fdSEd Maste 	LOAD(InitializeConditionVariable);
117*c43e99fdSEd Maste 	LOAD(SleepConditionVariableCS);
118*c43e99fdSEd Maste 	LOAD(WakeAllConditionVariable);
119*c43e99fdSEd Maste 	LOAD(WakeConditionVariable);
120*c43e99fdSEd Maste 
121*c43e99fdSEd Maste 	return InitializeConditionVariable_fn && SleepConditionVariableCS_fn &&
122*c43e99fdSEd Maste 	    WakeAllConditionVariable_fn && WakeConditionVariable_fn;
123*c43e99fdSEd Maste }
124*c43e99fdSEd Maste 
125*c43e99fdSEd Maste /* XXXX Even if we can build this, we don't necessarily want to: the functions
126*c43e99fdSEd Maste  * in question didn't exist before Vista, so we'd better LoadProc them. */
127*c43e99fdSEd Maste static void *
evthread_win32_condvar_alloc(unsigned condflags)128*c43e99fdSEd Maste evthread_win32_condvar_alloc(unsigned condflags)
129*c43e99fdSEd Maste {
130*c43e99fdSEd Maste 	CONDITION_VARIABLE *cond = mm_malloc(sizeof(CONDITION_VARIABLE));
131*c43e99fdSEd Maste 	if (!cond)
132*c43e99fdSEd Maste 		return NULL;
133*c43e99fdSEd Maste 	InitializeConditionVariable_fn(cond);
134*c43e99fdSEd Maste 	return cond;
135*c43e99fdSEd Maste }
136*c43e99fdSEd Maste 
137*c43e99fdSEd Maste static void
evthread_win32_condvar_free(void * cond_)138*c43e99fdSEd Maste evthread_win32_condvar_free(void *cond_)
139*c43e99fdSEd Maste {
140*c43e99fdSEd Maste 	CONDITION_VARIABLE *cond = cond_;
141*c43e99fdSEd Maste 	/* There doesn't _seem_ to be a cleaup fn here... */
142*c43e99fdSEd Maste 	mm_free(cond);
143*c43e99fdSEd Maste }
144*c43e99fdSEd Maste 
145*c43e99fdSEd Maste static int
evthread_win32_condvar_signal(void * cond,int broadcast)146*c43e99fdSEd Maste evthread_win32_condvar_signal(void *cond, int broadcast)
147*c43e99fdSEd Maste {
148*c43e99fdSEd Maste 	CONDITION_VARIABLE *cond = cond_;
149*c43e99fdSEd Maste 	if (broadcast)
150*c43e99fdSEd Maste 		WakeAllConditionVariable_fn(cond);
151*c43e99fdSEd Maste 	else
152*c43e99fdSEd Maste 		WakeConditionVariable_fn(cond);
153*c43e99fdSEd Maste 	return 0;
154*c43e99fdSEd Maste }
155*c43e99fdSEd Maste 
156*c43e99fdSEd Maste static int
evthread_win32_condvar_wait(void * cond_,void * lock_,const struct timeval * tv)157*c43e99fdSEd Maste evthread_win32_condvar_wait(void *cond_, void *lock_, const struct timeval *tv)
158*c43e99fdSEd Maste {
159*c43e99fdSEd Maste 	CONDITION_VARIABLE *cond = cond_;
160*c43e99fdSEd Maste 	CRITICAL_SECTION *lock = lock_;
161*c43e99fdSEd Maste 	DWORD ms, err;
162*c43e99fdSEd Maste 	BOOL result;
163*c43e99fdSEd Maste 
164*c43e99fdSEd Maste 	if (tv)
165*c43e99fdSEd Maste 		ms = evutil_tv_to_msec_(tv);
166*c43e99fdSEd Maste 	else
167*c43e99fdSEd Maste 		ms = INFINITE;
168*c43e99fdSEd Maste 	result = SleepConditionVariableCS_fn(cond, lock, ms);
169*c43e99fdSEd Maste 	if (result) {
170*c43e99fdSEd Maste 		if (GetLastError() == WAIT_TIMEOUT)
171*c43e99fdSEd Maste 			return 1;
172*c43e99fdSEd Maste 		else
173*c43e99fdSEd Maste 			return -1;
174*c43e99fdSEd Maste 	} else {
175*c43e99fdSEd Maste 		return 0;
176*c43e99fdSEd Maste 	}
177*c43e99fdSEd Maste }
178*c43e99fdSEd Maste #endif
179*c43e99fdSEd Maste 
180*c43e99fdSEd Maste struct evthread_win32_cond {
181*c43e99fdSEd Maste 	HANDLE event;
182*c43e99fdSEd Maste 
183*c43e99fdSEd Maste 	CRITICAL_SECTION lock;
184*c43e99fdSEd Maste 	int n_waiting;
185*c43e99fdSEd Maste 	int n_to_wake;
186*c43e99fdSEd Maste 	int generation;
187*c43e99fdSEd Maste };
188*c43e99fdSEd Maste 
189*c43e99fdSEd Maste static void *
evthread_win32_cond_alloc(unsigned flags)190*c43e99fdSEd Maste evthread_win32_cond_alloc(unsigned flags)
191*c43e99fdSEd Maste {
192*c43e99fdSEd Maste 	struct evthread_win32_cond *cond;
193*c43e99fdSEd Maste 	if (!(cond = mm_malloc(sizeof(struct evthread_win32_cond))))
194*c43e99fdSEd Maste 		return NULL;
195*c43e99fdSEd Maste 	if (InitializeCriticalSectionAndSpinCount(&cond->lock, SPIN_COUNT)==0) {
196*c43e99fdSEd Maste 		mm_free(cond);
197*c43e99fdSEd Maste 		return NULL;
198*c43e99fdSEd Maste 	}
199*c43e99fdSEd Maste 	if ((cond->event = CreateEvent(NULL,TRUE,FALSE,NULL)) == NULL) {
200*c43e99fdSEd Maste 		DeleteCriticalSection(&cond->lock);
201*c43e99fdSEd Maste 		mm_free(cond);
202*c43e99fdSEd Maste 		return NULL;
203*c43e99fdSEd Maste 	}
204*c43e99fdSEd Maste 	cond->n_waiting = cond->n_to_wake = cond->generation = 0;
205*c43e99fdSEd Maste 	return cond;
206*c43e99fdSEd Maste }
207*c43e99fdSEd Maste 
208*c43e99fdSEd Maste static void
evthread_win32_cond_free(void * cond_)209*c43e99fdSEd Maste evthread_win32_cond_free(void *cond_)
210*c43e99fdSEd Maste {
211*c43e99fdSEd Maste 	struct evthread_win32_cond *cond = cond_;
212*c43e99fdSEd Maste 	DeleteCriticalSection(&cond->lock);
213*c43e99fdSEd Maste 	CloseHandle(cond->event);
214*c43e99fdSEd Maste 	mm_free(cond);
215*c43e99fdSEd Maste }
216*c43e99fdSEd Maste 
217*c43e99fdSEd Maste static int
evthread_win32_cond_signal(void * cond_,int broadcast)218*c43e99fdSEd Maste evthread_win32_cond_signal(void *cond_, int broadcast)
219*c43e99fdSEd Maste {
220*c43e99fdSEd Maste 	struct evthread_win32_cond *cond = cond_;
221*c43e99fdSEd Maste 	EnterCriticalSection(&cond->lock);
222*c43e99fdSEd Maste 	if (broadcast)
223*c43e99fdSEd Maste 		cond->n_to_wake = cond->n_waiting;
224*c43e99fdSEd Maste 	else
225*c43e99fdSEd Maste 		++cond->n_to_wake;
226*c43e99fdSEd Maste 	cond->generation++;
227*c43e99fdSEd Maste 	SetEvent(cond->event);
228*c43e99fdSEd Maste 	LeaveCriticalSection(&cond->lock);
229*c43e99fdSEd Maste 	return 0;
230*c43e99fdSEd Maste }
231*c43e99fdSEd Maste 
232*c43e99fdSEd Maste static int
evthread_win32_cond_wait(void * cond_,void * lock_,const struct timeval * tv)233*c43e99fdSEd Maste evthread_win32_cond_wait(void *cond_, void *lock_, const struct timeval *tv)
234*c43e99fdSEd Maste {
235*c43e99fdSEd Maste 	struct evthread_win32_cond *cond = cond_;
236*c43e99fdSEd Maste 	CRITICAL_SECTION *lock = lock_;
237*c43e99fdSEd Maste 	int generation_at_start;
238*c43e99fdSEd Maste 	int waiting = 1;
239*c43e99fdSEd Maste 	int result = -1;
240*c43e99fdSEd Maste 	DWORD ms = INFINITE, ms_orig = INFINITE, startTime, endTime;
241*c43e99fdSEd Maste 	if (tv)
242*c43e99fdSEd Maste 		ms_orig = ms = evutil_tv_to_msec_(tv);
243*c43e99fdSEd Maste 
244*c43e99fdSEd Maste 	EnterCriticalSection(&cond->lock);
245*c43e99fdSEd Maste 	++cond->n_waiting;
246*c43e99fdSEd Maste 	generation_at_start = cond->generation;
247*c43e99fdSEd Maste 	LeaveCriticalSection(&cond->lock);
248*c43e99fdSEd Maste 
249*c43e99fdSEd Maste 	LeaveCriticalSection(lock);
250*c43e99fdSEd Maste 
251*c43e99fdSEd Maste 	startTime = GetTickCount();
252*c43e99fdSEd Maste 	do {
253*c43e99fdSEd Maste 		DWORD res;
254*c43e99fdSEd Maste 		res = WaitForSingleObject(cond->event, ms);
255*c43e99fdSEd Maste 		EnterCriticalSection(&cond->lock);
256*c43e99fdSEd Maste 		if (cond->n_to_wake &&
257*c43e99fdSEd Maste 		    cond->generation != generation_at_start) {
258*c43e99fdSEd Maste 			--cond->n_to_wake;
259*c43e99fdSEd Maste 			--cond->n_waiting;
260*c43e99fdSEd Maste 			result = 0;
261*c43e99fdSEd Maste 			waiting = 0;
262*c43e99fdSEd Maste 			goto out;
263*c43e99fdSEd Maste 		} else if (res != WAIT_OBJECT_0) {
264*c43e99fdSEd Maste 			result = (res==WAIT_TIMEOUT) ? 1 : -1;
265*c43e99fdSEd Maste 			--cond->n_waiting;
266*c43e99fdSEd Maste 			waiting = 0;
267*c43e99fdSEd Maste 			goto out;
268*c43e99fdSEd Maste 		} else if (ms != INFINITE) {
269*c43e99fdSEd Maste 			endTime = GetTickCount();
270*c43e99fdSEd Maste 			if (startTime + ms_orig <= endTime) {
271*c43e99fdSEd Maste 				result = 1; /* Timeout */
272*c43e99fdSEd Maste 				--cond->n_waiting;
273*c43e99fdSEd Maste 				waiting = 0;
274*c43e99fdSEd Maste 				goto out;
275*c43e99fdSEd Maste 			} else {
276*c43e99fdSEd Maste 				ms = startTime + ms_orig - endTime;
277*c43e99fdSEd Maste 			}
278*c43e99fdSEd Maste 		}
279*c43e99fdSEd Maste 		/* If we make it here, we are still waiting. */
280*c43e99fdSEd Maste 		if (cond->n_to_wake == 0) {
281*c43e99fdSEd Maste 			/* There is nobody else who should wake up; reset
282*c43e99fdSEd Maste 			 * the event. */
283*c43e99fdSEd Maste 			ResetEvent(cond->event);
284*c43e99fdSEd Maste 		}
285*c43e99fdSEd Maste 	out:
286*c43e99fdSEd Maste 		LeaveCriticalSection(&cond->lock);
287*c43e99fdSEd Maste 	} while (waiting);
288*c43e99fdSEd Maste 
289*c43e99fdSEd Maste 	EnterCriticalSection(lock);
290*c43e99fdSEd Maste 
291*c43e99fdSEd Maste 	EnterCriticalSection(&cond->lock);
292*c43e99fdSEd Maste 	if (!cond->n_waiting)
293*c43e99fdSEd Maste 		ResetEvent(cond->event);
294*c43e99fdSEd Maste 	LeaveCriticalSection(&cond->lock);
295*c43e99fdSEd Maste 
296*c43e99fdSEd Maste 	return result;
297*c43e99fdSEd Maste }
298*c43e99fdSEd Maste 
299*c43e99fdSEd Maste int
evthread_use_windows_threads(void)300*c43e99fdSEd Maste evthread_use_windows_threads(void)
301*c43e99fdSEd Maste {
302*c43e99fdSEd Maste 	struct evthread_lock_callbacks cbs = {
303*c43e99fdSEd Maste 		EVTHREAD_LOCK_API_VERSION,
304*c43e99fdSEd Maste 		EVTHREAD_LOCKTYPE_RECURSIVE,
305*c43e99fdSEd Maste 		evthread_win32_lock_create,
306*c43e99fdSEd Maste 		evthread_win32_lock_free,
307*c43e99fdSEd Maste 		evthread_win32_lock,
308*c43e99fdSEd Maste 		evthread_win32_unlock
309*c43e99fdSEd Maste 	};
310*c43e99fdSEd Maste 
311*c43e99fdSEd Maste 
312*c43e99fdSEd Maste 	struct evthread_condition_callbacks cond_cbs = {
313*c43e99fdSEd Maste 		EVTHREAD_CONDITION_API_VERSION,
314*c43e99fdSEd Maste 		evthread_win32_cond_alloc,
315*c43e99fdSEd Maste 		evthread_win32_cond_free,
316*c43e99fdSEd Maste 		evthread_win32_cond_signal,
317*c43e99fdSEd Maste 		evthread_win32_cond_wait
318*c43e99fdSEd Maste 	};
319*c43e99fdSEd Maste #ifdef WIN32_HAVE_CONDITION_VARIABLES
320*c43e99fdSEd Maste 	struct evthread_condition_callbacks condvar_cbs = {
321*c43e99fdSEd Maste 		EVTHREAD_CONDITION_API_VERSION,
322*c43e99fdSEd Maste 		evthread_win32_condvar_alloc,
323*c43e99fdSEd Maste 		evthread_win32_condvar_free,
324*c43e99fdSEd Maste 		evthread_win32_condvar_signal,
325*c43e99fdSEd Maste 		evthread_win32_condvar_wait
326*c43e99fdSEd Maste 	};
327*c43e99fdSEd Maste #endif
328*c43e99fdSEd Maste 
329*c43e99fdSEd Maste 	evthread_set_lock_callbacks(&cbs);
330*c43e99fdSEd Maste 	evthread_set_id_callback(evthread_win32_get_id);
331*c43e99fdSEd Maste #ifdef WIN32_HAVE_CONDITION_VARIABLES
332*c43e99fdSEd Maste 	if (evthread_win32_condvar_init()) {
333*c43e99fdSEd Maste 		evthread_set_condition_callbacks(&condvar_cbs);
334*c43e99fdSEd Maste 		return 0;
335*c43e99fdSEd Maste 	}
336*c43e99fdSEd Maste #endif
337*c43e99fdSEd Maste 	evthread_set_condition_callbacks(&cond_cbs);
338*c43e99fdSEd Maste 
339*c43e99fdSEd Maste 	return 0;
340*c43e99fdSEd Maste }
341*c43e99fdSEd Maste 
342