1/*- 2 * See the file LICENSE for redistribution information. 3 * 4 * Copyright (c) 1999,2008 Oracle. All rights reserved. 5 * 6 * $Id: mut_pthread.c,v 12.30 2008/01/08 20:58:43 bostic Exp $ 7 */ 8 9#include "db_config.h" 10 11#include "db_int.h" 12 13/* 14 * This is where we load in architecture/compiler specific mutex code. 15 */ 16#define LOAD_ACTUAL_MUTEX_CODE 17#include "dbinc/mutex_int.h" 18 19#ifdef HAVE_MUTEX_SOLARIS_LWP 20#define pthread_cond_destroy(x) 0 21#define pthread_cond_signal _lwp_cond_signal 22#define pthread_cond_wait _lwp_cond_wait 23#define pthread_mutex_destroy(x) 0 24#define pthread_mutex_lock _lwp_mutex_lock 25#define pthread_mutex_trylock _lwp_mutex_trylock 26#define pthread_mutex_unlock _lwp_mutex_unlock 27#endif 28#ifdef HAVE_MUTEX_UI_THREADS 29#define pthread_cond_destroy(x) cond_destroy 30#define pthread_cond_signal cond_signal 31#define pthread_cond_wait cond_wait 32#define pthread_mutex_destroy mutex_destroy 33#define pthread_mutex_lock mutex_lock 34#define pthread_mutex_trylock mutex_trylock 35#define pthread_mutex_unlock mutex_unlock 36#endif 37 38#define PTHREAD_UNLOCK_ATTEMPTS 5 39 40/* 41 * IBM's MVS pthread mutex implementation returns -1 and sets errno rather than 42 * returning errno itself. As -1 is not a valid errno value, assume functions 43 * returning -1 have set errno. If they haven't, return a random error value. 44 */ 45#define RET_SET(f, ret) do { \ 46 if (((ret) = (f)) == -1 && ((ret) = errno) == 0) \ 47 (ret) = EAGAIN; \ 48} while (0) 49 50/* 51 * __db_pthread_mutex_init -- 52 * Initialize a pthread mutex. 53 * 54 * PUBLIC: int __db_pthread_mutex_init __P((ENV *, db_mutex_t, u_int32_t)); 55 */ 56int 57__db_pthread_mutex_init(env, mutex, flags) 58 ENV *env; 59 db_mutex_t mutex; 60 u_int32_t flags; 61{ 62 DB_MUTEX *mutexp; 63 DB_MUTEXMGR *mtxmgr; 64 DB_MUTEXREGION *mtxregion; 65 int ret; 66 67 mtxmgr = env->mutex_handle; 68 mtxregion = mtxmgr->reginfo.primary; 69 mutexp = MUTEXP_SET(mutex); 70 ret = 0; 71 72#ifdef HAVE_MUTEX_PTHREADS 73 { 74 pthread_condattr_t condattr, *condattrp = NULL; 75 pthread_mutexattr_t mutexattr, *mutexattrp = NULL; 76 77 if (!LF_ISSET(DB_MUTEX_PROCESS_ONLY)) { 78 RET_SET((pthread_mutexattr_init(&mutexattr)), ret); 79#ifndef HAVE_MUTEX_THREAD_ONLY 80 if (ret == 0) 81 RET_SET((pthread_mutexattr_setpshared( 82 &mutexattr, PTHREAD_PROCESS_SHARED)), ret); 83#endif 84 mutexattrp = &mutexattr; 85 } 86 87 if (ret == 0) 88 RET_SET((pthread_mutex_init(&mutexp->mutex, mutexattrp)), ret); 89 if (mutexattrp != NULL) 90 (void)pthread_mutexattr_destroy(mutexattrp); 91 if (ret == 0 && LF_ISSET(DB_MUTEX_SELF_BLOCK)) { 92 if (!LF_ISSET(DB_MUTEX_PROCESS_ONLY)) { 93 RET_SET((pthread_condattr_init(&condattr)), ret); 94 if (ret == 0) { 95 condattrp = &condattr; 96#ifndef HAVE_MUTEX_THREAD_ONLY 97 RET_SET((pthread_condattr_setpshared( 98 &condattr, PTHREAD_PROCESS_SHARED)), ret); 99#endif 100 } 101 } 102 103 if (ret == 0) 104 RET_SET( 105 (pthread_cond_init(&mutexp->cond, condattrp)), ret); 106 107 F_SET(mutexp, DB_MUTEX_SELF_BLOCK); 108 if (condattrp != NULL) 109 (void)pthread_condattr_destroy(condattrp); 110 } 111 112 } 113#endif 114#ifdef HAVE_MUTEX_SOLARIS_LWP 115 /* 116 * XXX 117 * Gcc complains about missing braces in the static initializations of 118 * lwp_cond_t and lwp_mutex_t structures because the structures contain 119 * sub-structures/unions and the Solaris include file that defines the 120 * initialization values doesn't have surrounding braces. There's not 121 * much we can do. 122 */ 123 if (LF_ISSET(DB_MUTEX_PROCESS_ONLY)) { 124 static lwp_mutex_t mi = DEFAULTMUTEX; 125 126 mutexp->mutex = mi; 127 } else { 128 static lwp_mutex_t mi = SHAREDMUTEX; 129 130 mutexp->mutex = mi; 131 } 132 if (LF_ISSET(DB_MUTEX_SELF_BLOCK)) { 133 if (LF_ISSET(DB_MUTEX_PROCESS_ONLY)) { 134 static lwp_cond_t ci = DEFAULTCV; 135 136 mutexp->cond = ci; 137 } else { 138 static lwp_cond_t ci = SHAREDCV; 139 140 mutexp->cond = ci; 141 } 142 F_SET(mutexp, DB_MUTEX_SELF_BLOCK); 143 } 144#endif 145#ifdef HAVE_MUTEX_UI_THREADS 146 { 147 int type; 148 149 type = LF_ISSET(DB_MUTEX_PROCESS_ONLY) ? USYNC_THREAD : USYNC_PROCESS; 150 151 ret = mutex_init(&mutexp->mutex, type, NULL); 152 if (ret == 0 && LF_ISSET(DB_MUTEX_SELF_BLOCK)) { 153 ret = cond_init(&mutexp->cond, type, NULL); 154 155 F_SET(mutexp, DB_MUTEX_SELF_BLOCK); 156 }} 157#endif 158 159 if (ret != 0) { 160 __db_err(env, ret, "unable to initialize mutex"); 161 } 162 return (ret); 163} 164 165/* 166 * __db_pthread_mutex_lock 167 * Lock on a mutex, blocking if necessary. 168 * 169 * PUBLIC: int __db_pthread_mutex_lock __P((ENV *, db_mutex_t)); 170 */ 171int 172__db_pthread_mutex_lock(env, mutex) 173 ENV *env; 174 db_mutex_t mutex; 175{ 176 DB_ENV *dbenv; 177 DB_MUTEX *mutexp; 178 DB_MUTEXMGR *mtxmgr; 179 DB_MUTEXREGION *mtxregion; 180 int i, ret; 181 182 dbenv = env->dbenv; 183 184 if (!MUTEX_ON(env) || F_ISSET(dbenv, DB_ENV_NOLOCKING)) 185 return (0); 186 187 mtxmgr = env->mutex_handle; 188 mtxregion = mtxmgr->reginfo.primary; 189 mutexp = MUTEXP_SET(mutex); 190 191 CHECK_MTX_THREAD(env, mutexp); 192 193#if defined(HAVE_STATISTICS) && !defined(HAVE_MUTEX_HYBRID) 194 /* 195 * We want to know which mutexes are contentious, but don't want to 196 * do an interlocked test here -- that's slower when the underlying 197 * system has adaptive mutexes and can perform optimizations like 198 * spinning only if the thread holding the mutex is actually running 199 * on a CPU. Make a guess, using a normal load instruction. 200 */ 201 if (F_ISSET(mutexp, DB_MUTEX_LOCKED)) 202 ++mutexp->mutex_set_wait; 203 else 204 ++mutexp->mutex_set_nowait; 205#endif 206 207 RET_SET((pthread_mutex_lock(&mutexp->mutex)), ret); 208 if (ret != 0) 209 goto err; 210 211 if (F_ISSET(mutexp, DB_MUTEX_SELF_BLOCK)) { 212 /* 213 * If we are using hybrid mutexes then the pthread mutexes 214 * are only used to wait after spinning on the TAS mutex. 215 * Set the wait flag before checking to see if the mutex 216 * is still locked. The holder will clear the bit before 217 * checking the wait flag. 218 */ 219#ifdef HAVE_MUTEX_HYBRID 220 mutexp->wait++; 221 MUTEX_MEMBAR(mutexp->wait); 222#endif 223 while (F_ISSET(mutexp, DB_MUTEX_LOCKED)) { 224 RET_SET((pthread_cond_wait( 225 &mutexp->cond, &mutexp->mutex)), ret); 226 /* 227 * !!! 228 * Solaris bug workaround: 229 * pthread_cond_wait() sometimes returns ETIME -- out 230 * of sheer paranoia, check both ETIME and ETIMEDOUT. 231 * We believe this happens when the application uses 232 * SIGALRM for some purpose, e.g., the C library sleep 233 * call, and Solaris delivers the signal to the wrong 234 * LWP. 235 */ 236 if (ret != 0 && ret != EINTR && 237#ifdef ETIME 238 ret != ETIME && 239#endif 240 ret != ETIMEDOUT) { 241 (void)pthread_mutex_unlock(&mutexp->mutex); 242 goto err; 243 } 244 } 245 246#ifdef HAVE_MUTEX_HYBRID 247 mutexp->wait--; 248#else 249 F_SET(mutexp, DB_MUTEX_LOCKED); 250 dbenv->thread_id(dbenv, &mutexp->pid, &mutexp->tid); 251#endif 252 253 /* 254 * According to HP-UX engineers contacted by Netscape, 255 * pthread_mutex_unlock() will occasionally return EFAULT 256 * for no good reason on mutexes in shared memory regions, 257 * and the correct caller behavior is to try again. Do 258 * so, up to PTHREAD_UNLOCK_ATTEMPTS consecutive times. 259 * Note that we don't bother to restrict this to HP-UX; 260 * it should be harmless elsewhere. [#2471] 261 */ 262 i = PTHREAD_UNLOCK_ATTEMPTS; 263 do { 264 RET_SET((pthread_mutex_unlock(&mutexp->mutex)), ret); 265 } while (ret == EFAULT && --i > 0); 266 if (ret != 0) 267 goto err; 268 } else { 269#ifdef DIAGNOSTIC 270 if (F_ISSET(mutexp, DB_MUTEX_LOCKED)) { 271 char buf[DB_THREADID_STRLEN]; 272 (void)dbenv->thread_id_string(dbenv, 273 mutexp->pid, mutexp->tid, buf); 274 __db_errx(env, 275 "pthread lock failed: lock currently in use: pid/tid: %s", 276 buf); 277 ret = EINVAL; 278 goto err; 279 } 280#endif 281 F_SET(mutexp, DB_MUTEX_LOCKED); 282 dbenv->thread_id(dbenv, &mutexp->pid, &mutexp->tid); 283 } 284 285#ifdef DIAGNOSTIC 286 /* 287 * We want to switch threads as often as possible. Yield every time 288 * we get a mutex to ensure contention. 289 */ 290 if (F_ISSET(dbenv, DB_ENV_YIELDCPU)) 291 __os_yield(env, 0, 0); 292#endif 293 return (0); 294 295err: __db_err(env, ret, "pthread lock failed"); 296 return (__env_panic(env, ret)); 297} 298 299/* 300 * __db_pthread_mutex_unlock -- 301 * Release a mutex. 302 * 303 * PUBLIC: int __db_pthread_mutex_unlock __P((ENV *, db_mutex_t)); 304 */ 305int 306__db_pthread_mutex_unlock(env, mutex) 307 ENV *env; 308 db_mutex_t mutex; 309{ 310 DB_ENV *dbenv; 311 DB_MUTEX *mutexp; 312 DB_MUTEXMGR *mtxmgr; 313 DB_MUTEXREGION *mtxregion; 314 int i, ret; 315 316 dbenv = env->dbenv; 317 318 if (!MUTEX_ON(env) || F_ISSET(dbenv, DB_ENV_NOLOCKING)) 319 return (0); 320 321 mtxmgr = env->mutex_handle; 322 mtxregion = mtxmgr->reginfo.primary; 323 mutexp = MUTEXP_SET(mutex); 324 325#if !defined(HAVE_MUTEX_HYBRID) && defined(DIAGNOSTIC) 326 if (!F_ISSET(mutexp, DB_MUTEX_LOCKED)) { 327 __db_errx( 328 env, "pthread unlock failed: lock already unlocked"); 329 return (__env_panic(env, EACCES)); 330 } 331#endif 332 if (F_ISSET(mutexp, DB_MUTEX_SELF_BLOCK)) { 333 RET_SET((pthread_mutex_lock(&mutexp->mutex)), ret); 334 if (ret != 0) 335 goto err; 336 337 F_CLR(mutexp, DB_MUTEX_LOCKED); 338 339 RET_SET((pthread_cond_signal(&mutexp->cond)), ret); 340 if (ret != 0) 341 goto err; 342 } else 343 F_CLR(mutexp, DB_MUTEX_LOCKED); 344 345 /* See comment above; workaround for [#2471]. */ 346 i = PTHREAD_UNLOCK_ATTEMPTS; 347 do { 348 RET_SET((pthread_mutex_unlock(&mutexp->mutex)), ret); 349 } while (ret == EFAULT && --i > 0); 350 351err: if (ret != 0) { 352 __db_err(env, ret, "pthread unlock failed"); 353 return (__env_panic(env, ret)); 354 } 355 return (ret); 356} 357 358/* 359 * __db_pthread_mutex_destroy -- 360 * Destroy a mutex. 361 * 362 * PUBLIC: int __db_pthread_mutex_destroy __P((ENV *, db_mutex_t)); 363 */ 364int 365__db_pthread_mutex_destroy(env, mutex) 366 ENV *env; 367 db_mutex_t mutex; 368{ 369 DB_MUTEX *mutexp; 370 DB_MUTEXMGR *mtxmgr; 371 DB_MUTEXREGION *mtxregion; 372 int ret, t_ret; 373 374 if (!MUTEX_ON(env)) 375 return (0); 376 377 mtxmgr = env->mutex_handle; 378 mtxregion = mtxmgr->reginfo.primary; 379 mutexp = MUTEXP_SET(mutex); 380 381 ret = 0; 382 if (F_ISSET(mutexp, DB_MUTEX_SELF_BLOCK)) { 383 RET_SET((pthread_cond_destroy(&mutexp->cond)), ret); 384 if (ret != 0) 385 __db_err(env, ret, "unable to destroy cond"); 386 } 387 RET_SET((pthread_mutex_destroy(&mutexp->mutex)), t_ret); 388 if (t_ret != 0) { 389 __db_err(env, t_ret, "unable to destroy mutex"); 390 if (ret == 0) 391 ret = t_ret; 392 } 393 return (ret); 394} 395