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