1275970Scy/*
2275970Scy * Copyright 2009-2012 Niels Provos and Nick Mathewson
3275970Scy *
4275970Scy * Redistribution and use in source and binary forms, with or without
5275970Scy * modification, are permitted provided that the following conditions
6275970Scy * are met:
7275970Scy * 1. Redistributions of source code must retain the above copyright
8275970Scy *    notice, this list of conditions and the following disclaimer.
9275970Scy * 2. Redistributions in binary form must reproduce the above copyright
10275970Scy *    notice, this list of conditions and the following disclaimer in the
11275970Scy *    documentation and/or other materials provided with the distribution.
12275970Scy * 3. The name of the author may not be used to endorse or promote products
13275970Scy *    derived from this software without specific prior written permission.
14275970Scy *
15275970Scy * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16275970Scy * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17275970Scy * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18275970Scy * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19275970Scy * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20275970Scy * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21275970Scy * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22275970Scy * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23275970Scy * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24275970Scy * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25275970Scy */
26275970Scy#include "event2/event-config.h"
27275970Scy#include "evconfig-private.h"
28275970Scy
29275970Scy#ifdef _WIN32
30275970Scy#ifndef _WIN32_WINNT
31275970Scy/* Minimum required for InitializeCriticalSectionAndSpinCount */
32275970Scy#define _WIN32_WINNT 0x0403
33275970Scy#endif
34275970Scy#include <winsock2.h>
35275970Scy#define WIN32_LEAN_AND_MEAN
36275970Scy#include <windows.h>
37275970Scy#undef WIN32_LEAN_AND_MEAN
38275970Scy#include <sys/locking.h>
39275970Scy#endif
40275970Scy
41275970Scystruct event_base;
42275970Scy#include "event2/thread.h"
43275970Scy
44275970Scy#include "mm-internal.h"
45275970Scy#include "evthread-internal.h"
46275970Scy#include "time-internal.h"
47275970Scy
48275970Scy#define SPIN_COUNT 2000
49275970Scy
50275970Scystatic void *
51275970Scyevthread_win32_lock_create(unsigned locktype)
52275970Scy{
53275970Scy	CRITICAL_SECTION *lock = mm_malloc(sizeof(CRITICAL_SECTION));
54275970Scy	if (!lock)
55275970Scy		return NULL;
56275970Scy	if (InitializeCriticalSectionAndSpinCount(lock, SPIN_COUNT) == 0) {
57275970Scy		mm_free(lock);
58275970Scy		return NULL;
59275970Scy	}
60275970Scy	return lock;
61275970Scy}
62275970Scy
63275970Scystatic void
64275970Scyevthread_win32_lock_free(void *lock_, unsigned locktype)
65275970Scy{
66275970Scy	CRITICAL_SECTION *lock = lock_;
67275970Scy	DeleteCriticalSection(lock);
68275970Scy	mm_free(lock);
69275970Scy}
70275970Scy
71275970Scystatic int
72275970Scyevthread_win32_lock(unsigned mode, void *lock_)
73275970Scy{
74275970Scy	CRITICAL_SECTION *lock = lock_;
75275970Scy	if ((mode & EVTHREAD_TRY)) {
76275970Scy		return ! TryEnterCriticalSection(lock);
77275970Scy	} else {
78275970Scy		EnterCriticalSection(lock);
79275970Scy		return 0;
80275970Scy	}
81275970Scy}
82275970Scy
83275970Scystatic int
84275970Scyevthread_win32_unlock(unsigned mode, void *lock_)
85275970Scy{
86275970Scy	CRITICAL_SECTION *lock = lock_;
87275970Scy	LeaveCriticalSection(lock);
88275970Scy	return 0;
89275970Scy}
90275970Scy
91275970Scystatic unsigned long
92275970Scyevthread_win32_get_id(void)
93275970Scy{
94275970Scy	return (unsigned long) GetCurrentThreadId();
95275970Scy}
96275970Scy
97275970Scy#ifdef WIN32_HAVE_CONDITION_VARIABLES
98275970Scystatic void WINAPI (*InitializeConditionVariable_fn)(PCONDITION_VARIABLE)
99275970Scy	= NULL;
100275970Scystatic BOOL WINAPI (*SleepConditionVariableCS_fn)(
101275970Scy	PCONDITION_VARIABLE, PCRITICAL_SECTION, DWORD) = NULL;
102275970Scystatic void WINAPI (*WakeAllConditionVariable_fn)(PCONDITION_VARIABLE) = NULL;
103275970Scystatic void WINAPI (*WakeConditionVariable_fn)(PCONDITION_VARIABLE) = NULL;
104275970Scy
105275970Scystatic int
106275970Scyevthread_win32_condvar_init(void)
107275970Scy{
108275970Scy	HANDLE lib;
109275970Scy
110275970Scy	lib = GetModuleHandle(TEXT("kernel32.dll"));
111275970Scy	if (lib == NULL)
112275970Scy		return 0;
113275970Scy
114275970Scy#define LOAD(name)				\
115275970Scy	name##_fn = GetProcAddress(lib, #name)
116275970Scy	LOAD(InitializeConditionVariable);
117275970Scy	LOAD(SleepConditionVariableCS);
118275970Scy	LOAD(WakeAllConditionVariable);
119275970Scy	LOAD(WakeConditionVariable);
120275970Scy
121275970Scy	return InitializeConditionVariable_fn && SleepConditionVariableCS_fn &&
122275970Scy	    WakeAllConditionVariable_fn && WakeConditionVariable_fn;
123275970Scy}
124275970Scy
125275970Scy/* XXXX Even if we can build this, we don't necessarily want to: the functions
126275970Scy * in question didn't exist before Vista, so we'd better LoadProc them. */
127275970Scystatic void *
128275970Scyevthread_win32_condvar_alloc(unsigned condflags)
129275970Scy{
130275970Scy	CONDITION_VARIABLE *cond = mm_malloc(sizeof(CONDITION_VARIABLE));
131275970Scy	if (!cond)
132275970Scy		return NULL;
133275970Scy	InitializeConditionVariable_fn(cond);
134275970Scy	return cond;
135275970Scy}
136275970Scy
137275970Scystatic void
138275970Scyevthread_win32_condvar_free(void *cond_)
139275970Scy{
140275970Scy	CONDITION_VARIABLE *cond = cond_;
141275970Scy	/* There doesn't _seem_ to be a cleaup fn here... */
142275970Scy	mm_free(cond);
143275970Scy}
144275970Scy
145275970Scystatic int
146275970Scyevthread_win32_condvar_signal(void *cond, int broadcast)
147275970Scy{
148275970Scy	CONDITION_VARIABLE *cond = cond_;
149275970Scy	if (broadcast)
150275970Scy		WakeAllConditionVariable_fn(cond);
151275970Scy	else
152275970Scy		WakeConditionVariable_fn(cond);
153275970Scy	return 0;
154275970Scy}
155275970Scy
156275970Scystatic int
157275970Scyevthread_win32_condvar_wait(void *cond_, void *lock_, const struct timeval *tv)
158275970Scy{
159275970Scy	CONDITION_VARIABLE *cond = cond_;
160275970Scy	CRITICAL_SECTION *lock = lock_;
161275970Scy	DWORD ms, err;
162275970Scy	BOOL result;
163275970Scy
164275970Scy	if (tv)
165275970Scy		ms = evutil_tv_to_msec_(tv);
166275970Scy	else
167275970Scy		ms = INFINITE;
168275970Scy	result = SleepConditionVariableCS_fn(cond, lock, ms);
169275970Scy	if (result) {
170275970Scy		if (GetLastError() == WAIT_TIMEOUT)
171275970Scy			return 1;
172275970Scy		else
173275970Scy			return -1;
174275970Scy	} else {
175275970Scy		return 0;
176275970Scy	}
177275970Scy}
178275970Scy#endif
179275970Scy
180275970Scystruct evthread_win32_cond {
181275970Scy	HANDLE event;
182275970Scy
183275970Scy	CRITICAL_SECTION lock;
184275970Scy	int n_waiting;
185275970Scy	int n_to_wake;
186275970Scy	int generation;
187275970Scy};
188275970Scy
189275970Scystatic void *
190275970Scyevthread_win32_cond_alloc(unsigned flags)
191275970Scy{
192275970Scy	struct evthread_win32_cond *cond;
193275970Scy	if (!(cond = mm_malloc(sizeof(struct evthread_win32_cond))))
194275970Scy		return NULL;
195275970Scy	if (InitializeCriticalSectionAndSpinCount(&cond->lock, SPIN_COUNT)==0) {
196275970Scy		mm_free(cond);
197275970Scy		return NULL;
198275970Scy	}
199275970Scy	if ((cond->event = CreateEvent(NULL,TRUE,FALSE,NULL)) == NULL) {
200275970Scy		DeleteCriticalSection(&cond->lock);
201275970Scy		mm_free(cond);
202275970Scy		return NULL;
203275970Scy	}
204275970Scy	cond->n_waiting = cond->n_to_wake = cond->generation = 0;
205275970Scy	return cond;
206275970Scy}
207275970Scy
208275970Scystatic void
209275970Scyevthread_win32_cond_free(void *cond_)
210275970Scy{
211275970Scy	struct evthread_win32_cond *cond = cond_;
212275970Scy	DeleteCriticalSection(&cond->lock);
213275970Scy	CloseHandle(cond->event);
214275970Scy	mm_free(cond);
215275970Scy}
216275970Scy
217275970Scystatic int
218275970Scyevthread_win32_cond_signal(void *cond_, int broadcast)
219275970Scy{
220275970Scy	struct evthread_win32_cond *cond = cond_;
221275970Scy	EnterCriticalSection(&cond->lock);
222275970Scy	if (broadcast)
223275970Scy		cond->n_to_wake = cond->n_waiting;
224275970Scy	else
225275970Scy		++cond->n_to_wake;
226275970Scy	cond->generation++;
227275970Scy	SetEvent(cond->event);
228275970Scy	LeaveCriticalSection(&cond->lock);
229275970Scy	return 0;
230275970Scy}
231275970Scy
232275970Scystatic int
233275970Scyevthread_win32_cond_wait(void *cond_, void *lock_, const struct timeval *tv)
234275970Scy{
235275970Scy	struct evthread_win32_cond *cond = cond_;
236275970Scy	CRITICAL_SECTION *lock = lock_;
237275970Scy	int generation_at_start;
238275970Scy	int waiting = 1;
239275970Scy	int result = -1;
240275970Scy	DWORD ms = INFINITE, ms_orig = INFINITE, startTime, endTime;
241275970Scy	if (tv)
242275970Scy		ms_orig = ms = evutil_tv_to_msec_(tv);
243275970Scy
244275970Scy	EnterCriticalSection(&cond->lock);
245275970Scy	++cond->n_waiting;
246275970Scy	generation_at_start = cond->generation;
247275970Scy	LeaveCriticalSection(&cond->lock);
248275970Scy
249275970Scy	LeaveCriticalSection(lock);
250275970Scy
251275970Scy	startTime = GetTickCount();
252275970Scy	do {
253275970Scy		DWORD res;
254275970Scy		res = WaitForSingleObject(cond->event, ms);
255275970Scy		EnterCriticalSection(&cond->lock);
256275970Scy		if (cond->n_to_wake &&
257275970Scy		    cond->generation != generation_at_start) {
258275970Scy			--cond->n_to_wake;
259275970Scy			--cond->n_waiting;
260275970Scy			result = 0;
261275970Scy			waiting = 0;
262275970Scy			goto out;
263275970Scy		} else if (res != WAIT_OBJECT_0) {
264275970Scy			result = (res==WAIT_TIMEOUT) ? 1 : -1;
265275970Scy			--cond->n_waiting;
266275970Scy			waiting = 0;
267275970Scy			goto out;
268275970Scy		} else if (ms != INFINITE) {
269275970Scy			endTime = GetTickCount();
270275970Scy			if (startTime + ms_orig <= endTime) {
271275970Scy				result = 1; /* Timeout */
272275970Scy				--cond->n_waiting;
273275970Scy				waiting = 0;
274275970Scy				goto out;
275275970Scy			} else {
276275970Scy				ms = startTime + ms_orig - endTime;
277275970Scy			}
278275970Scy		}
279275970Scy		/* If we make it here, we are still waiting. */
280275970Scy		if (cond->n_to_wake == 0) {
281275970Scy			/* There is nobody else who should wake up; reset
282275970Scy			 * the event. */
283275970Scy			ResetEvent(cond->event);
284275970Scy		}
285275970Scy	out:
286275970Scy		LeaveCriticalSection(&cond->lock);
287275970Scy	} while (waiting);
288275970Scy
289275970Scy	EnterCriticalSection(lock);
290275970Scy
291275970Scy	EnterCriticalSection(&cond->lock);
292275970Scy	if (!cond->n_waiting)
293275970Scy		ResetEvent(cond->event);
294275970Scy	LeaveCriticalSection(&cond->lock);
295275970Scy
296275970Scy	return result;
297275970Scy}
298275970Scy
299275970Scyint
300275970Scyevthread_use_windows_threads(void)
301275970Scy{
302275970Scy	struct evthread_lock_callbacks cbs = {
303275970Scy		EVTHREAD_LOCK_API_VERSION,
304275970Scy		EVTHREAD_LOCKTYPE_RECURSIVE,
305275970Scy		evthread_win32_lock_create,
306275970Scy		evthread_win32_lock_free,
307275970Scy		evthread_win32_lock,
308275970Scy		evthread_win32_unlock
309275970Scy	};
310275970Scy
311275970Scy
312275970Scy	struct evthread_condition_callbacks cond_cbs = {
313275970Scy		EVTHREAD_CONDITION_API_VERSION,
314275970Scy		evthread_win32_cond_alloc,
315275970Scy		evthread_win32_cond_free,
316275970Scy		evthread_win32_cond_signal,
317275970Scy		evthread_win32_cond_wait
318275970Scy	};
319275970Scy#ifdef WIN32_HAVE_CONDITION_VARIABLES
320275970Scy	struct evthread_condition_callbacks condvar_cbs = {
321275970Scy		EVTHREAD_CONDITION_API_VERSION,
322275970Scy		evthread_win32_condvar_alloc,
323275970Scy		evthread_win32_condvar_free,
324275970Scy		evthread_win32_condvar_signal,
325275970Scy		evthread_win32_condvar_wait
326275970Scy	};
327275970Scy#endif
328275970Scy
329275970Scy	evthread_set_lock_callbacks(&cbs);
330275970Scy	evthread_set_id_callback(evthread_win32_get_id);
331275970Scy#ifdef WIN32_HAVE_CONDITION_VARIABLES
332275970Scy	if (evthread_win32_condvar_init()) {
333275970Scy		evthread_set_condition_callbacks(&condvar_cbs);
334275970Scy		return 0;
335275970Scy	}
336275970Scy#endif
337275970Scy	evthread_set_condition_callbacks(&cond_cbs);
338275970Scy
339275970Scy	return 0;
340275970Scy}
341275970Scy
342