evthread-internal.h revision 2a572d125a91a4aafd3ad8ce87259fc640fa0763
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#ifndef EVTHREAD_INTERNAL_H_INCLUDED_ 27#define EVTHREAD_INTERNAL_H_INCLUDED_ 28 29#ifdef __cplusplus 30extern "C" { 31#endif 32 33#include "event2/event-config.h" 34#include "evconfig-private.h" 35 36#include "event2/thread.h" 37#include "util-internal.h" 38 39struct event_base; 40 41#ifndef _WIN32 42/* On Windows, the way we currently make DLLs, it's not allowed for us to 43 * have shared global structures. Thus, we only do the direct-call-to-function 44 * code path if we know that the local shared library system supports it. 45 */ 46#define EVTHREAD_EXPOSE_STRUCTS 47#endif 48 49#if ! defined(EVENT__DISABLE_THREAD_SUPPORT) && defined(EVTHREAD_EXPOSE_STRUCTS) 50/* Global function pointers to lock-related functions. NULL if locking isn't 51 enabled. */ 52extern struct evthread_lock_callbacks evthread_lock_fns_; 53extern struct evthread_condition_callbacks evthread_cond_fns_; 54extern unsigned long (*evthread_id_fn_)(void); 55extern int evthread_lock_debugging_enabled_; 56 57/** Return the ID of the current thread, or 1 if threading isn't enabled. */ 58#define EVTHREAD_GET_ID() \ 59 (evthread_id_fn_ ? evthread_id_fn_() : 1) 60 61/** Return true iff we're in the thread that is currently (or most recently) 62 * running a given event_base's loop. Requires lock. */ 63#define EVBASE_IN_THREAD(base) \ 64 (evthread_id_fn_ == NULL || \ 65 (base)->th_owner_id == evthread_id_fn_()) 66 67/** Return true iff we need to notify the base's main thread about changes to 68 * its state, because it's currently running the main loop in another 69 * thread. Requires lock. */ 70#define EVBASE_NEED_NOTIFY(base) \ 71 (evthread_id_fn_ != NULL && \ 72 (base)->running_loop && \ 73 (base)->th_owner_id != evthread_id_fn_()) 74 75/** Allocate a new lock, and store it in lockvar, a void*. Sets lockvar to 76 NULL if locking is not enabled. */ 77#define EVTHREAD_ALLOC_LOCK(lockvar, locktype) \ 78 ((lockvar) = evthread_lock_fns_.alloc ? \ 79 evthread_lock_fns_.alloc(locktype) : NULL) 80 81/** Free a given lock, if it is present and locking is enabled. */ 82#define EVTHREAD_FREE_LOCK(lockvar, locktype) \ 83 do { \ 84 void *lock_tmp_ = (lockvar); \ 85 if (lock_tmp_ && evthread_lock_fns_.free) \ 86 evthread_lock_fns_.free(lock_tmp_, (locktype)); \ 87 } while (0) 88 89/** Acquire a lock. */ 90#define EVLOCK_LOCK(lockvar,mode) \ 91 do { \ 92 if (lockvar) \ 93 evthread_lock_fns_.lock(mode, lockvar); \ 94 } while (0) 95 96/** Release a lock */ 97#define EVLOCK_UNLOCK(lockvar,mode) \ 98 do { \ 99 if (lockvar) \ 100 evthread_lock_fns_.unlock(mode, lockvar); \ 101 } while (0) 102 103/** Helper: put lockvar1 and lockvar2 into pointerwise ascending order. */ 104#define EVLOCK_SORTLOCKS_(lockvar1, lockvar2) \ 105 do { \ 106 if (lockvar1 && lockvar2 && lockvar1 > lockvar2) { \ 107 void *tmp = lockvar1; \ 108 lockvar1 = lockvar2; \ 109 lockvar2 = tmp; \ 110 } \ 111 } while (0) 112 113/** Lock an event_base, if it is set up for locking. Acquires the lock 114 in the base structure whose field is named 'lockvar'. */ 115#define EVBASE_ACQUIRE_LOCK(base, lockvar) do { \ 116 EVLOCK_LOCK((base)->lockvar, 0); \ 117 } while (0) 118 119/** Unlock an event_base, if it is set up for locking. */ 120#define EVBASE_RELEASE_LOCK(base, lockvar) do { \ 121 EVLOCK_UNLOCK((base)->lockvar, 0); \ 122 } while (0) 123 124/** If lock debugging is enabled, and lock is non-null, assert that 'lock' is 125 * locked and held by us. */ 126#define EVLOCK_ASSERT_LOCKED(lock) \ 127 do { \ 128 if ((lock) && evthread_lock_debugging_enabled_) { \ 129 EVUTIL_ASSERT(evthread_is_debug_lock_held_(lock)); \ 130 } \ 131 } while (0) 132 133/** Try to grab the lock for 'lockvar' without blocking, and return 1 if we 134 * manage to get it. */ 135static inline int EVLOCK_TRY_LOCK_(void *lock); 136static inline int 137EVLOCK_TRY_LOCK_(void *lock) 138{ 139 if (lock && evthread_lock_fns_.lock) { 140 int r = evthread_lock_fns_.lock(EVTHREAD_TRY, lock); 141 return !r; 142 } else { 143 /* Locking is disabled either globally or for this thing; 144 * of course we count as having the lock. */ 145 return 1; 146 } 147} 148 149/** Allocate a new condition variable and store it in the void *, condvar */ 150#define EVTHREAD_ALLOC_COND(condvar) \ 151 do { \ 152 (condvar) = evthread_cond_fns_.alloc_condition ? \ 153 evthread_cond_fns_.alloc_condition(0) : NULL; \ 154 } while (0) 155/** Deallocate and free a condition variable in condvar */ 156#define EVTHREAD_FREE_COND(cond) \ 157 do { \ 158 if (cond) \ 159 evthread_cond_fns_.free_condition((cond)); \ 160 } while (0) 161/** Signal one thread waiting on cond */ 162#define EVTHREAD_COND_SIGNAL(cond) \ 163 ( (cond) ? evthread_cond_fns_.signal_condition((cond), 0) : 0 ) 164/** Signal all threads waiting on cond */ 165#define EVTHREAD_COND_BROADCAST(cond) \ 166 ( (cond) ? evthread_cond_fns_.signal_condition((cond), 1) : 0 ) 167/** Wait until the condition 'cond' is signalled. Must be called while 168 * holding 'lock'. The lock will be released until the condition is 169 * signalled, at which point it will be acquired again. Returns 0 for 170 * success, -1 for failure. */ 171#define EVTHREAD_COND_WAIT(cond, lock) \ 172 ( (cond) ? evthread_cond_fns_.wait_condition((cond), (lock), NULL) : 0 ) 173/** As EVTHREAD_COND_WAIT, but gives up after 'tv' has elapsed. Returns 1 174 * on timeout. */ 175#define EVTHREAD_COND_WAIT_TIMED(cond, lock, tv) \ 176 ( (cond) ? evthread_cond_fns_.wait_condition((cond), (lock), (tv)) : 0 ) 177 178/** True iff locking functions have been configured. */ 179#define EVTHREAD_LOCKING_ENABLED() \ 180 (evthread_lock_fns_.lock != NULL) 181 182#elif ! defined(EVENT__DISABLE_THREAD_SUPPORT) 183 184unsigned long evthreadimpl_get_id_(void); 185int evthreadimpl_is_lock_debugging_enabled_(void); 186void *evthreadimpl_lock_alloc_(unsigned locktype); 187void evthreadimpl_lock_free_(void *lock, unsigned locktype); 188int evthreadimpl_lock_lock_(unsigned mode, void *lock); 189int evthreadimpl_lock_unlock_(unsigned mode, void *lock); 190void *evthreadimpl_cond_alloc_(unsigned condtype); 191void evthreadimpl_cond_free_(void *cond); 192int evthreadimpl_cond_signal_(void *cond, int broadcast); 193int evthreadimpl_cond_wait_(void *cond, void *lock, const struct timeval *tv); 194int evthreadimpl_locking_enabled_(void); 195 196#define EVTHREAD_GET_ID() evthreadimpl_get_id_() 197#define EVBASE_IN_THREAD(base) \ 198 ((base)->th_owner_id == evthreadimpl_get_id_()) 199#define EVBASE_NEED_NOTIFY(base) \ 200 ((base)->running_loop && \ 201 ((base)->th_owner_id != evthreadimpl_get_id_())) 202 203#define EVTHREAD_ALLOC_LOCK(lockvar, locktype) \ 204 ((lockvar) = evthreadimpl_lock_alloc_(locktype)) 205 206#define EVTHREAD_FREE_LOCK(lockvar, locktype) \ 207 do { \ 208 void *lock_tmp_ = (lockvar); \ 209 if (lock_tmp_) \ 210 evthreadimpl_lock_free_(lock_tmp_, (locktype)); \ 211 } while (0) 212 213/** Acquire a lock. */ 214#define EVLOCK_LOCK(lockvar,mode) \ 215 do { \ 216 if (lockvar) \ 217 evthreadimpl_lock_lock_(mode, lockvar); \ 218 } while (0) 219 220/** Release a lock */ 221#define EVLOCK_UNLOCK(lockvar,mode) \ 222 do { \ 223 if (lockvar) \ 224 evthreadimpl_lock_unlock_(mode, lockvar); \ 225 } while (0) 226 227/** Lock an event_base, if it is set up for locking. Acquires the lock 228 in the base structure whose field is named 'lockvar'. */ 229#define EVBASE_ACQUIRE_LOCK(base, lockvar) do { \ 230 EVLOCK_LOCK((base)->lockvar, 0); \ 231 } while (0) 232 233/** Unlock an event_base, if it is set up for locking. */ 234#define EVBASE_RELEASE_LOCK(base, lockvar) do { \ 235 EVLOCK_UNLOCK((base)->lockvar, 0); \ 236 } while (0) 237 238/** If lock debugging is enabled, and lock is non-null, assert that 'lock' is 239 * locked and held by us. */ 240#define EVLOCK_ASSERT_LOCKED(lock) \ 241 do { \ 242 if ((lock) && evthreadimpl_is_lock_debugging_enabled_()) { \ 243 EVUTIL_ASSERT(evthread_is_debug_lock_held_(lock)); \ 244 } \ 245 } while (0) 246 247/** Try to grab the lock for 'lockvar' without blocking, and return 1 if we 248 * manage to get it. */ 249static inline int EVLOCK_TRY_LOCK_(void *lock); 250static inline int 251EVLOCK_TRY_LOCK_(void *lock) 252{ 253 if (lock) { 254 int r = evthreadimpl_lock_lock_(EVTHREAD_TRY, lock); 255 return !r; 256 } else { 257 /* Locking is disabled either globally or for this thing; 258 * of course we count as having the lock. */ 259 return 1; 260 } 261} 262 263/** Allocate a new condition variable and store it in the void *, condvar */ 264#define EVTHREAD_ALLOC_COND(condvar) \ 265 do { \ 266 (condvar) = evthreadimpl_cond_alloc_(0); \ 267 } while (0) 268/** Deallocate and free a condition variable in condvar */ 269#define EVTHREAD_FREE_COND(cond) \ 270 do { \ 271 if (cond) \ 272 evthreadimpl_cond_free_((cond)); \ 273 } while (0) 274/** Signal one thread waiting on cond */ 275#define EVTHREAD_COND_SIGNAL(cond) \ 276 ( (cond) ? evthreadimpl_cond_signal_((cond), 0) : 0 ) 277/** Signal all threads waiting on cond */ 278#define EVTHREAD_COND_BROADCAST(cond) \ 279 ( (cond) ? evthreadimpl_cond_signal_((cond), 1) : 0 ) 280/** Wait until the condition 'cond' is signalled. Must be called while 281 * holding 'lock'. The lock will be released until the condition is 282 * signalled, at which point it will be acquired again. Returns 0 for 283 * success, -1 for failure. */ 284#define EVTHREAD_COND_WAIT(cond, lock) \ 285 ( (cond) ? evthreadimpl_cond_wait_((cond), (lock), NULL) : 0 ) 286/** As EVTHREAD_COND_WAIT, but gives up after 'tv' has elapsed. Returns 1 287 * on timeout. */ 288#define EVTHREAD_COND_WAIT_TIMED(cond, lock, tv) \ 289 ( (cond) ? evthreadimpl_cond_wait_((cond), (lock), (tv)) : 0 ) 290 291#define EVTHREAD_LOCKING_ENABLED() \ 292 (evthreadimpl_locking_enabled_()) 293 294#else /* EVENT__DISABLE_THREAD_SUPPORT */ 295 296#define EVTHREAD_GET_ID() 1 297#define EVTHREAD_ALLOC_LOCK(lockvar, locktype) EVUTIL_NIL_STMT_ 298#define EVTHREAD_FREE_LOCK(lockvar, locktype) EVUTIL_NIL_STMT_ 299 300#define EVLOCK_LOCK(lockvar, mode) EVUTIL_NIL_STMT_ 301#define EVLOCK_UNLOCK(lockvar, mode) EVUTIL_NIL_STMT_ 302#define EVLOCK_LOCK2(lock1,lock2,mode1,mode2) EVUTIL_NIL_STMT_ 303#define EVLOCK_UNLOCK2(lock1,lock2,mode1,mode2) EVUTIL_NIL_STMT_ 304 305#define EVBASE_IN_THREAD(base) 1 306#define EVBASE_NEED_NOTIFY(base) 0 307#define EVBASE_ACQUIRE_LOCK(base, lock) EVUTIL_NIL_STMT_ 308#define EVBASE_RELEASE_LOCK(base, lock) EVUTIL_NIL_STMT_ 309#define EVLOCK_ASSERT_LOCKED(lock) EVUTIL_NIL_STMT_ 310 311#define EVLOCK_TRY_LOCK_(lock) 1 312 313#define EVTHREAD_ALLOC_COND(condvar) EVUTIL_NIL_STMT_ 314#define EVTHREAD_FREE_COND(cond) EVUTIL_NIL_STMT_ 315#define EVTHREAD_COND_SIGNAL(cond) EVUTIL_NIL_STMT_ 316#define EVTHREAD_COND_BROADCAST(cond) EVUTIL_NIL_STMT_ 317#define EVTHREAD_COND_WAIT(cond, lock) EVUTIL_NIL_STMT_ 318#define EVTHREAD_COND_WAIT_TIMED(cond, lock, howlong) EVUTIL_NIL_STMT_ 319 320#define EVTHREAD_LOCKING_ENABLED() 0 321 322#endif 323 324/* This code is shared between both lock impls */ 325#if ! defined(EVENT__DISABLE_THREAD_SUPPORT) 326/** Helper: put lockvar1 and lockvar2 into pointerwise ascending order. */ 327#define EVLOCK_SORTLOCKS_(lockvar1, lockvar2) \ 328 do { \ 329 if (lockvar1 && lockvar2 && lockvar1 > lockvar2) { \ 330 void *tmp = lockvar1; \ 331 lockvar1 = lockvar2; \ 332 lockvar2 = tmp; \ 333 } \ 334 } while (0) 335 336/** Acquire both lock1 and lock2. Always allocates locks in the same order, 337 * so that two threads locking two locks with LOCK2 will not deadlock. */ 338#define EVLOCK_LOCK2(lock1,lock2,mode1,mode2) \ 339 do { \ 340 void *lock1_tmplock_ = (lock1); \ 341 void *lock2_tmplock_ = (lock2); \ 342 EVLOCK_SORTLOCKS_(lock1_tmplock_,lock2_tmplock_); \ 343 EVLOCK_LOCK(lock1_tmplock_,mode1); \ 344 if (lock2_tmplock_ != lock1_tmplock_) \ 345 EVLOCK_LOCK(lock2_tmplock_,mode2); \ 346 } while (0) 347/** Release both lock1 and lock2. */ 348#define EVLOCK_UNLOCK2(lock1,lock2,mode1,mode2) \ 349 do { \ 350 void *lock1_tmplock_ = (lock1); \ 351 void *lock2_tmplock_ = (lock2); \ 352 EVLOCK_SORTLOCKS_(lock1_tmplock_,lock2_tmplock_); \ 353 if (lock2_tmplock_ != lock1_tmplock_) \ 354 EVLOCK_UNLOCK(lock2_tmplock_,mode2); \ 355 EVLOCK_UNLOCK(lock1_tmplock_,mode1); \ 356 } while (0) 357 358int evthread_is_debug_lock_held_(void *lock); 359void *evthread_debug_get_real_lock_(void *lock); 360 361void *evthread_setup_global_lock_(void *lock_, unsigned locktype, 362 int enable_locks); 363 364#define EVTHREAD_SETUP_GLOBAL_LOCK(lockvar, locktype) \ 365 do { \ 366 lockvar = evthread_setup_global_lock_(lockvar, \ 367 (locktype), enable_locks); \ 368 if (!lockvar) { \ 369 event_warn("Couldn't allocate %s", #lockvar); \ 370 return -1; \ 371 } \ 372 } while (0); 373 374int event_global_setup_locks_(const int enable_locks); 375int evsig_global_setup_locks_(const int enable_locks); 376int evutil_global_setup_locks_(const int enable_locks); 377int evutil_secure_rng_global_setup_locks_(const int enable_locks); 378 379/** Return current evthread_lock_callbacks */ 380struct evthread_lock_callbacks *evthread_get_lock_callbacks(void); 381/** Return current evthread_condition_callbacks */ 382struct evthread_condition_callbacks *evthread_get_condition_callbacks(void); 383/** Disable locking for internal usage (like global shutdown) */ 384void evthreadimpl_disable_lock_debugging_(void); 385 386#endif 387 388#ifdef __cplusplus 389} 390#endif 391 392#endif /* EVTHREAD_INTERNAL_H_INCLUDED_ */ 393