]> arthur.barton.de Git - netatalk.git/blob - libevent/evthread_win32.c
Add libevent
[netatalk.git] / libevent / evthread_win32.c
1 /*
2  * Copyright 2009-2010 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
28 #ifdef WIN32
29 #include <winsock2.h>
30 #define WIN32_LEAN_AND_MEAN
31 #include <windows.h>
32 #undef WIN32_LEAN_AND_MEAN
33 #include <sys/locking.h>
34 #endif
35
36 struct event_base;
37 #include "event2/thread.h"
38
39 #include "mm-internal.h"
40 #include "evthread-internal.h"
41
42 #define SPIN_COUNT 2000
43
44 static void *
45 evthread_win32_lock_create(unsigned locktype)
46 {
47         CRITICAL_SECTION *lock = mm_malloc(sizeof(CRITICAL_SECTION));
48         if (!lock)
49                 return NULL;
50         if (InitializeCriticalSectionAndSpinCount(lock, SPIN_COUNT) == 0) {
51                 mm_free(lock);
52                 return NULL;
53         }
54         return lock;
55 }
56
57 static void
58 evthread_win32_lock_free(void *_lock, unsigned locktype)
59 {
60         CRITICAL_SECTION *lock = _lock;
61         DeleteCriticalSection(lock);
62         mm_free(lock);
63 }
64
65 static int
66 evthread_win32_lock(unsigned mode, void *_lock)
67 {
68         CRITICAL_SECTION *lock = _lock;
69         if ((mode & EVTHREAD_TRY)) {
70                 return ! TryEnterCriticalSection(lock);
71         } else {
72                 EnterCriticalSection(lock);
73                 return 0;
74         }
75 }
76
77 static int
78 evthread_win32_unlock(unsigned mode, void *_lock)
79 {
80         CRITICAL_SECTION *lock = _lock;
81         LeaveCriticalSection(lock);
82         return 0;
83 }
84
85 static unsigned long
86 evthread_win32_get_id(void)
87 {
88         return (unsigned long) GetCurrentThreadId();
89 }
90
91 #ifdef WIN32_HAVE_CONDITION_VARIABLES
92 static void WINAPI (*InitializeConditionVariable_fn)(PCONDITION_VARIABLE)
93         = NULL;
94 static BOOL WINAPI (*SleepConditionVariableCS_fn)(
95         PCONDITION_VARIABLE, PCRITICAL_SECTION, DWORD) = NULL;
96 static void WINAPI (*WakeAllConditionVariable_fn)(PCONDITION_VARIABLE) = NULL;
97 static void WINAPI (*WakeConditionVariable_fn)(PCONDITION_VARIABLE) = NULL;
98
99 static int
100 evthread_win32_condvar_init(void)
101 {
102         HANDLE lib;
103
104         lib = GetModuleHandle(TEXT("kernel32.dll"));
105         if (lib == NULL)
106                 return 0;
107
108 #define LOAD(name)                              \
109         name##_fn = GetProcAddress(lib, #name)
110         LOAD(InitializeConditionVariable);
111         LOAD(SleepConditionVariable);
112         LOAD(WakeAllConditionVariable);
113         LOAD(WakeConditionVariable);
114
115         return InitializeConditionVariable_fn && SleepConditionVariableCS_fn &&
116             WakeAllConditionVariable_fn && WakeConditionVariable_fn;
117 }
118
119 /* XXXX Even if we can build this, we don't necessarily want to: the functions
120  * in question didn't exist before Vista, so we'd better LoadProc them. */
121 static void *
122 evthread_win32_condvar_alloc(unsigned condflags)
123 {
124         CONDITION_VARIABLE *cond = mm_malloc(sizeof(CONDITION_VARIABLE));
125         if (!cond)
126                 return NULL;
127         InitializeConditionVariable_fn(cond);
128         return cond;
129 }
130
131 static void
132 evthread_win32_condvar_free(void *_cond)
133 {
134         CONDITION_VARIABLE *cond = _cond;
135         /* There doesn't _seem_ to be a cleaup fn here... */
136         mm_free(cond);
137 }
138
139 static int
140 evthread_win32_condvar_signal(void *_cond, int broadcast)
141 {
142         CONDITION_VARIABLE *cond = _cond;
143         if (broadcast)
144                 WakeAllConditionVariable_fn(cond);
145         else
146                 WakeConditionVariable_fn(cond);
147         return 0;
148 }
149
150 static int
151 evthread_win32_condvar_wait(void *_cond, void *_lock, const struct timeval *tv)
152 {
153         CONDITION_VARIABLE *cond = _cond;
154         CRITICAL_SECTION *lock = _lock;
155         DWORD ms, err;
156         BOOL result;
157
158         if (tv)
159                 ms = evutil_tv_to_msec(tv);
160         else
161                 ms = INFINITE;
162         result = SleepConditionVariableCS_fn(cond, lock, ms);
163         if (result) {
164                 if (GetLastError() == WAIT_TIMEOUT)
165                         return 1;
166                 else
167                         return -1;
168         } else {
169                 return 0;
170         }
171 }
172 #endif
173
174 struct evthread_win32_cond {
175         HANDLE event;
176
177         CRITICAL_SECTION lock;
178         int n_waiting;
179         int n_to_wake;
180         int generation;
181 };
182
183 static void *
184 evthread_win32_cond_alloc(unsigned flags)
185 {
186         struct evthread_win32_cond *cond;
187         if (!(cond = mm_malloc(sizeof(struct evthread_win32_cond))))
188                 return NULL;
189         if (InitializeCriticalSectionAndSpinCount(&cond->lock, SPIN_COUNT)==0) {
190                 mm_free(cond);
191                 return NULL;
192         }
193         if ((cond->event = CreateEvent(NULL,TRUE,FALSE,NULL)) == NULL) {
194                 DeleteCriticalSection(&cond->lock);
195                 mm_free(cond);
196                 return NULL;
197         }
198         cond->n_waiting = cond->n_to_wake = cond->generation = 0;
199         return cond;
200 }
201
202 static void
203 evthread_win32_cond_free(void *_cond)
204 {
205         struct evthread_win32_cond *cond = _cond;
206         DeleteCriticalSection(&cond->lock);
207         CloseHandle(cond->event);
208         mm_free(cond);
209 }
210
211 static int
212 evthread_win32_cond_signal(void *_cond, int broadcast)
213 {
214         struct evthread_win32_cond *cond = _cond;
215         EnterCriticalSection(&cond->lock);
216         if (broadcast)
217                 cond->n_to_wake = cond->n_waiting;
218         else
219                 ++cond->n_to_wake;
220         cond->generation++;
221         SetEvent(cond->event);
222         LeaveCriticalSection(&cond->lock);
223         return 0;
224 }
225
226 static int
227 evthread_win32_cond_wait(void *_cond, void *_lock, const struct timeval *tv)
228 {
229         struct evthread_win32_cond *cond = _cond;
230         CRITICAL_SECTION *lock = _lock;
231         int generation_at_start;
232         int waiting = 1;
233         int result = -1;
234         DWORD ms = INFINITE, ms_orig = INFINITE, startTime, endTime;
235         if (tv)
236                 ms_orig = ms = evutil_tv_to_msec(tv);
237
238         EnterCriticalSection(&cond->lock);
239         ++cond->n_waiting;
240         generation_at_start = cond->generation;
241         LeaveCriticalSection(&cond->lock);
242
243         LeaveCriticalSection(lock);
244
245         startTime = GetTickCount();
246         do {
247                 DWORD res;
248                 res = WaitForSingleObject(cond->event, ms);
249                 EnterCriticalSection(&cond->lock);
250                 if (cond->n_to_wake &&
251                     cond->generation != generation_at_start) {
252                         --cond->n_to_wake;
253                         --cond->n_waiting;
254                         result = 0;
255                         waiting = 0;
256                         goto out;
257                 } else if (res != WAIT_OBJECT_0) {
258                         result = (res==WAIT_TIMEOUT) ? 1 : -1;
259                         --cond->n_waiting;
260                         waiting = 0;
261                         goto out;
262                 } else if (ms != INFINITE) {
263                         endTime = GetTickCount();
264                         if (startTime + ms_orig <= endTime) {
265                                 result = 1; /* Timeout */
266                                 --cond->n_waiting;
267                                 waiting = 0;
268                                 goto out;
269                         } else {
270                                 ms = startTime + ms_orig - endTime;
271                         }
272                 }
273                 /* If we make it here, we are still waiting. */
274                 if (cond->n_to_wake == 0) {
275                         /* There is nobody else who should wake up; reset
276                          * the event. */
277                         ResetEvent(cond->event);
278                 }
279         out:
280                 LeaveCriticalSection(&cond->lock);
281         } while (waiting);
282
283         EnterCriticalSection(lock);
284
285         EnterCriticalSection(&cond->lock);
286         if (!cond->n_waiting)
287                 ResetEvent(cond->event);
288         LeaveCriticalSection(&cond->lock);
289
290         return result;
291 }
292
293 int
294 evthread_use_windows_threads(void)
295 {
296         struct evthread_lock_callbacks cbs = {
297                 EVTHREAD_LOCK_API_VERSION,
298                 EVTHREAD_LOCKTYPE_RECURSIVE,
299                 evthread_win32_lock_create,
300                 evthread_win32_lock_free,
301                 evthread_win32_lock,
302                 evthread_win32_unlock
303         };
304
305
306         struct evthread_condition_callbacks cond_cbs = {
307                 EVTHREAD_CONDITION_API_VERSION,
308                 evthread_win32_cond_alloc,
309                 evthread_win32_cond_free,
310                 evthread_win32_cond_signal,
311                 evthread_win32_cond_wait
312         };
313 #ifdef WIN32_HAVE_CONDITION_VARIABLES
314         struct evthread_condition_callbacks condvar_cbs = {
315                 EVTHREAD_CONDITION_API_VERSION,
316                 evthread_win32_condvar_alloc,
317                 evthread_win32_condvar_free,
318                 evthread_win32_condvar_signal,
319                 evthread_win32_condvar_wait
320         };
321 #endif
322
323         evthread_set_lock_callbacks(&cbs);
324         evthread_set_id_callback(evthread_win32_get_id);
325 #ifdef WIN32_HAVE_CONDITION_VARIABLES
326         if (evthread_win32_condvar_init()) {
327                 evthread_set_condition_callbacks(&condvar_cbs);
328                 return 0;
329         }
330 #endif
331         evthread_set_condition_callbacks(&cond_cbs);
332
333         return 0;
334 }
335