|
1 /* |
|
2 * Copyright (c) 2008-2012 Niels Provos, 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 |
|
27 #include "event2/event-config.h" |
|
28 |
|
29 #ifndef _EVENT_DISABLE_THREAD_SUPPORT |
|
30 |
|
31 #include "event2/thread.h" |
|
32 |
|
33 #include <stdlib.h> |
|
34 #include <string.h> |
|
35 |
|
36 #include "log-internal.h" |
|
37 #include "mm-internal.h" |
|
38 #include "util-internal.h" |
|
39 #include "evthread-internal.h" |
|
40 |
|
41 #ifdef EVTHREAD_EXPOSE_STRUCTS |
|
42 #define GLOBAL |
|
43 #else |
|
44 #define GLOBAL static |
|
45 #endif |
|
46 |
|
47 /* globals */ |
|
48 GLOBAL int _evthread_lock_debugging_enabled = 0; |
|
49 GLOBAL struct evthread_lock_callbacks _evthread_lock_fns = { |
|
50 0, 0, NULL, NULL, NULL, NULL |
|
51 }; |
|
52 GLOBAL unsigned long (*_evthread_id_fn)(void) = NULL; |
|
53 GLOBAL struct evthread_condition_callbacks _evthread_cond_fns = { |
|
54 0, NULL, NULL, NULL, NULL |
|
55 }; |
|
56 |
|
57 /* Used for debugging */ |
|
58 static struct evthread_lock_callbacks _original_lock_fns = { |
|
59 0, 0, NULL, NULL, NULL, NULL |
|
60 }; |
|
61 static struct evthread_condition_callbacks _original_cond_fns = { |
|
62 0, NULL, NULL, NULL, NULL |
|
63 }; |
|
64 |
|
65 void |
|
66 evthread_set_id_callback(unsigned long (*id_fn)(void)) |
|
67 { |
|
68 _evthread_id_fn = id_fn; |
|
69 } |
|
70 |
|
71 int |
|
72 evthread_set_lock_callbacks(const struct evthread_lock_callbacks *cbs) |
|
73 { |
|
74 struct evthread_lock_callbacks *target = |
|
75 _evthread_lock_debugging_enabled |
|
76 ? &_original_lock_fns : &_evthread_lock_fns; |
|
77 |
|
78 if (!cbs) { |
|
79 if (target->alloc) |
|
80 event_warnx("Trying to disable lock functions after " |
|
81 "they have been set up will probaby not work."); |
|
82 memset(target, 0, sizeof(_evthread_lock_fns)); |
|
83 return 0; |
|
84 } |
|
85 if (target->alloc) { |
|
86 /* Uh oh; we already had locking callbacks set up.*/ |
|
87 if (target->lock_api_version == cbs->lock_api_version && |
|
88 target->supported_locktypes == cbs->supported_locktypes && |
|
89 target->alloc == cbs->alloc && |
|
90 target->free == cbs->free && |
|
91 target->lock == cbs->lock && |
|
92 target->unlock == cbs->unlock) { |
|
93 /* no change -- allow this. */ |
|
94 return 0; |
|
95 } |
|
96 event_warnx("Can't change lock callbacks once they have been " |
|
97 "initialized."); |
|
98 return -1; |
|
99 } |
|
100 if (cbs->alloc && cbs->free && cbs->lock && cbs->unlock) { |
|
101 memcpy(target, cbs, sizeof(_evthread_lock_fns)); |
|
102 return event_global_setup_locks_(1); |
|
103 } else { |
|
104 return -1; |
|
105 } |
|
106 } |
|
107 |
|
108 int |
|
109 evthread_set_condition_callbacks(const struct evthread_condition_callbacks *cbs) |
|
110 { |
|
111 struct evthread_condition_callbacks *target = |
|
112 _evthread_lock_debugging_enabled |
|
113 ? &_original_cond_fns : &_evthread_cond_fns; |
|
114 |
|
115 if (!cbs) { |
|
116 if (target->alloc_condition) |
|
117 event_warnx("Trying to disable condition functions " |
|
118 "after they have been set up will probaby not " |
|
119 "work."); |
|
120 memset(target, 0, sizeof(_evthread_cond_fns)); |
|
121 return 0; |
|
122 } |
|
123 if (target->alloc_condition) { |
|
124 /* Uh oh; we already had condition callbacks set up.*/ |
|
125 if (target->condition_api_version == cbs->condition_api_version && |
|
126 target->alloc_condition == cbs->alloc_condition && |
|
127 target->free_condition == cbs->free_condition && |
|
128 target->signal_condition == cbs->signal_condition && |
|
129 target->wait_condition == cbs->wait_condition) { |
|
130 /* no change -- allow this. */ |
|
131 return 0; |
|
132 } |
|
133 event_warnx("Can't change condition callbacks once they " |
|
134 "have been initialized."); |
|
135 return -1; |
|
136 } |
|
137 if (cbs->alloc_condition && cbs->free_condition && |
|
138 cbs->signal_condition && cbs->wait_condition) { |
|
139 memcpy(target, cbs, sizeof(_evthread_cond_fns)); |
|
140 } |
|
141 if (_evthread_lock_debugging_enabled) { |
|
142 _evthread_cond_fns.alloc_condition = cbs->alloc_condition; |
|
143 _evthread_cond_fns.free_condition = cbs->free_condition; |
|
144 _evthread_cond_fns.signal_condition = cbs->signal_condition; |
|
145 } |
|
146 return 0; |
|
147 } |
|
148 |
|
149 struct debug_lock { |
|
150 unsigned locktype; |
|
151 unsigned long held_by; |
|
152 /* XXXX if we ever use read-write locks, we will need a separate |
|
153 * lock to protect count. */ |
|
154 int count; |
|
155 void *lock; |
|
156 }; |
|
157 |
|
158 static void * |
|
159 debug_lock_alloc(unsigned locktype) |
|
160 { |
|
161 struct debug_lock *result = mm_malloc(sizeof(struct debug_lock)); |
|
162 if (!result) |
|
163 return NULL; |
|
164 if (_original_lock_fns.alloc) { |
|
165 if (!(result->lock = _original_lock_fns.alloc( |
|
166 locktype|EVTHREAD_LOCKTYPE_RECURSIVE))) { |
|
167 mm_free(result); |
|
168 return NULL; |
|
169 } |
|
170 } else { |
|
171 result->lock = NULL; |
|
172 } |
|
173 result->locktype = locktype; |
|
174 result->count = 0; |
|
175 result->held_by = 0; |
|
176 return result; |
|
177 } |
|
178 |
|
179 static void |
|
180 debug_lock_free(void *lock_, unsigned locktype) |
|
181 { |
|
182 struct debug_lock *lock = lock_; |
|
183 EVUTIL_ASSERT(lock->count == 0); |
|
184 EVUTIL_ASSERT(locktype == lock->locktype); |
|
185 if (_original_lock_fns.free) { |
|
186 _original_lock_fns.free(lock->lock, |
|
187 lock->locktype|EVTHREAD_LOCKTYPE_RECURSIVE); |
|
188 } |
|
189 lock->lock = NULL; |
|
190 lock->count = -100; |
|
191 mm_free(lock); |
|
192 } |
|
193 |
|
194 static void |
|
195 evthread_debug_lock_mark_locked(unsigned mode, struct debug_lock *lock) |
|
196 { |
|
197 ++lock->count; |
|
198 if (!(lock->locktype & EVTHREAD_LOCKTYPE_RECURSIVE)) |
|
199 EVUTIL_ASSERT(lock->count == 1); |
|
200 if (_evthread_id_fn) { |
|
201 unsigned long me; |
|
202 me = _evthread_id_fn(); |
|
203 if (lock->count > 1) |
|
204 EVUTIL_ASSERT(lock->held_by == me); |
|
205 lock->held_by = me; |
|
206 } |
|
207 } |
|
208 |
|
209 static int |
|
210 debug_lock_lock(unsigned mode, void *lock_) |
|
211 { |
|
212 struct debug_lock *lock = lock_; |
|
213 int res = 0; |
|
214 if (lock->locktype & EVTHREAD_LOCKTYPE_READWRITE) |
|
215 EVUTIL_ASSERT(mode & (EVTHREAD_READ|EVTHREAD_WRITE)); |
|
216 else |
|
217 EVUTIL_ASSERT((mode & (EVTHREAD_READ|EVTHREAD_WRITE)) == 0); |
|
218 if (_original_lock_fns.lock) |
|
219 res = _original_lock_fns.lock(mode, lock->lock); |
|
220 if (!res) { |
|
221 evthread_debug_lock_mark_locked(mode, lock); |
|
222 } |
|
223 return res; |
|
224 } |
|
225 |
|
226 static void |
|
227 evthread_debug_lock_mark_unlocked(unsigned mode, struct debug_lock *lock) |
|
228 { |
|
229 if (lock->locktype & EVTHREAD_LOCKTYPE_READWRITE) |
|
230 EVUTIL_ASSERT(mode & (EVTHREAD_READ|EVTHREAD_WRITE)); |
|
231 else |
|
232 EVUTIL_ASSERT((mode & (EVTHREAD_READ|EVTHREAD_WRITE)) == 0); |
|
233 if (_evthread_id_fn) { |
|
234 EVUTIL_ASSERT(lock->held_by == _evthread_id_fn()); |
|
235 if (lock->count == 1) |
|
236 lock->held_by = 0; |
|
237 } |
|
238 --lock->count; |
|
239 EVUTIL_ASSERT(lock->count >= 0); |
|
240 } |
|
241 |
|
242 static int |
|
243 debug_lock_unlock(unsigned mode, void *lock_) |
|
244 { |
|
245 struct debug_lock *lock = lock_; |
|
246 int res = 0; |
|
247 evthread_debug_lock_mark_unlocked(mode, lock); |
|
248 if (_original_lock_fns.unlock) |
|
249 res = _original_lock_fns.unlock(mode, lock->lock); |
|
250 return res; |
|
251 } |
|
252 |
|
253 static int |
|
254 debug_cond_wait(void *_cond, void *_lock, const struct timeval *tv) |
|
255 { |
|
256 int r; |
|
257 struct debug_lock *lock = _lock; |
|
258 EVUTIL_ASSERT(lock); |
|
259 EVLOCK_ASSERT_LOCKED(_lock); |
|
260 evthread_debug_lock_mark_unlocked(0, lock); |
|
261 r = _original_cond_fns.wait_condition(_cond, lock->lock, tv); |
|
262 evthread_debug_lock_mark_locked(0, lock); |
|
263 return r; |
|
264 } |
|
265 |
|
266 void |
|
267 evthread_enable_lock_debuging(void) |
|
268 { |
|
269 struct evthread_lock_callbacks cbs = { |
|
270 EVTHREAD_LOCK_API_VERSION, |
|
271 EVTHREAD_LOCKTYPE_RECURSIVE, |
|
272 debug_lock_alloc, |
|
273 debug_lock_free, |
|
274 debug_lock_lock, |
|
275 debug_lock_unlock |
|
276 }; |
|
277 if (_evthread_lock_debugging_enabled) |
|
278 return; |
|
279 memcpy(&_original_lock_fns, &_evthread_lock_fns, |
|
280 sizeof(struct evthread_lock_callbacks)); |
|
281 memcpy(&_evthread_lock_fns, &cbs, |
|
282 sizeof(struct evthread_lock_callbacks)); |
|
283 |
|
284 memcpy(&_original_cond_fns, &_evthread_cond_fns, |
|
285 sizeof(struct evthread_condition_callbacks)); |
|
286 _evthread_cond_fns.wait_condition = debug_cond_wait; |
|
287 _evthread_lock_debugging_enabled = 1; |
|
288 |
|
289 /* XXX return value should get checked. */ |
|
290 event_global_setup_locks_(0); |
|
291 } |
|
292 |
|
293 int |
|
294 _evthread_is_debug_lock_held(void *lock_) |
|
295 { |
|
296 struct debug_lock *lock = lock_; |
|
297 if (! lock->count) |
|
298 return 0; |
|
299 if (_evthread_id_fn) { |
|
300 unsigned long me = _evthread_id_fn(); |
|
301 if (lock->held_by != me) |
|
302 return 0; |
|
303 } |
|
304 return 1; |
|
305 } |
|
306 |
|
307 void * |
|
308 _evthread_debug_get_real_lock(void *lock_) |
|
309 { |
|
310 struct debug_lock *lock = lock_; |
|
311 return lock->lock; |
|
312 } |
|
313 |
|
314 void * |
|
315 evthread_setup_global_lock_(void *lock_, unsigned locktype, int enable_locks) |
|
316 { |
|
317 /* there are four cases here: |
|
318 1) we're turning on debugging; locking is not on. |
|
319 2) we're turning on debugging; locking is on. |
|
320 3) we're turning on locking; debugging is not on. |
|
321 4) we're turning on locking; debugging is on. */ |
|
322 |
|
323 if (!enable_locks && _original_lock_fns.alloc == NULL) { |
|
324 /* Case 1: allocate a debug lock. */ |
|
325 EVUTIL_ASSERT(lock_ == NULL); |
|
326 return debug_lock_alloc(locktype); |
|
327 } else if (!enable_locks && _original_lock_fns.alloc != NULL) { |
|
328 /* Case 2: wrap the lock in a debug lock. */ |
|
329 struct debug_lock *lock; |
|
330 EVUTIL_ASSERT(lock_ != NULL); |
|
331 |
|
332 if (!(locktype & EVTHREAD_LOCKTYPE_RECURSIVE)) { |
|
333 /* We can't wrap it: We need a recursive lock */ |
|
334 _original_lock_fns.free(lock_, locktype); |
|
335 return debug_lock_alloc(locktype); |
|
336 } |
|
337 lock = mm_malloc(sizeof(struct debug_lock)); |
|
338 if (!lock) { |
|
339 _original_lock_fns.free(lock_, locktype); |
|
340 return NULL; |
|
341 } |
|
342 lock->lock = lock_; |
|
343 lock->locktype = locktype; |
|
344 lock->count = 0; |
|
345 lock->held_by = 0; |
|
346 return lock; |
|
347 } else if (enable_locks && ! _evthread_lock_debugging_enabled) { |
|
348 /* Case 3: allocate a regular lock */ |
|
349 EVUTIL_ASSERT(lock_ == NULL); |
|
350 return _evthread_lock_fns.alloc(locktype); |
|
351 } else { |
|
352 /* Case 4: Fill in a debug lock with a real lock */ |
|
353 struct debug_lock *lock = lock_; |
|
354 EVUTIL_ASSERT(enable_locks && |
|
355 _evthread_lock_debugging_enabled); |
|
356 EVUTIL_ASSERT(lock->locktype == locktype); |
|
357 EVUTIL_ASSERT(lock->lock == NULL); |
|
358 lock->lock = _original_lock_fns.alloc( |
|
359 locktype|EVTHREAD_LOCKTYPE_RECURSIVE); |
|
360 if (!lock->lock) { |
|
361 lock->count = -200; |
|
362 mm_free(lock); |
|
363 return NULL; |
|
364 } |
|
365 return lock; |
|
366 } |
|
367 } |
|
368 |
|
369 |
|
370 #ifndef EVTHREAD_EXPOSE_STRUCTS |
|
371 unsigned long |
|
372 _evthreadimpl_get_id() |
|
373 { |
|
374 return _evthread_id_fn ? _evthread_id_fn() : 1; |
|
375 } |
|
376 void * |
|
377 _evthreadimpl_lock_alloc(unsigned locktype) |
|
378 { |
|
379 return _evthread_lock_fns.alloc ? |
|
380 _evthread_lock_fns.alloc(locktype) : NULL; |
|
381 } |
|
382 void |
|
383 _evthreadimpl_lock_free(void *lock, unsigned locktype) |
|
384 { |
|
385 if (_evthread_lock_fns.free) |
|
386 _evthread_lock_fns.free(lock, locktype); |
|
387 } |
|
388 int |
|
389 _evthreadimpl_lock_lock(unsigned mode, void *lock) |
|
390 { |
|
391 if (_evthread_lock_fns.lock) |
|
392 return _evthread_lock_fns.lock(mode, lock); |
|
393 else |
|
394 return 0; |
|
395 } |
|
396 int |
|
397 _evthreadimpl_lock_unlock(unsigned mode, void *lock) |
|
398 { |
|
399 if (_evthread_lock_fns.unlock) |
|
400 return _evthread_lock_fns.unlock(mode, lock); |
|
401 else |
|
402 return 0; |
|
403 } |
|
404 void * |
|
405 _evthreadimpl_cond_alloc(unsigned condtype) |
|
406 { |
|
407 return _evthread_cond_fns.alloc_condition ? |
|
408 _evthread_cond_fns.alloc_condition(condtype) : NULL; |
|
409 } |
|
410 void |
|
411 _evthreadimpl_cond_free(void *cond) |
|
412 { |
|
413 if (_evthread_cond_fns.free_condition) |
|
414 _evthread_cond_fns.free_condition(cond); |
|
415 } |
|
416 int |
|
417 _evthreadimpl_cond_signal(void *cond, int broadcast) |
|
418 { |
|
419 if (_evthread_cond_fns.signal_condition) |
|
420 return _evthread_cond_fns.signal_condition(cond, broadcast); |
|
421 else |
|
422 return 0; |
|
423 } |
|
424 int |
|
425 _evthreadimpl_cond_wait(void *cond, void *lock, const struct timeval *tv) |
|
426 { |
|
427 if (_evthread_cond_fns.wait_condition) |
|
428 return _evthread_cond_fns.wait_condition(cond, lock, tv); |
|
429 else |
|
430 return 0; |
|
431 } |
|
432 int |
|
433 _evthreadimpl_is_lock_debugging_enabled(void) |
|
434 { |
|
435 return _evthread_lock_debugging_enabled; |
|
436 } |
|
437 |
|
438 int |
|
439 _evthreadimpl_locking_enabled(void) |
|
440 { |
|
441 return _evthread_lock_fns.lock != NULL; |
|
442 } |
|
443 #endif |
|
444 |
|
445 #endif |