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