1290001Sglebius/* 2290001Sglebius * Copyright (c) 2008-2012 Niels Provos, Nick Mathewson 3290001Sglebius * 4290001Sglebius * Redistribution and use in source and binary forms, with or without 5290001Sglebius * modification, are permitted provided that the following conditions 6290001Sglebius * are met: 7290001Sglebius * 1. Redistributions of source code must retain the above copyright 8290001Sglebius * notice, this list of conditions and the following disclaimer. 9290001Sglebius * 2. Redistributions in binary form must reproduce the above copyright 10290001Sglebius * notice, this list of conditions and the following disclaimer in the 11290001Sglebius * documentation and/or other materials provided with the distribution. 12290001Sglebius * 3. The name of the author may not be used to endorse or promote products 13290001Sglebius * derived from this software without specific prior written permission. 14290001Sglebius * 15290001Sglebius * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16290001Sglebius * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17290001Sglebius * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18290001Sglebius * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19290001Sglebius * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20290001Sglebius * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21290001Sglebius * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22290001Sglebius * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23290001Sglebius * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24290001Sglebius * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25290001Sglebius */ 26290001Sglebius 27290001Sglebius#include "event2/event-config.h" 28290001Sglebius#include "evconfig-private.h" 29290001Sglebius 30290001Sglebius#ifndef EVENT__DISABLE_THREAD_SUPPORT 31290001Sglebius 32290001Sglebius#include "event2/thread.h" 33290001Sglebius 34290001Sglebius#include <stdlib.h> 35290001Sglebius#include <string.h> 36290001Sglebius 37290001Sglebius#include "log-internal.h" 38290001Sglebius#include "mm-internal.h" 39290001Sglebius#include "util-internal.h" 40290001Sglebius#include "evthread-internal.h" 41290001Sglebius 42290001Sglebius#ifdef EVTHREAD_EXPOSE_STRUCTS 43290001Sglebius#define GLOBAL 44290001Sglebius#else 45290001Sglebius#define GLOBAL static 46290001Sglebius#endif 47290001Sglebius 48290001Sglebius/* globals */ 49290001SglebiusGLOBAL int evthread_lock_debugging_enabled_ = 0; 50290001SglebiusGLOBAL struct evthread_lock_callbacks evthread_lock_fns_ = { 51290001Sglebius 0, 0, NULL, NULL, NULL, NULL 52290001Sglebius}; 53290001SglebiusGLOBAL unsigned long (*evthread_id_fn_)(void) = NULL; 54290001SglebiusGLOBAL struct evthread_condition_callbacks evthread_cond_fns_ = { 55290001Sglebius 0, NULL, NULL, NULL, NULL 56290001Sglebius}; 57290001Sglebius 58290001Sglebius/* Used for debugging */ 59290001Sglebiusstatic struct evthread_lock_callbacks original_lock_fns_ = { 60290001Sglebius 0, 0, NULL, NULL, NULL, NULL 61290001Sglebius}; 62290001Sglebiusstatic struct evthread_condition_callbacks original_cond_fns_ = { 63290001Sglebius 0, NULL, NULL, NULL, NULL 64290001Sglebius}; 65290001Sglebius 66290001Sglebiusvoid 67290001Sglebiusevthread_set_id_callback(unsigned long (*id_fn)(void)) 68290001Sglebius{ 69290001Sglebius evthread_id_fn_ = id_fn; 70290001Sglebius} 71290001Sglebius 72290001Sglebiusstruct evthread_lock_callbacks *evthread_get_lock_callbacks() 73290001Sglebius{ 74290001Sglebius return evthread_lock_debugging_enabled_ 75290001Sglebius ? &original_lock_fns_ : &evthread_lock_fns_; 76290001Sglebius} 77290001Sglebiusstruct evthread_condition_callbacks *evthread_get_condition_callbacks() 78290001Sglebius{ 79290001Sglebius return evthread_lock_debugging_enabled_ 80290001Sglebius ? &original_cond_fns_ : &evthread_cond_fns_; 81290001Sglebius} 82290001Sglebiusvoid evthreadimpl_disable_lock_debugging_(void) 83290001Sglebius{ 84290001Sglebius evthread_lock_debugging_enabled_ = 0; 85290001Sglebius} 86290001Sglebius 87290001Sglebiusint 88290001Sglebiusevthread_set_lock_callbacks(const struct evthread_lock_callbacks *cbs) 89290001Sglebius{ 90290001Sglebius struct evthread_lock_callbacks *target = evthread_get_lock_callbacks(); 91290001Sglebius 92290001Sglebius if (!cbs) { 93290001Sglebius if (target->alloc) 94290001Sglebius event_warnx("Trying to disable lock functions after " 95290001Sglebius "they have been set up will probaby not work."); 96290001Sglebius memset(target, 0, sizeof(evthread_lock_fns_)); 97290001Sglebius return 0; 98290001Sglebius } 99290001Sglebius if (target->alloc) { 100290001Sglebius /* Uh oh; we already had locking callbacks set up.*/ 101290001Sglebius if (target->lock_api_version == cbs->lock_api_version && 102290001Sglebius target->supported_locktypes == cbs->supported_locktypes && 103290001Sglebius target->alloc == cbs->alloc && 104290001Sglebius target->free == cbs->free && 105290001Sglebius target->lock == cbs->lock && 106290001Sglebius target->unlock == cbs->unlock) { 107290001Sglebius /* no change -- allow this. */ 108290001Sglebius return 0; 109290001Sglebius } 110290001Sglebius event_warnx("Can't change lock callbacks once they have been " 111290001Sglebius "initialized."); 112290001Sglebius return -1; 113290001Sglebius } 114290001Sglebius if (cbs->alloc && cbs->free && cbs->lock && cbs->unlock) { 115290001Sglebius memcpy(target, cbs, sizeof(evthread_lock_fns_)); 116290001Sglebius return event_global_setup_locks_(1); 117290001Sglebius } else { 118290001Sglebius return -1; 119290001Sglebius } 120290001Sglebius} 121290001Sglebius 122290001Sglebiusint 123290001Sglebiusevthread_set_condition_callbacks(const struct evthread_condition_callbacks *cbs) 124290001Sglebius{ 125290001Sglebius struct evthread_condition_callbacks *target = evthread_get_condition_callbacks(); 126290001Sglebius 127290001Sglebius if (!cbs) { 128290001Sglebius if (target->alloc_condition) 129290001Sglebius event_warnx("Trying to disable condition functions " 130290001Sglebius "after they have been set up will probaby not " 131290001Sglebius "work."); 132290001Sglebius memset(target, 0, sizeof(evthread_cond_fns_)); 133290001Sglebius return 0; 134290001Sglebius } 135290001Sglebius if (target->alloc_condition) { 136290001Sglebius /* Uh oh; we already had condition callbacks set up.*/ 137290001Sglebius if (target->condition_api_version == cbs->condition_api_version && 138290001Sglebius target->alloc_condition == cbs->alloc_condition && 139290001Sglebius target->free_condition == cbs->free_condition && 140290001Sglebius target->signal_condition == cbs->signal_condition && 141290001Sglebius target->wait_condition == cbs->wait_condition) { 142290001Sglebius /* no change -- allow this. */ 143290001Sglebius return 0; 144290001Sglebius } 145290001Sglebius event_warnx("Can't change condition callbacks once they " 146290001Sglebius "have been initialized."); 147290001Sglebius return -1; 148290001Sglebius } 149290001Sglebius if (cbs->alloc_condition && cbs->free_condition && 150290001Sglebius cbs->signal_condition && cbs->wait_condition) { 151290001Sglebius memcpy(target, cbs, sizeof(evthread_cond_fns_)); 152290001Sglebius } 153290001Sglebius if (evthread_lock_debugging_enabled_) { 154290001Sglebius evthread_cond_fns_.alloc_condition = cbs->alloc_condition; 155290001Sglebius evthread_cond_fns_.free_condition = cbs->free_condition; 156290001Sglebius evthread_cond_fns_.signal_condition = cbs->signal_condition; 157290001Sglebius } 158290001Sglebius return 0; 159290001Sglebius} 160290001Sglebius 161290001Sglebius#define DEBUG_LOCK_SIG 0xdeb0b10c 162290001Sglebius 163290001Sglebiusstruct debug_lock { 164290001Sglebius unsigned signature; 165290001Sglebius unsigned locktype; 166290001Sglebius unsigned long held_by; 167290001Sglebius /* XXXX if we ever use read-write locks, we will need a separate 168290001Sglebius * lock to protect count. */ 169290001Sglebius int count; 170290001Sglebius void *lock; 171290001Sglebius}; 172290001Sglebius 173290001Sglebiusstatic void * 174290001Sglebiusdebug_lock_alloc(unsigned locktype) 175290001Sglebius{ 176290001Sglebius struct debug_lock *result = mm_malloc(sizeof(struct debug_lock)); 177290001Sglebius if (!result) 178290001Sglebius return NULL; 179290001Sglebius if (original_lock_fns_.alloc) { 180290001Sglebius if (!(result->lock = original_lock_fns_.alloc( 181290001Sglebius locktype|EVTHREAD_LOCKTYPE_RECURSIVE))) { 182290001Sglebius mm_free(result); 183290001Sglebius return NULL; 184290001Sglebius } 185290001Sglebius } else { 186290001Sglebius result->lock = NULL; 187290001Sglebius } 188290001Sglebius result->signature = DEBUG_LOCK_SIG; 189290001Sglebius result->locktype = locktype; 190290001Sglebius result->count = 0; 191290001Sglebius result->held_by = 0; 192290001Sglebius return result; 193290001Sglebius} 194290001Sglebius 195290001Sglebiusstatic void 196290001Sglebiusdebug_lock_free(void *lock_, unsigned locktype) 197290001Sglebius{ 198290001Sglebius struct debug_lock *lock = lock_; 199290001Sglebius EVUTIL_ASSERT(lock->count == 0); 200290001Sglebius EVUTIL_ASSERT(locktype == lock->locktype); 201290001Sglebius EVUTIL_ASSERT(DEBUG_LOCK_SIG == lock->signature); 202290001Sglebius if (original_lock_fns_.free) { 203290001Sglebius original_lock_fns_.free(lock->lock, 204290001Sglebius lock->locktype|EVTHREAD_LOCKTYPE_RECURSIVE); 205290001Sglebius } 206290001Sglebius lock->lock = NULL; 207290001Sglebius lock->count = -100; 208290001Sglebius lock->signature = 0x12300fda; 209290001Sglebius mm_free(lock); 210290001Sglebius} 211290001Sglebius 212290001Sglebiusstatic void 213290001Sglebiusevthread_debug_lock_mark_locked(unsigned mode, struct debug_lock *lock) 214290001Sglebius{ 215290001Sglebius EVUTIL_ASSERT(DEBUG_LOCK_SIG == lock->signature); 216290001Sglebius ++lock->count; 217290001Sglebius if (!(lock->locktype & EVTHREAD_LOCKTYPE_RECURSIVE)) 218290001Sglebius EVUTIL_ASSERT(lock->count == 1); 219290001Sglebius if (evthread_id_fn_) { 220290001Sglebius unsigned long me; 221290001Sglebius me = evthread_id_fn_(); 222290001Sglebius if (lock->count > 1) 223290001Sglebius EVUTIL_ASSERT(lock->held_by == me); 224290001Sglebius lock->held_by = me; 225290001Sglebius } 226290001Sglebius} 227290001Sglebius 228290001Sglebiusstatic int 229290001Sglebiusdebug_lock_lock(unsigned mode, void *lock_) 230290001Sglebius{ 231290001Sglebius struct debug_lock *lock = lock_; 232290001Sglebius int res = 0; 233290001Sglebius if (lock->locktype & EVTHREAD_LOCKTYPE_READWRITE) 234290001Sglebius EVUTIL_ASSERT(mode & (EVTHREAD_READ|EVTHREAD_WRITE)); 235290001Sglebius else 236290001Sglebius EVUTIL_ASSERT((mode & (EVTHREAD_READ|EVTHREAD_WRITE)) == 0); 237290001Sglebius if (original_lock_fns_.lock) 238290001Sglebius res = original_lock_fns_.lock(mode, lock->lock); 239290001Sglebius if (!res) { 240290001Sglebius evthread_debug_lock_mark_locked(mode, lock); 241290001Sglebius } 242290001Sglebius return res; 243290001Sglebius} 244290001Sglebius 245290001Sglebiusstatic void 246290001Sglebiusevthread_debug_lock_mark_unlocked(unsigned mode, struct debug_lock *lock) 247290001Sglebius{ 248290001Sglebius EVUTIL_ASSERT(DEBUG_LOCK_SIG == lock->signature); 249290001Sglebius if (lock->locktype & EVTHREAD_LOCKTYPE_READWRITE) 250290001Sglebius EVUTIL_ASSERT(mode & (EVTHREAD_READ|EVTHREAD_WRITE)); 251290001Sglebius else 252290001Sglebius EVUTIL_ASSERT((mode & (EVTHREAD_READ|EVTHREAD_WRITE)) == 0); 253290001Sglebius if (evthread_id_fn_) { 254290001Sglebius unsigned long me; 255290001Sglebius me = evthread_id_fn_(); 256290001Sglebius EVUTIL_ASSERT(lock->held_by == me); 257290001Sglebius if (lock->count == 1) 258290001Sglebius lock->held_by = 0; 259290001Sglebius } 260290001Sglebius --lock->count; 261290001Sglebius EVUTIL_ASSERT(lock->count >= 0); 262290001Sglebius} 263290001Sglebius 264290001Sglebiusstatic int 265290001Sglebiusdebug_lock_unlock(unsigned mode, void *lock_) 266290001Sglebius{ 267290001Sglebius struct debug_lock *lock = lock_; 268290001Sglebius int res = 0; 269290001Sglebius evthread_debug_lock_mark_unlocked(mode, lock); 270290001Sglebius if (original_lock_fns_.unlock) 271290001Sglebius res = original_lock_fns_.unlock(mode, lock->lock); 272290001Sglebius return res; 273290001Sglebius} 274290001Sglebius 275290001Sglebiusstatic int 276290001Sglebiusdebug_cond_wait(void *cond_, void *lock_, const struct timeval *tv) 277290001Sglebius{ 278290001Sglebius int r; 279290001Sglebius struct debug_lock *lock = lock_; 280290001Sglebius EVUTIL_ASSERT(lock); 281290001Sglebius EVUTIL_ASSERT(DEBUG_LOCK_SIG == lock->signature); 282290001Sglebius EVLOCK_ASSERT_LOCKED(lock_); 283290001Sglebius evthread_debug_lock_mark_unlocked(0, lock); 284290001Sglebius r = original_cond_fns_.wait_condition(cond_, lock->lock, tv); 285290001Sglebius evthread_debug_lock_mark_locked(0, lock); 286290001Sglebius return r; 287290001Sglebius} 288290001Sglebius 289290001Sglebius/* misspelled version for backward compatibility */ 290290001Sglebiusvoid 291290001Sglebiusevthread_enable_lock_debuging(void) 292290001Sglebius{ 293290001Sglebius evthread_enable_lock_debugging(); 294290001Sglebius} 295290001Sglebius 296290001Sglebiusvoid 297290001Sglebiusevthread_enable_lock_debugging(void) 298290001Sglebius{ 299290001Sglebius struct evthread_lock_callbacks cbs = { 300290001Sglebius EVTHREAD_LOCK_API_VERSION, 301290001Sglebius EVTHREAD_LOCKTYPE_RECURSIVE, 302290001Sglebius debug_lock_alloc, 303290001Sglebius debug_lock_free, 304290001Sglebius debug_lock_lock, 305290001Sglebius debug_lock_unlock 306290001Sglebius }; 307290001Sglebius if (evthread_lock_debugging_enabled_) 308290001Sglebius return; 309290001Sglebius memcpy(&original_lock_fns_, &evthread_lock_fns_, 310290001Sglebius sizeof(struct evthread_lock_callbacks)); 311290001Sglebius memcpy(&evthread_lock_fns_, &cbs, 312290001Sglebius sizeof(struct evthread_lock_callbacks)); 313290001Sglebius 314290001Sglebius memcpy(&original_cond_fns_, &evthread_cond_fns_, 315290001Sglebius sizeof(struct evthread_condition_callbacks)); 316290001Sglebius evthread_cond_fns_.wait_condition = debug_cond_wait; 317290001Sglebius evthread_lock_debugging_enabled_ = 1; 318290001Sglebius 319290001Sglebius /* XXX return value should get checked. */ 320290001Sglebius event_global_setup_locks_(0); 321290001Sglebius} 322290001Sglebius 323290001Sglebiusint 324290001Sglebiusevthread_is_debug_lock_held_(void *lock_) 325290001Sglebius{ 326290001Sglebius struct debug_lock *lock = lock_; 327290001Sglebius if (! lock->count) 328290001Sglebius return 0; 329290001Sglebius if (evthread_id_fn_) { 330290001Sglebius unsigned long me = evthread_id_fn_(); 331290001Sglebius if (lock->held_by != me) 332290001Sglebius return 0; 333290001Sglebius } 334290001Sglebius return 1; 335290001Sglebius} 336290001Sglebius 337290001Sglebiusvoid * 338290001Sglebiusevthread_debug_get_real_lock_(void *lock_) 339290001Sglebius{ 340290001Sglebius struct debug_lock *lock = lock_; 341290001Sglebius return lock->lock; 342290001Sglebius} 343290001Sglebius 344290001Sglebiusvoid * 345290001Sglebiusevthread_setup_global_lock_(void *lock_, unsigned locktype, int enable_locks) 346290001Sglebius{ 347290001Sglebius /* there are four cases here: 348290001Sglebius 1) we're turning on debugging; locking is not on. 349290001Sglebius 2) we're turning on debugging; locking is on. 350290001Sglebius 3) we're turning on locking; debugging is not on. 351290001Sglebius 4) we're turning on locking; debugging is on. */ 352290001Sglebius 353290001Sglebius if (!enable_locks && original_lock_fns_.alloc == NULL) { 354290001Sglebius /* Case 1: allocate a debug lock. */ 355290001Sglebius EVUTIL_ASSERT(lock_ == NULL); 356290001Sglebius return debug_lock_alloc(locktype); 357290001Sglebius } else if (!enable_locks && original_lock_fns_.alloc != NULL) { 358290001Sglebius /* Case 2: wrap the lock in a debug lock. */ 359290001Sglebius struct debug_lock *lock; 360290001Sglebius EVUTIL_ASSERT(lock_ != NULL); 361290001Sglebius 362290001Sglebius if (!(locktype & EVTHREAD_LOCKTYPE_RECURSIVE)) { 363290001Sglebius /* We can't wrap it: We need a recursive lock */ 364290001Sglebius original_lock_fns_.free(lock_, locktype); 365290001Sglebius return debug_lock_alloc(locktype); 366290001Sglebius } 367290001Sglebius lock = mm_malloc(sizeof(struct debug_lock)); 368290001Sglebius if (!lock) { 369290001Sglebius original_lock_fns_.free(lock_, locktype); 370290001Sglebius return NULL; 371290001Sglebius } 372290001Sglebius lock->lock = lock_; 373290001Sglebius lock->locktype = locktype; 374290001Sglebius lock->count = 0; 375290001Sglebius lock->held_by = 0; 376290001Sglebius return lock; 377290001Sglebius } else if (enable_locks && ! evthread_lock_debugging_enabled_) { 378290001Sglebius /* Case 3: allocate a regular lock */ 379290001Sglebius EVUTIL_ASSERT(lock_ == NULL); 380290001Sglebius return evthread_lock_fns_.alloc(locktype); 381290001Sglebius } else { 382290001Sglebius /* Case 4: Fill in a debug lock with a real lock */ 383290001Sglebius struct debug_lock *lock = lock_; 384290001Sglebius EVUTIL_ASSERT(enable_locks && 385290001Sglebius evthread_lock_debugging_enabled_); 386290001Sglebius EVUTIL_ASSERT(lock->locktype == locktype); 387290001Sglebius EVUTIL_ASSERT(lock->lock == NULL); 388290001Sglebius lock->lock = original_lock_fns_.alloc( 389290001Sglebius locktype|EVTHREAD_LOCKTYPE_RECURSIVE); 390290001Sglebius if (!lock->lock) { 391290001Sglebius lock->count = -200; 392290001Sglebius mm_free(lock); 393290001Sglebius return NULL; 394290001Sglebius } 395290001Sglebius return lock; 396290001Sglebius } 397290001Sglebius} 398290001Sglebius 399290001Sglebius 400290001Sglebius#ifndef EVTHREAD_EXPOSE_STRUCTS 401290001Sglebiusunsigned long 402290001Sglebiusevthreadimpl_get_id_() 403290001Sglebius{ 404290001Sglebius return evthread_id_fn_ ? evthread_id_fn_() : 1; 405290001Sglebius} 406290001Sglebiusvoid * 407290001Sglebiusevthreadimpl_lock_alloc_(unsigned locktype) 408290001Sglebius{ 409290001Sglebius return evthread_lock_fns_.alloc ? 410290001Sglebius evthread_lock_fns_.alloc(locktype) : NULL; 411290001Sglebius} 412290001Sglebiusvoid 413290001Sglebiusevthreadimpl_lock_free_(void *lock, unsigned locktype) 414290001Sglebius{ 415290001Sglebius if (evthread_lock_fns_.free) 416290001Sglebius evthread_lock_fns_.free(lock, locktype); 417290001Sglebius} 418290001Sglebiusint 419290001Sglebiusevthreadimpl_lock_lock_(unsigned mode, void *lock) 420290001Sglebius{ 421290001Sglebius if (evthread_lock_fns_.lock) 422290001Sglebius return evthread_lock_fns_.lock(mode, lock); 423290001Sglebius else 424290001Sglebius return 0; 425290001Sglebius} 426290001Sglebiusint 427290001Sglebiusevthreadimpl_lock_unlock_(unsigned mode, void *lock) 428290001Sglebius{ 429290001Sglebius if (evthread_lock_fns_.unlock) 430290001Sglebius return evthread_lock_fns_.unlock(mode, lock); 431290001Sglebius else 432290001Sglebius return 0; 433290001Sglebius} 434290001Sglebiusvoid * 435290001Sglebiusevthreadimpl_cond_alloc_(unsigned condtype) 436290001Sglebius{ 437290001Sglebius return evthread_cond_fns_.alloc_condition ? 438290001Sglebius evthread_cond_fns_.alloc_condition(condtype) : NULL; 439290001Sglebius} 440290001Sglebiusvoid 441290001Sglebiusevthreadimpl_cond_free_(void *cond) 442290001Sglebius{ 443290001Sglebius if (evthread_cond_fns_.free_condition) 444290001Sglebius evthread_cond_fns_.free_condition(cond); 445290001Sglebius} 446290001Sglebiusint 447290001Sglebiusevthreadimpl_cond_signal_(void *cond, int broadcast) 448290001Sglebius{ 449290001Sglebius if (evthread_cond_fns_.signal_condition) 450290001Sglebius return evthread_cond_fns_.signal_condition(cond, broadcast); 451290001Sglebius else 452290001Sglebius return 0; 453290001Sglebius} 454290001Sglebiusint 455290001Sglebiusevthreadimpl_cond_wait_(void *cond, void *lock, const struct timeval *tv) 456290001Sglebius{ 457290001Sglebius if (evthread_cond_fns_.wait_condition) 458290001Sglebius return evthread_cond_fns_.wait_condition(cond, lock, tv); 459290001Sglebius else 460290001Sglebius return 0; 461290001Sglebius} 462290001Sglebiusint 463290001Sglebiusevthreadimpl_is_lock_debugging_enabled_(void) 464290001Sglebius{ 465290001Sglebius return evthread_lock_debugging_enabled_; 466290001Sglebius} 467290001Sglebius 468290001Sglebiusint 469290001Sglebiusevthreadimpl_locking_enabled_(void) 470290001Sglebius{ 471290001Sglebius return evthread_lock_fns_.lock != NULL; 472290001Sglebius} 473290001Sglebius#endif 474290001Sglebius 475290001Sglebius#endif 476