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