xref: /libevent-2.1.12/evthread_win32.c (revision 7e45739e)
1b85b710cSNick Mathewson /*
2e49e2891SNick Mathewson  * Copyright 2009-2012 Niels Provos and Nick Mathewson
3b85b710cSNick Mathewson  *
4b85b710cSNick Mathewson  * Redistribution and use in source and binary forms, with or without
5b85b710cSNick Mathewson  * modification, are permitted provided that the following conditions
6b85b710cSNick Mathewson  * are met:
7b85b710cSNick Mathewson  * 1. Redistributions of source code must retain the above copyright
8b85b710cSNick Mathewson  *    notice, this list of conditions and the following disclaimer.
9b85b710cSNick Mathewson  * 2. Redistributions in binary form must reproduce the above copyright
10b85b710cSNick Mathewson  *    notice, this list of conditions and the following disclaimer in the
11b85b710cSNick Mathewson  *    documentation and/or other materials provided with the distribution.
12b85b710cSNick Mathewson  * 3. The name of the author may not be used to endorse or promote products
13b85b710cSNick Mathewson  *    derived from this software without specific prior written permission.
14b85b710cSNick Mathewson  *
15b85b710cSNick Mathewson  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16b85b710cSNick Mathewson  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17b85b710cSNick Mathewson  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18b85b710cSNick Mathewson  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19b85b710cSNick Mathewson  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20b85b710cSNick Mathewson  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21b85b710cSNick Mathewson  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22b85b710cSNick Mathewson  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23b85b710cSNick Mathewson  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24b85b710cSNick Mathewson  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25b85b710cSNick Mathewson  */
26ec347b92SNick Mathewson #include "event2/event-config.h"
27309fc7c4SNick Mathewson #include "evconfig-private.h"
28309fc7c4SNick Mathewson 
29816115a1SPeter Rosin #ifdef _WIN32
30816115a1SPeter Rosin #ifndef _WIN32_WINNT
31816115a1SPeter Rosin /* Minimum required for InitializeCriticalSectionAndSpinCount */
32816115a1SPeter Rosin #define _WIN32_WINNT 0x0403
33309fc7c4SNick Mathewson #endif
34309fc7c4SNick Mathewson #include <winsock2.h>
35309fc7c4SNick Mathewson #define WIN32_LEAN_AND_MEAN
36309fc7c4SNick Mathewson #include <windows.h>
37ec35eb55SNick Mathewson #undef WIN32_LEAN_AND_MEAN
38309fc7c4SNick Mathewson #include <sys/locking.h>
39309fc7c4SNick Mathewson #endif
40309fc7c4SNick Mathewson 
41fbe64f21SEvan Jones struct event_base;
42309fc7c4SNick Mathewson #include "event2/thread.h"
437dd362b1SNick Mathewson 
44d4977b52SNick Mathewson #include "mm-internal.h"
457dd362b1SNick Mathewson #include "evthread-internal.h"
4632c6f1baSNick Mathewson #include "time-internal.h"
4732c6f1baSNick Mathewson 
48309fc7c4SNick Mathewson #define SPIN_COUNT 2000
49347952ffSNick Mathewson 
50309fc7c4SNick Mathewson static void *
evthread_win32_lock_create(unsigned locktype)51309fc7c4SNick Mathewson evthread_win32_lock_create(unsigned locktype)
52309fc7c4SNick Mathewson {
53309fc7c4SNick Mathewson 	CRITICAL_SECTION *lock = mm_malloc(sizeof(CRITICAL_SECTION));
5432c6f1baSNick Mathewson 	if (!lock)
5532c6f1baSNick Mathewson 		return NULL;
5632c6f1baSNick Mathewson 	if (InitializeCriticalSectionAndSpinCount(lock, SPIN_COUNT) == 0) {
5732c6f1baSNick Mathewson 		mm_free(lock);
58309fc7c4SNick Mathewson 		return NULL;
59309fc7c4SNick Mathewson 	}
60309fc7c4SNick Mathewson 	return lock;
61309fc7c4SNick Mathewson }
62347952ffSNick Mathewson 
63309fc7c4SNick Mathewson static void
evthread_win32_lock_free(void * lock_,unsigned locktype)64309fc7c4SNick Mathewson evthread_win32_lock_free(void *lock_, unsigned locktype)
65309fc7c4SNick Mathewson {
662f33e00aSJardel Weyrich 	CRITICAL_SECTION *lock = lock_;
67309fc7c4SNick Mathewson 	DeleteCriticalSection(lock);
68309fc7c4SNick Mathewson 	mm_free(lock);
69bd6f1babSRoman Puls }
70347952ffSNick Mathewson 
71309fc7c4SNick Mathewson static int
evthread_win32_lock(unsigned mode,void * lock_)72309fc7c4SNick Mathewson evthread_win32_lock(unsigned mode, void *lock_)
73347952ffSNick Mathewson {
744c32b9deSNick Mathewson 	CRITICAL_SECTION *lock = lock_;
75347952ffSNick Mathewson 	if ((mode & EVTHREAD_TRY)) {
76309fc7c4SNick Mathewson 		return ! TryEnterCriticalSection(lock);
77347952ffSNick Mathewson 	} else {
78347952ffSNick Mathewson 		EnterCriticalSection(lock);
79347952ffSNick Mathewson 		return 0;
80347952ffSNick Mathewson 	}
81bd6f1babSRoman Puls }
82347952ffSNick Mathewson 
83347952ffSNick Mathewson static int
evthread_win32_unlock(unsigned mode,void * lock_)84347952ffSNick Mathewson evthread_win32_unlock(unsigned mode, void *lock_)
85bd6f1babSRoman Puls {
86bd6f1babSRoman Puls 	CRITICAL_SECTION *lock = lock_;
87309fc7c4SNick Mathewson 	LeaveCriticalSection(lock);
88309fc7c4SNick Mathewson 	return 0;
89309fc7c4SNick Mathewson }
90309fc7c4SNick Mathewson 
91309fc7c4SNick Mathewson static unsigned long
evthread_win32_get_id(void)92309fc7c4SNick Mathewson evthread_win32_get_id(void)
93309fc7c4SNick Mathewson {
94309fc7c4SNick Mathewson 	return (unsigned long) GetCurrentThreadId();
95d4977b52SNick Mathewson }
96d4977b52SNick Mathewson 
97d4977b52SNick Mathewson #ifdef WIN32_HAVE_CONDITION_VARIABLES
98d4977b52SNick Mathewson static void WINAPI (*InitializeConditionVariable_fn)(PCONDITION_VARIABLE)
99d4977b52SNick Mathewson 	= NULL;
100d4977b52SNick Mathewson static BOOL WINAPI (*SleepConditionVariableCS_fn)(
101d4977b52SNick Mathewson 	PCONDITION_VARIABLE, PCRITICAL_SECTION, DWORD) = NULL;
102d4977b52SNick Mathewson static void WINAPI (*WakeAllConditionVariable_fn)(PCONDITION_VARIABLE) = NULL;
103d4977b52SNick Mathewson static void WINAPI (*WakeConditionVariable_fn)(PCONDITION_VARIABLE) = NULL;
104d4977b52SNick Mathewson 
105d4977b52SNick Mathewson static int
evthread_win32_condvar_init(void)106d4977b52SNick Mathewson evthread_win32_condvar_init(void)
107d4977b52SNick Mathewson {
108d4977b52SNick Mathewson 	HANDLE lib;
109d4977b52SNick Mathewson 
110d4977b52SNick Mathewson 	lib = GetModuleHandle(TEXT("kernel32.dll"));
111d4977b52SNick Mathewson 	if (lib == NULL)
112d4977b52SNick Mathewson 		return 0;
113d4977b52SNick Mathewson 
114d4977b52SNick Mathewson #define LOAD(name)				\
115*7e45739eSNick Mathewson 	name##_fn = GetProcAddress(lib, #name)
116d4977b52SNick Mathewson 	LOAD(InitializeConditionVariable);
117d4977b52SNick Mathewson 	LOAD(SleepConditionVariableCS);
118d4977b52SNick Mathewson 	LOAD(WakeAllConditionVariable);
119d4977b52SNick Mathewson 	LOAD(WakeConditionVariable);
120d4977b52SNick Mathewson 
121d4977b52SNick Mathewson 	return InitializeConditionVariable_fn && SleepConditionVariableCS_fn &&
122d4977b52SNick Mathewson 	    WakeAllConditionVariable_fn && WakeConditionVariable_fn;
123d4977b52SNick Mathewson }
124d4977b52SNick Mathewson 
125d4977b52SNick Mathewson /* XXXX Even if we can build this, we don't necessarily want to: the functions
126d4977b52SNick Mathewson  * in question didn't exist before Vista, so we'd better LoadProc them. */
127d4977b52SNick Mathewson static void *
evthread_win32_condvar_alloc(unsigned condflags)128d4977b52SNick Mathewson evthread_win32_condvar_alloc(unsigned condflags)
129d4977b52SNick Mathewson {
130d4977b52SNick Mathewson 	CONDITION_VARIABLE *cond = mm_malloc(sizeof(CONDITION_VARIABLE));
131d4977b52SNick Mathewson 	if (!cond)
132d4977b52SNick Mathewson 		return NULL;
133d4977b52SNick Mathewson 	InitializeConditionVariable_fn(cond);
134d4977b52SNick Mathewson 	return cond;
135d4977b52SNick Mathewson }
136d4977b52SNick Mathewson 
137d4977b52SNick Mathewson static void
evthread_win32_condvar_free(void * cond_)138d4977b52SNick Mathewson evthread_win32_condvar_free(void *cond_)
139d4977b52SNick Mathewson {
140d4977b52SNick Mathewson 	CONDITION_VARIABLE *cond = cond_;
141d4977b52SNick Mathewson 	/* There doesn't _seem_ to be a cleaup fn here... */
142d4977b52SNick Mathewson 	mm_free(cond);
143d4977b52SNick Mathewson }
144d4977b52SNick Mathewson 
145d4977b52SNick Mathewson static int
evthread_win32_condvar_signal(void * cond,int broadcast)146d4977b52SNick Mathewson evthread_win32_condvar_signal(void *cond, int broadcast)
147d4977b52SNick Mathewson {
148d4977b52SNick Mathewson 	CONDITION_VARIABLE *cond = cond_;
149d4977b52SNick Mathewson 	if (broadcast)
150d4977b52SNick Mathewson 		WakeAllConditionVariable_fn(cond);
151d4977b52SNick Mathewson 	else
152d4977b52SNick Mathewson 		WakeConditionVariable_fn(cond);
153d4977b52SNick Mathewson 	return 0;
154d4977b52SNick Mathewson }
155d4977b52SNick Mathewson 
156d4977b52SNick Mathewson static int
evthread_win32_condvar_wait(void * cond_,void * lock_,const struct timeval * tv)157d4977b52SNick Mathewson evthread_win32_condvar_wait(void *cond_, void *lock_, const struct timeval *tv)
158d4977b52SNick Mathewson {
159d4977b52SNick Mathewson 	CONDITION_VARIABLE *cond = cond_;
160d4977b52SNick Mathewson 	CRITICAL_SECTION *lock = lock_;
161d4977b52SNick Mathewson 	DWORD ms, err;
162d4977b52SNick Mathewson 	BOOL result;
163d4977b52SNick Mathewson 
164d4977b52SNick Mathewson 	if (tv)
165d4977b52SNick Mathewson 		ms = evutil_tv_to_msec_(tv);
166d4977b52SNick Mathewson 	else
167d4977b52SNick Mathewson 		ms = INFINITE;
168d4977b52SNick Mathewson 	result = SleepConditionVariableCS_fn(cond, lock, ms);
169d4977b52SNick Mathewson 	if (result) {
170d4977b52SNick Mathewson 		if (GetLastError() == WAIT_TIMEOUT)
171d4977b52SNick Mathewson 			return 1;
172d4977b52SNick Mathewson 		else
173d4977b52SNick Mathewson 			return -1;
174d4977b52SNick Mathewson 	} else {
175d4977b52SNick Mathewson 		return 0;
176d4977b52SNick Mathewson 	}
177d4977b52SNick Mathewson }
178d4977b52SNick Mathewson #endif
179d4977b52SNick Mathewson 
180d4977b52SNick Mathewson struct evthread_win32_cond {
181d4977b52SNick Mathewson 	HANDLE event;
182d4977b52SNick Mathewson 
183d4977b52SNick Mathewson 	CRITICAL_SECTION lock;
184d4977b52SNick Mathewson 	int n_waiting;
185d4977b52SNick Mathewson 	int n_to_wake;
186d4977b52SNick Mathewson 	int generation;
187d4977b52SNick Mathewson };
188d4977b52SNick Mathewson 
189d4977b52SNick Mathewson static void *
evthread_win32_cond_alloc(unsigned flags)190d4977b52SNick Mathewson evthread_win32_cond_alloc(unsigned flags)
191d4977b52SNick Mathewson {
192d4977b52SNick Mathewson 	struct evthread_win32_cond *cond;
193d4977b52SNick Mathewson 	if (!(cond = mm_malloc(sizeof(struct evthread_win32_cond))))
194d4977b52SNick Mathewson 		return NULL;
195d4977b52SNick Mathewson 	if (InitializeCriticalSectionAndSpinCount(&cond->lock, SPIN_COUNT)==0) {
196d4977b52SNick Mathewson 		mm_free(cond);
197d4977b52SNick Mathewson 		return NULL;
198d4977b52SNick Mathewson 	}
199d4977b52SNick Mathewson 	if ((cond->event = CreateEvent(NULL,TRUE,FALSE,NULL)) == NULL) {
200d4977b52SNick Mathewson 		DeleteCriticalSection(&cond->lock);
201d4977b52SNick Mathewson 		mm_free(cond);
202d4977b52SNick Mathewson 		return NULL;
203d4977b52SNick Mathewson 	}
204d4977b52SNick Mathewson 	cond->n_waiting = cond->n_to_wake = cond->generation = 0;
205d4977b52SNick Mathewson 	return cond;
206d4977b52SNick Mathewson }
207d4977b52SNick Mathewson 
208d4977b52SNick Mathewson static void
evthread_win32_cond_free(void * cond_)209d4977b52SNick Mathewson evthread_win32_cond_free(void *cond_)
210d4977b52SNick Mathewson {
211d4977b52SNick Mathewson 	struct evthread_win32_cond *cond = cond_;
212d4977b52SNick Mathewson 	DeleteCriticalSection(&cond->lock);
213d4977b52SNick Mathewson 	CloseHandle(cond->event);
214d4977b52SNick Mathewson 	mm_free(cond);
215d4977b52SNick Mathewson }
216d4977b52SNick Mathewson 
217d4977b52SNick Mathewson static int
evthread_win32_cond_signal(void * cond_,int broadcast)218d4977b52SNick Mathewson evthread_win32_cond_signal(void *cond_, int broadcast)
219d4977b52SNick Mathewson {
220d4977b52SNick Mathewson 	struct evthread_win32_cond *cond = cond_;
221d4977b52SNick Mathewson 	EnterCriticalSection(&cond->lock);
222d4977b52SNick Mathewson 	if (broadcast)
223d4977b52SNick Mathewson 		cond->n_to_wake = cond->n_waiting;
224d4977b52SNick Mathewson 	else
225d4977b52SNick Mathewson 		++cond->n_to_wake;
226d4977b52SNick Mathewson 	cond->generation++;
227d4977b52SNick Mathewson 	SetEvent(cond->event);
228d4977b52SNick Mathewson 	LeaveCriticalSection(&cond->lock);
229d4977b52SNick Mathewson 	return 0;
230d4977b52SNick Mathewson }
231d4977b52SNick Mathewson 
232d4977b52SNick Mathewson static int
evthread_win32_cond_wait(void * cond_,void * lock_,const struct timeval * tv)233d4977b52SNick Mathewson evthread_win32_cond_wait(void *cond_, void *lock_, const struct timeval *tv)
234d4977b52SNick Mathewson {
235d4977b52SNick Mathewson 	struct evthread_win32_cond *cond = cond_;
236d4977b52SNick Mathewson 	CRITICAL_SECTION *lock = lock_;
237d4977b52SNick Mathewson 	int generation_at_start;
238d4977b52SNick Mathewson 	int waiting = 1;
239d4977b52SNick Mathewson 	int result = -1;
240d4977b52SNick Mathewson 	DWORD ms = INFINITE, ms_orig = INFINITE, startTime, endTime;
241d4977b52SNick Mathewson 	if (tv)
242d4977b52SNick Mathewson 		ms_orig = ms = evutil_tv_to_msec_(tv);
243d4977b52SNick Mathewson 
244d4977b52SNick Mathewson 	EnterCriticalSection(&cond->lock);
245d4977b52SNick Mathewson 	++cond->n_waiting;
246d4977b52SNick Mathewson 	generation_at_start = cond->generation;
247d4977b52SNick Mathewson 	LeaveCriticalSection(&cond->lock);
248d4977b52SNick Mathewson 
249d4977b52SNick Mathewson 	LeaveCriticalSection(lock);
250d4977b52SNick Mathewson 
251d4977b52SNick Mathewson 	startTime = GetTickCount();
252d4977b52SNick Mathewson 	do {
253d4977b52SNick Mathewson 		DWORD res;
254d4977b52SNick Mathewson 		res = WaitForSingleObject(cond->event, ms);
255d4977b52SNick Mathewson 		EnterCriticalSection(&cond->lock);
256d4977b52SNick Mathewson 		if (cond->n_to_wake &&
257d4977b52SNick Mathewson 		    cond->generation != generation_at_start) {
258d4977b52SNick Mathewson 			--cond->n_to_wake;
259d4977b52SNick Mathewson 			--cond->n_waiting;
260acc4aca4SNick Mathewson 			result = 0;
261d4977b52SNick Mathewson 			waiting = 0;
262d4977b52SNick Mathewson 			goto out;
263d4977b52SNick Mathewson 		} else if (res != WAIT_OBJECT_0) {
264d4977b52SNick Mathewson 			result = (res==WAIT_TIMEOUT) ? 1 : -1;
265acc4aca4SNick Mathewson 			--cond->n_waiting;
266d4977b52SNick Mathewson 			waiting = 0;
267d4977b52SNick Mathewson 			goto out;
268d4977b52SNick Mathewson 		} else if (ms != INFINITE) {
269d4977b52SNick Mathewson 			endTime = GetTickCount();
270d4977b52SNick Mathewson 			if (startTime + ms_orig <= endTime) {
271d4977b52SNick Mathewson 				result = 1; /* Timeout */
272acc4aca4SNick Mathewson 				--cond->n_waiting;
273d4977b52SNick Mathewson 				waiting = 0;
274d4977b52SNick Mathewson 				goto out;
275d4977b52SNick Mathewson 			} else {
276d4977b52SNick Mathewson 				ms = startTime + ms_orig - endTime;
277acc4aca4SNick Mathewson 			}
278acc4aca4SNick Mathewson 		}
279acc4aca4SNick Mathewson 		/* If we make it here, we are still waiting. */
280acc4aca4SNick Mathewson 		if (cond->n_to_wake == 0) {
281acc4aca4SNick Mathewson 			/* There is nobody else who should wake up; reset
282acc4aca4SNick Mathewson 			 * the event. */
283acc4aca4SNick Mathewson 			ResetEvent(cond->event);
284d4977b52SNick Mathewson 		}
285d4977b52SNick Mathewson 	out:
286d4977b52SNick Mathewson 		LeaveCriticalSection(&cond->lock);
287d4977b52SNick Mathewson 	} while (waiting);
288d4977b52SNick Mathewson 
289d4977b52SNick Mathewson 	EnterCriticalSection(lock);
290d4977b52SNick Mathewson 
291d4977b52SNick Mathewson 	EnterCriticalSection(&cond->lock);
292d4977b52SNick Mathewson 	if (!cond->n_waiting)
293d4977b52SNick Mathewson 		ResetEvent(cond->event);
294d4977b52SNick Mathewson 	LeaveCriticalSection(&cond->lock);
295d4977b52SNick Mathewson 
296d4977b52SNick Mathewson 	return result;
297309fc7c4SNick Mathewson }
298ec35eb55SNick Mathewson 
299309fc7c4SNick Mathewson int
evthread_use_windows_threads(void)300347952ffSNick Mathewson evthread_use_windows_threads(void)
301347952ffSNick Mathewson {
302347952ffSNick Mathewson 	struct evthread_lock_callbacks cbs = {
303bd6f1babSRoman Puls 		EVTHREAD_LOCK_API_VERSION,
304347952ffSNick Mathewson 		EVTHREAD_LOCKTYPE_RECURSIVE,
305347952ffSNick Mathewson 		evthread_win32_lock_create,
306347952ffSNick Mathewson 		evthread_win32_lock_free,
307347952ffSNick Mathewson 		evthread_win32_lock,
308347952ffSNick Mathewson 		evthread_win32_unlock
309d4977b52SNick Mathewson 	};
310d4977b52SNick Mathewson 
311d4977b52SNick Mathewson 
312d4977b52SNick Mathewson 	struct evthread_condition_callbacks cond_cbs = {
313d4977b52SNick Mathewson 		EVTHREAD_CONDITION_API_VERSION,
314d4977b52SNick Mathewson 		evthread_win32_cond_alloc,
315d4977b52SNick Mathewson 		evthread_win32_cond_free,
316d4977b52SNick Mathewson 		evthread_win32_cond_signal,
317d4977b52SNick Mathewson 		evthread_win32_cond_wait
318d4977b52SNick Mathewson 	};
319d4977b52SNick Mathewson #ifdef WIN32_HAVE_CONDITION_VARIABLES
320d4977b52SNick Mathewson 	struct evthread_condition_callbacks condvar_cbs = {
321d4977b52SNick Mathewson 		EVTHREAD_CONDITION_API_VERSION,
322d4977b52SNick Mathewson 		evthread_win32_condvar_alloc,
323d4977b52SNick Mathewson 		evthread_win32_condvar_free,
324d4977b52SNick Mathewson 		evthread_win32_condvar_signal,
325d4977b52SNick Mathewson 		evthread_win32_condvar_wait
326d4977b52SNick Mathewson 	};
327347952ffSNick Mathewson #endif
328ec35eb55SNick Mathewson 
329d4977b52SNick Mathewson 	evthread_set_lock_callbacks(&cbs);
330d4977b52SNick Mathewson 	evthread_set_id_callback(evthread_win32_get_id);
331d4977b52SNick Mathewson #ifdef WIN32_HAVE_CONDITION_VARIABLES
332d4977b52SNick Mathewson 	if (evthread_win32_condvar_init()) {
333d4977b52SNick Mathewson 		evthread_set_condition_callbacks(&condvar_cbs);
334d4977b52SNick Mathewson 		return 0;
335d4977b52SNick Mathewson 	}
336d4977b52SNick Mathewson #endif
337309fc7c4SNick Mathewson 	evthread_set_condition_callbacks(&cond_cbs);
338309fc7c4SNick Mathewson 
339309fc7c4SNick Mathewson 	return 0;
340 }
341 
342