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