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