1275970Scy/* 2275970Scy * Copyright (c) 2008-2012 Niels Provos, Nick Mathewson 3275970Scy * 4275970Scy * Redistribution and use in source and binary forms, with or without 5275970Scy * modification, are permitted provided that the following conditions 6275970Scy * are met: 7275970Scy * 1. Redistributions of source code must retain the above copyright 8275970Scy * notice, this list of conditions and the following disclaimer. 9275970Scy * 2. Redistributions in binary form must reproduce the above copyright 10275970Scy * notice, this list of conditions and the following disclaimer in the 11275970Scy * documentation and/or other materials provided with the distribution. 12275970Scy * 3. The name of the author may not be used to endorse or promote products 13275970Scy * derived from this software without specific prior written permission. 14275970Scy * 15275970Scy * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16275970Scy * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17275970Scy * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18275970Scy * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19275970Scy * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20275970Scy * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21275970Scy * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22275970Scy * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23275970Scy * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24275970Scy * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25275970Scy */ 26275970Scy#ifndef EVTHREAD_INTERNAL_H_INCLUDED_ 27275970Scy#define EVTHREAD_INTERNAL_H_INCLUDED_ 28275970Scy 29275970Scy#ifdef __cplusplus 30275970Scyextern "C" { 31275970Scy#endif 32275970Scy 33275970Scy#include "event2/event-config.h" 34275970Scy#include "evconfig-private.h" 35275970Scy 36275970Scy#include "event2/thread.h" 37275970Scy#include "util-internal.h" 38275970Scy 39275970Scystruct event_base; 40275970Scy 41275970Scy#ifndef _WIN32 42275970Scy/* On Windows, the way we currently make DLLs, it's not allowed for us to 43275970Scy * have shared global structures. Thus, we only do the direct-call-to-function 44275970Scy * code path if we know that the local shared library system supports it. 45275970Scy */ 46275970Scy#define EVTHREAD_EXPOSE_STRUCTS 47275970Scy#endif 48275970Scy 49275970Scy#if ! defined(EVENT__DISABLE_THREAD_SUPPORT) && defined(EVTHREAD_EXPOSE_STRUCTS) 50275970Scy/* Global function pointers to lock-related functions. NULL if locking isn't 51275970Scy enabled. */ 52275970Scyextern struct evthread_lock_callbacks evthread_lock_fns_; 53275970Scyextern struct evthread_condition_callbacks evthread_cond_fns_; 54275970Scyextern unsigned long (*evthread_id_fn_)(void); 55275970Scyextern int evthread_lock_debugging_enabled_; 56275970Scy 57275970Scy/** Return the ID of the current thread, or 1 if threading isn't enabled. */ 58275970Scy#define EVTHREAD_GET_ID() \ 59275970Scy (evthread_id_fn_ ? evthread_id_fn_() : 1) 60275970Scy 61275970Scy/** Return true iff we're in the thread that is currently (or most recently) 62275970Scy * running a given event_base's loop. Requires lock. */ 63275970Scy#define EVBASE_IN_THREAD(base) \ 64275970Scy (evthread_id_fn_ == NULL || \ 65275970Scy (base)->th_owner_id == evthread_id_fn_()) 66275970Scy 67275970Scy/** Return true iff we need to notify the base's main thread about changes to 68275970Scy * its state, because it's currently running the main loop in another 69275970Scy * thread. Requires lock. */ 70275970Scy#define EVBASE_NEED_NOTIFY(base) \ 71275970Scy (evthread_id_fn_ != NULL && \ 72275970Scy (base)->running_loop && \ 73275970Scy (base)->th_owner_id != evthread_id_fn_()) 74275970Scy 75275970Scy/** Allocate a new lock, and store it in lockvar, a void*. Sets lockvar to 76275970Scy NULL if locking is not enabled. */ 77275970Scy#define EVTHREAD_ALLOC_LOCK(lockvar, locktype) \ 78275970Scy ((lockvar) = evthread_lock_fns_.alloc ? \ 79275970Scy evthread_lock_fns_.alloc(locktype) : NULL) 80275970Scy 81275970Scy/** Free a given lock, if it is present and locking is enabled. */ 82275970Scy#define EVTHREAD_FREE_LOCK(lockvar, locktype) \ 83275970Scy do { \ 84275970Scy void *lock_tmp_ = (lockvar); \ 85275970Scy if (lock_tmp_ && evthread_lock_fns_.free) \ 86275970Scy evthread_lock_fns_.free(lock_tmp_, (locktype)); \ 87275970Scy } while (0) 88275970Scy 89275970Scy/** Acquire a lock. */ 90275970Scy#define EVLOCK_LOCK(lockvar,mode) \ 91275970Scy do { \ 92275970Scy if (lockvar) \ 93275970Scy evthread_lock_fns_.lock(mode, lockvar); \ 94275970Scy } while (0) 95275970Scy 96275970Scy/** Release a lock */ 97275970Scy#define EVLOCK_UNLOCK(lockvar,mode) \ 98275970Scy do { \ 99275970Scy if (lockvar) \ 100275970Scy evthread_lock_fns_.unlock(mode, lockvar); \ 101275970Scy } while (0) 102275970Scy 103275970Scy/** Helper: put lockvar1 and lockvar2 into pointerwise ascending order. */ 104275970Scy#define EVLOCK_SORTLOCKS_(lockvar1, lockvar2) \ 105275970Scy do { \ 106275970Scy if (lockvar1 && lockvar2 && lockvar1 > lockvar2) { \ 107275970Scy void *tmp = lockvar1; \ 108275970Scy lockvar1 = lockvar2; \ 109275970Scy lockvar2 = tmp; \ 110275970Scy } \ 111275970Scy } while (0) 112275970Scy 113275970Scy/** Lock an event_base, if it is set up for locking. Acquires the lock 114275970Scy in the base structure whose field is named 'lockvar'. */ 115275970Scy#define EVBASE_ACQUIRE_LOCK(base, lockvar) do { \ 116275970Scy EVLOCK_LOCK((base)->lockvar, 0); \ 117275970Scy } while (0) 118275970Scy 119275970Scy/** Unlock an event_base, if it is set up for locking. */ 120275970Scy#define EVBASE_RELEASE_LOCK(base, lockvar) do { \ 121275970Scy EVLOCK_UNLOCK((base)->lockvar, 0); \ 122275970Scy } while (0) 123275970Scy 124275970Scy/** If lock debugging is enabled, and lock is non-null, assert that 'lock' is 125275970Scy * locked and held by us. */ 126275970Scy#define EVLOCK_ASSERT_LOCKED(lock) \ 127275970Scy do { \ 128275970Scy if ((lock) && evthread_lock_debugging_enabled_) { \ 129275970Scy EVUTIL_ASSERT(evthread_is_debug_lock_held_(lock)); \ 130275970Scy } \ 131275970Scy } while (0) 132275970Scy 133275970Scy/** Try to grab the lock for 'lockvar' without blocking, and return 1 if we 134275970Scy * manage to get it. */ 135275970Scystatic inline int EVLOCK_TRY_LOCK_(void *lock); 136275970Scystatic inline int 137275970ScyEVLOCK_TRY_LOCK_(void *lock) 138275970Scy{ 139275970Scy if (lock && evthread_lock_fns_.lock) { 140275970Scy int r = evthread_lock_fns_.lock(EVTHREAD_TRY, lock); 141275970Scy return !r; 142275970Scy } else { 143275970Scy /* Locking is disabled either globally or for this thing; 144275970Scy * of course we count as having the lock. */ 145275970Scy return 1; 146275970Scy } 147275970Scy} 148275970Scy 149275970Scy/** Allocate a new condition variable and store it in the void *, condvar */ 150275970Scy#define EVTHREAD_ALLOC_COND(condvar) \ 151275970Scy do { \ 152275970Scy (condvar) = evthread_cond_fns_.alloc_condition ? \ 153275970Scy evthread_cond_fns_.alloc_condition(0) : NULL; \ 154275970Scy } while (0) 155275970Scy/** Deallocate and free a condition variable in condvar */ 156275970Scy#define EVTHREAD_FREE_COND(cond) \ 157275970Scy do { \ 158275970Scy if (cond) \ 159275970Scy evthread_cond_fns_.free_condition((cond)); \ 160275970Scy } while (0) 161275970Scy/** Signal one thread waiting on cond */ 162275970Scy#define EVTHREAD_COND_SIGNAL(cond) \ 163275970Scy ( (cond) ? evthread_cond_fns_.signal_condition((cond), 0) : 0 ) 164275970Scy/** Signal all threads waiting on cond */ 165275970Scy#define EVTHREAD_COND_BROADCAST(cond) \ 166275970Scy ( (cond) ? evthread_cond_fns_.signal_condition((cond), 1) : 0 ) 167275970Scy/** Wait until the condition 'cond' is signalled. Must be called while 168275970Scy * holding 'lock'. The lock will be released until the condition is 169275970Scy * signalled, at which point it will be acquired again. Returns 0 for 170275970Scy * success, -1 for failure. */ 171275970Scy#define EVTHREAD_COND_WAIT(cond, lock) \ 172275970Scy ( (cond) ? evthread_cond_fns_.wait_condition((cond), (lock), NULL) : 0 ) 173275970Scy/** As EVTHREAD_COND_WAIT, but gives up after 'tv' has elapsed. Returns 1 174275970Scy * on timeout. */ 175275970Scy#define EVTHREAD_COND_WAIT_TIMED(cond, lock, tv) \ 176275970Scy ( (cond) ? evthread_cond_fns_.wait_condition((cond), (lock), (tv)) : 0 ) 177275970Scy 178275970Scy/** True iff locking functions have been configured. */ 179275970Scy#define EVTHREAD_LOCKING_ENABLED() \ 180275970Scy (evthread_lock_fns_.lock != NULL) 181275970Scy 182275970Scy#elif ! defined(EVENT__DISABLE_THREAD_SUPPORT) 183275970Scy 184275970Scyunsigned long evthreadimpl_get_id_(void); 185275970Scyint evthreadimpl_is_lock_debugging_enabled_(void); 186275970Scyvoid *evthreadimpl_lock_alloc_(unsigned locktype); 187275970Scyvoid evthreadimpl_lock_free_(void *lock, unsigned locktype); 188275970Scyint evthreadimpl_lock_lock_(unsigned mode, void *lock); 189275970Scyint evthreadimpl_lock_unlock_(unsigned mode, void *lock); 190275970Scyvoid *evthreadimpl_cond_alloc_(unsigned condtype); 191275970Scyvoid evthreadimpl_cond_free_(void *cond); 192275970Scyint evthreadimpl_cond_signal_(void *cond, int broadcast); 193275970Scyint evthreadimpl_cond_wait_(void *cond, void *lock, const struct timeval *tv); 194275970Scyint evthreadimpl_locking_enabled_(void); 195275970Scy 196275970Scy#define EVTHREAD_GET_ID() evthreadimpl_get_id_() 197275970Scy#define EVBASE_IN_THREAD(base) \ 198275970Scy ((base)->th_owner_id == evthreadimpl_get_id_()) 199275970Scy#define EVBASE_NEED_NOTIFY(base) \ 200275970Scy ((base)->running_loop && \ 201275970Scy ((base)->th_owner_id != evthreadimpl_get_id_())) 202275970Scy 203275970Scy#define EVTHREAD_ALLOC_LOCK(lockvar, locktype) \ 204275970Scy ((lockvar) = evthreadimpl_lock_alloc_(locktype)) 205275970Scy 206275970Scy#define EVTHREAD_FREE_LOCK(lockvar, locktype) \ 207275970Scy do { \ 208275970Scy void *lock_tmp_ = (lockvar); \ 209275970Scy if (lock_tmp_) \ 210275970Scy evthreadimpl_lock_free_(lock_tmp_, (locktype)); \ 211275970Scy } while (0) 212275970Scy 213275970Scy/** Acquire a lock. */ 214275970Scy#define EVLOCK_LOCK(lockvar,mode) \ 215275970Scy do { \ 216275970Scy if (lockvar) \ 217275970Scy evthreadimpl_lock_lock_(mode, lockvar); \ 218275970Scy } while (0) 219275970Scy 220275970Scy/** Release a lock */ 221275970Scy#define EVLOCK_UNLOCK(lockvar,mode) \ 222275970Scy do { \ 223275970Scy if (lockvar) \ 224275970Scy evthreadimpl_lock_unlock_(mode, lockvar); \ 225275970Scy } while (0) 226275970Scy 227275970Scy/** Lock an event_base, if it is set up for locking. Acquires the lock 228275970Scy in the base structure whose field is named 'lockvar'. */ 229275970Scy#define EVBASE_ACQUIRE_LOCK(base, lockvar) do { \ 230275970Scy EVLOCK_LOCK((base)->lockvar, 0); \ 231275970Scy } while (0) 232275970Scy 233275970Scy/** Unlock an event_base, if it is set up for locking. */ 234275970Scy#define EVBASE_RELEASE_LOCK(base, lockvar) do { \ 235275970Scy EVLOCK_UNLOCK((base)->lockvar, 0); \ 236275970Scy } while (0) 237275970Scy 238275970Scy/** If lock debugging is enabled, and lock is non-null, assert that 'lock' is 239275970Scy * locked and held by us. */ 240275970Scy#define EVLOCK_ASSERT_LOCKED(lock) \ 241275970Scy do { \ 242275970Scy if ((lock) && evthreadimpl_is_lock_debugging_enabled_()) { \ 243275970Scy EVUTIL_ASSERT(evthread_is_debug_lock_held_(lock)); \ 244275970Scy } \ 245275970Scy } while (0) 246275970Scy 247275970Scy/** Try to grab the lock for 'lockvar' without blocking, and return 1 if we 248275970Scy * manage to get it. */ 249275970Scystatic inline int EVLOCK_TRY_LOCK_(void *lock); 250275970Scystatic inline int 251275970ScyEVLOCK_TRY_LOCK_(void *lock) 252275970Scy{ 253275970Scy if (lock) { 254275970Scy int r = evthreadimpl_lock_lock_(EVTHREAD_TRY, lock); 255275970Scy return !r; 256275970Scy } else { 257275970Scy /* Locking is disabled either globally or for this thing; 258275970Scy * of course we count as having the lock. */ 259275970Scy return 1; 260275970Scy } 261275970Scy} 262275970Scy 263275970Scy/** Allocate a new condition variable and store it in the void *, condvar */ 264275970Scy#define EVTHREAD_ALLOC_COND(condvar) \ 265275970Scy do { \ 266275970Scy (condvar) = evthreadimpl_cond_alloc_(0); \ 267275970Scy } while (0) 268275970Scy/** Deallocate and free a condition variable in condvar */ 269275970Scy#define EVTHREAD_FREE_COND(cond) \ 270275970Scy do { \ 271275970Scy if (cond) \ 272275970Scy evthreadimpl_cond_free_((cond)); \ 273275970Scy } while (0) 274275970Scy/** Signal one thread waiting on cond */ 275275970Scy#define EVTHREAD_COND_SIGNAL(cond) \ 276275970Scy ( (cond) ? evthreadimpl_cond_signal_((cond), 0) : 0 ) 277275970Scy/** Signal all threads waiting on cond */ 278275970Scy#define EVTHREAD_COND_BROADCAST(cond) \ 279275970Scy ( (cond) ? evthreadimpl_cond_signal_((cond), 1) : 0 ) 280275970Scy/** Wait until the condition 'cond' is signalled. Must be called while 281275970Scy * holding 'lock'. The lock will be released until the condition is 282275970Scy * signalled, at which point it will be acquired again. Returns 0 for 283275970Scy * success, -1 for failure. */ 284275970Scy#define EVTHREAD_COND_WAIT(cond, lock) \ 285275970Scy ( (cond) ? evthreadimpl_cond_wait_((cond), (lock), NULL) : 0 ) 286275970Scy/** As EVTHREAD_COND_WAIT, but gives up after 'tv' has elapsed. Returns 1 287275970Scy * on timeout. */ 288275970Scy#define EVTHREAD_COND_WAIT_TIMED(cond, lock, tv) \ 289275970Scy ( (cond) ? evthreadimpl_cond_wait_((cond), (lock), (tv)) : 0 ) 290275970Scy 291275970Scy#define EVTHREAD_LOCKING_ENABLED() \ 292275970Scy (evthreadimpl_locking_enabled_()) 293275970Scy 294275970Scy#else /* EVENT__DISABLE_THREAD_SUPPORT */ 295275970Scy 296275970Scy#define EVTHREAD_GET_ID() 1 297275970Scy#define EVTHREAD_ALLOC_LOCK(lockvar, locktype) EVUTIL_NIL_STMT_ 298275970Scy#define EVTHREAD_FREE_LOCK(lockvar, locktype) EVUTIL_NIL_STMT_ 299275970Scy 300275970Scy#define EVLOCK_LOCK(lockvar, mode) EVUTIL_NIL_STMT_ 301275970Scy#define EVLOCK_UNLOCK(lockvar, mode) EVUTIL_NIL_STMT_ 302275970Scy#define EVLOCK_LOCK2(lock1,lock2,mode1,mode2) EVUTIL_NIL_STMT_ 303275970Scy#define EVLOCK_UNLOCK2(lock1,lock2,mode1,mode2) EVUTIL_NIL_STMT_ 304275970Scy 305275970Scy#define EVBASE_IN_THREAD(base) 1 306275970Scy#define EVBASE_NEED_NOTIFY(base) 0 307275970Scy#define EVBASE_ACQUIRE_LOCK(base, lock) EVUTIL_NIL_STMT_ 308275970Scy#define EVBASE_RELEASE_LOCK(base, lock) EVUTIL_NIL_STMT_ 309275970Scy#define EVLOCK_ASSERT_LOCKED(lock) EVUTIL_NIL_STMT_ 310275970Scy 311275970Scy#define EVLOCK_TRY_LOCK_(lock) 1 312275970Scy 313275970Scy#define EVTHREAD_ALLOC_COND(condvar) EVUTIL_NIL_STMT_ 314275970Scy#define EVTHREAD_FREE_COND(cond) EVUTIL_NIL_STMT_ 315275970Scy#define EVTHREAD_COND_SIGNAL(cond) EVUTIL_NIL_STMT_ 316275970Scy#define EVTHREAD_COND_BROADCAST(cond) EVUTIL_NIL_STMT_ 317275970Scy#define EVTHREAD_COND_WAIT(cond, lock) EVUTIL_NIL_STMT_ 318275970Scy#define EVTHREAD_COND_WAIT_TIMED(cond, lock, howlong) EVUTIL_NIL_STMT_ 319275970Scy 320275970Scy#define EVTHREAD_LOCKING_ENABLED() 0 321275970Scy 322275970Scy#endif 323275970Scy 324275970Scy/* This code is shared between both lock impls */ 325275970Scy#if ! defined(EVENT__DISABLE_THREAD_SUPPORT) 326275970Scy/** Helper: put lockvar1 and lockvar2 into pointerwise ascending order. */ 327275970Scy#define EVLOCK_SORTLOCKS_(lockvar1, lockvar2) \ 328275970Scy do { \ 329275970Scy if (lockvar1 && lockvar2 && lockvar1 > lockvar2) { \ 330275970Scy void *tmp = lockvar1; \ 331275970Scy lockvar1 = lockvar2; \ 332275970Scy lockvar2 = tmp; \ 333275970Scy } \ 334275970Scy } while (0) 335275970Scy 336275970Scy/** Acquire both lock1 and lock2. Always allocates locks in the same order, 337275970Scy * so that two threads locking two locks with LOCK2 will not deadlock. */ 338275970Scy#define EVLOCK_LOCK2(lock1,lock2,mode1,mode2) \ 339275970Scy do { \ 340275970Scy void *lock1_tmplock_ = (lock1); \ 341275970Scy void *lock2_tmplock_ = (lock2); \ 342275970Scy EVLOCK_SORTLOCKS_(lock1_tmplock_,lock2_tmplock_); \ 343275970Scy EVLOCK_LOCK(lock1_tmplock_,mode1); \ 344275970Scy if (lock2_tmplock_ != lock1_tmplock_) \ 345275970Scy EVLOCK_LOCK(lock2_tmplock_,mode2); \ 346275970Scy } while (0) 347275970Scy/** Release both lock1 and lock2. */ 348275970Scy#define EVLOCK_UNLOCK2(lock1,lock2,mode1,mode2) \ 349275970Scy do { \ 350275970Scy void *lock1_tmplock_ = (lock1); \ 351275970Scy void *lock2_tmplock_ = (lock2); \ 352275970Scy EVLOCK_SORTLOCKS_(lock1_tmplock_,lock2_tmplock_); \ 353275970Scy if (lock2_tmplock_ != lock1_tmplock_) \ 354275970Scy EVLOCK_UNLOCK(lock2_tmplock_,mode2); \ 355275970Scy EVLOCK_UNLOCK(lock1_tmplock_,mode1); \ 356275970Scy } while (0) 357275970Scy 358275970Scyint evthread_is_debug_lock_held_(void *lock); 359275970Scyvoid *evthread_debug_get_real_lock_(void *lock); 360275970Scy 361275970Scyvoid *evthread_setup_global_lock_(void *lock_, unsigned locktype, 362275970Scy int enable_locks); 363275970Scy 364275970Scy#define EVTHREAD_SETUP_GLOBAL_LOCK(lockvar, locktype) \ 365275970Scy do { \ 366275970Scy lockvar = evthread_setup_global_lock_(lockvar, \ 367275970Scy (locktype), enable_locks); \ 368275970Scy if (!lockvar) { \ 369275970Scy event_warn("Couldn't allocate %s", #lockvar); \ 370275970Scy return -1; \ 371275970Scy } \ 372275970Scy } while (0); 373275970Scy 374275970Scyint event_global_setup_locks_(const int enable_locks); 375275970Scyint evsig_global_setup_locks_(const int enable_locks); 376275970Scyint evutil_global_setup_locks_(const int enable_locks); 377275970Scyint evutil_secure_rng_global_setup_locks_(const int enable_locks); 378275970Scy 379285612Sdelphij/** Return current evthread_lock_callbacks */ 380285612Sdelphijstruct evthread_lock_callbacks *evthread_get_lock_callbacks(void); 381285612Sdelphij/** Return current evthread_condition_callbacks */ 382285612Sdelphijstruct evthread_condition_callbacks *evthread_get_condition_callbacks(void); 383285612Sdelphij/** Disable locking for internal usage (like global shutdown) */ 384285612Sdelphijvoid evthreadimpl_disable_lock_debugging_(void); 385285612Sdelphij 386275970Scy#endif 387275970Scy 388275970Scy#ifdef __cplusplus 389275970Scy} 390275970Scy#endif 391275970Scy 392275970Scy#endif /* EVTHREAD_INTERNAL_H_INCLUDED_ */ 393