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