1/*- 2 * See the file LICENSE for redistribution information. 3 * 4 * Copyright (c) 1996,2008 Oracle. All rights reserved. 5 * 6 * $Id: mut_tas.c,v 12.36 2008/03/13 15:23:09 mbrey 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/* 20 * __db_tas_mutex_init -- 21 * Initialize a test-and-set mutex. 22 * 23 * PUBLIC: int __db_tas_mutex_init __P((ENV *, db_mutex_t, u_int32_t)); 24 */ 25int 26__db_tas_mutex_init(env, mutex, flags) 27 ENV *env; 28 db_mutex_t mutex; 29 u_int32_t flags; 30{ 31 DB_ENV *dbenv; 32 DB_MUTEX *mutexp; 33 DB_MUTEXMGR *mtxmgr; 34 DB_MUTEXREGION *mtxregion; 35 int ret; 36 37 COMPQUIET(flags, 0); 38 39 dbenv = env->dbenv; 40 mtxmgr = env->mutex_handle; 41 mtxregion = mtxmgr->reginfo.primary; 42 mutexp = MUTEXP_SET(mutex); 43 44 /* Check alignment. */ 45 if (((uintptr_t)mutexp & (dbenv->mutex_align - 1)) != 0) { 46 __db_errx(env, "TAS: mutex not appropriately aligned"); 47 return (EINVAL); 48 } 49 50 if (MUTEX_INIT(&mutexp->tas)) { 51 ret = __os_get_syserr(); 52 __db_syserr(env, ret, "TAS: mutex initialize"); 53 return (__os_posix_err(ret)); 54 } 55#ifdef HAVE_MUTEX_HYBRID 56 if ((ret = __db_pthread_mutex_init(env, 57 mutex, flags | DB_MUTEX_SELF_BLOCK)) != 0) 58 return (ret); 59#endif 60 return (0); 61} 62 63/* 64 * __db_tas_mutex_lock 65 * Lock on a mutex, blocking if necessary. 66 * 67 * PUBLIC: int __db_tas_mutex_lock __P((ENV *, db_mutex_t)); 68 */ 69int 70__db_tas_mutex_lock(env, mutex) 71 ENV *env; 72 db_mutex_t mutex; 73{ 74 DB_ENV *dbenv; 75 DB_MUTEX *mutexp; 76 DB_MUTEXMGR *mtxmgr; 77 DB_MUTEXREGION *mtxregion; 78 u_int32_t nspins; 79#ifdef HAVE_MUTEX_HYBRID 80 int ret; 81#else 82 u_long ms, max_ms; 83#endif 84 dbenv = env->dbenv; 85 86 if (!MUTEX_ON(env) || F_ISSET(dbenv, DB_ENV_NOLOCKING)) 87 return (0); 88 89 mtxmgr = env->mutex_handle; 90 mtxregion = mtxmgr->reginfo.primary; 91 mutexp = MUTEXP_SET(mutex); 92 93 CHECK_MTX_THREAD(env, mutexp); 94 95#ifdef HAVE_STATISTICS 96 if (F_ISSET(mutexp, DB_MUTEX_LOCKED)) 97 ++mutexp->mutex_set_wait; 98 else 99 ++mutexp->mutex_set_nowait; 100#endif 101 102#ifndef HAVE_MUTEX_HYBRID 103 /* 104 * Wait 1ms initially, up to 10ms for mutexes backing logical database 105 * locks, and up to 25 ms for mutual exclusion data structure mutexes. 106 * SR: #7675 107 */ 108 ms = 1; 109 max_ms = F_ISSET(mutexp, DB_MUTEX_LOGICAL_LOCK) ? 10 : 25; 110#endif 111 112loop: /* Attempt to acquire the resource for N spins. */ 113 for (nspins = 114 mtxregion->stat.st_mutex_tas_spins; nspins > 0; --nspins) { 115#ifdef HAVE_MUTEX_HPPA_MSEM_INIT 116relock: 117#endif 118#ifdef HAVE_MUTEX_S390_CC_ASSEMBLY 119 tsl_t zero = 0; 120#endif 121 /* 122 * Avoid interlocked instructions until they're likely to 123 * succeed. 124 */ 125 if (F_ISSET(mutexp, DB_MUTEX_LOCKED) || 126 !MUTEX_SET(&mutexp->tas)) { 127 /* 128 * Some systems (notably those with newer Intel CPUs) 129 * need a small pause here. [#6975] 130 */ 131#ifdef MUTEX_PAUSE 132 MUTEX_PAUSE 133#endif 134 continue; 135 } 136 137#ifdef HAVE_MUTEX_HPPA_MSEM_INIT 138 /* 139 * HP semaphores are unlocked automatically when a holding 140 * process exits. If the mutex appears to be locked 141 * (F_ISSET(DB_MUTEX_LOCKED)) but we got here, assume this 142 * has happened. Set the pid and tid into the mutex and 143 * lock again. (The default state of the mutexes used to 144 * block in __lock_get_internal is locked, so exiting with 145 * a locked mutex is reasonable behavior for a process that 146 * happened to initialize or use one of them.) 147 */ 148 if (F_ISSET(mutexp, DB_MUTEX_LOCKED)) { 149 F_SET(mutexp, DB_MUTEX_LOCKED); 150 dbenv->thread_id(dbenv, &mutexp->pid, &mutexp->tid); 151 goto relock; 152 } 153 /* 154 * If we make it here, the mutex isn't locked, the diagnostic 155 * won't fire, and we were really unlocked by someone calling 156 * the DB mutex unlock function. 157 */ 158#endif 159#ifdef DIAGNOSTIC 160 if (F_ISSET(mutexp, DB_MUTEX_LOCKED)) { 161 char buf[DB_THREADID_STRLEN]; 162 __db_errx(env, 163 "TAS lock failed: lock currently in use: ID: %s", 164 dbenv->thread_id_string(dbenv, 165 mutexp->pid, mutexp->tid, buf)); 166 return (__env_panic(env, EACCES)); 167 } 168#endif 169 F_SET(mutexp, DB_MUTEX_LOCKED); 170 dbenv->thread_id(dbenv, &mutexp->pid, &mutexp->tid); 171 172#ifdef DIAGNOSTIC 173 /* 174 * We want to switch threads as often as possible. Yield 175 * every time we get a mutex to ensure contention. 176 */ 177 if (F_ISSET(dbenv, DB_ENV_YIELDCPU)) 178 __os_yield(env, 0, 0); 179#endif 180 return (0); 181 } 182 183 /* Wait for the lock to become available. */ 184#ifdef HAVE_MUTEX_HYBRID 185 /* 186 * By yielding here we can get the other thread to give up the 187 * mutex before calling the more expensive library mutex call. 188 * Tests have shown this to be a big win when there is contention. 189 */ 190 __os_yield(env, 0, 0); 191 if (!F_ISSET(mutexp, DB_MUTEX_LOCKED)) 192 goto loop; 193 if ((ret = __db_pthread_mutex_lock(env, mutex)) != 0) 194 return (ret); 195#else 196 __os_yield(env, 0, ms * US_PER_MS); 197 if ((ms <<= 1) > max_ms) 198 ms = max_ms; 199#endif 200 201 /* 202 * We're spinning. The environment might be hung, and somebody else 203 * has already recovered it. The first thing recovery does is panic 204 * the environment. Check to see if we're never going to get this 205 * mutex. 206 */ 207 PANIC_CHECK(env); 208 209 goto loop; 210} 211 212/* 213 * __db_tas_mutex_unlock -- 214 * Release a mutex. 215 * 216 * PUBLIC: int __db_tas_mutex_unlock __P((ENV *, db_mutex_t)); 217 */ 218int 219__db_tas_mutex_unlock(env, mutex) 220 ENV *env; 221 db_mutex_t mutex; 222{ 223 DB_ENV *dbenv; 224 DB_MUTEX *mutexp; 225 DB_MUTEXMGR *mtxmgr; 226 DB_MUTEXREGION *mtxregion; 227#ifdef HAVE_MUTEX_HYBRID 228 int ret; 229#endif 230 dbenv = env->dbenv; 231 232 if (!MUTEX_ON(env) || F_ISSET(dbenv, DB_ENV_NOLOCKING)) 233 return (0); 234 235 mtxmgr = env->mutex_handle; 236 mtxregion = mtxmgr->reginfo.primary; 237 mutexp = MUTEXP_SET(mutex); 238 239#ifdef DIAGNOSTIC 240 if (!F_ISSET(mutexp, DB_MUTEX_LOCKED)) { 241 __db_errx(env, "TAS unlock failed: lock already unlocked"); 242 return (__env_panic(env, EACCES)); 243 } 244#endif 245 246 F_CLR(mutexp, DB_MUTEX_LOCKED); 247#ifdef HAVE_MUTEX_HYBRID 248 MUTEX_MEMBAR(mutexp->flags); 249 250 if (mutexp->wait && 251 (ret = __db_pthread_mutex_unlock(env, mutex)) != 0) 252 return (ret); 253#endif 254 MUTEX_UNSET(&mutexp->tas); 255 256 return (0); 257} 258 259/* 260 * __db_tas_mutex_destroy -- 261 * Destroy a mutex. 262 * 263 * PUBLIC: int __db_tas_mutex_destroy __P((ENV *, db_mutex_t)); 264 */ 265int 266__db_tas_mutex_destroy(env, mutex) 267 ENV *env; 268 db_mutex_t mutex; 269{ 270 DB_MUTEX *mutexp; 271 DB_MUTEXMGR *mtxmgr; 272 DB_MUTEXREGION *mtxregion; 273#ifdef HAVE_MUTEX_HYBRID 274 int ret; 275#endif 276 277 if (!MUTEX_ON(env)) 278 return (0); 279 280 mtxmgr = env->mutex_handle; 281 mtxregion = mtxmgr->reginfo.primary; 282 mutexp = MUTEXP_SET(mutex); 283 284 MUTEX_DESTROY(&mutexp->tas); 285 286#ifdef HAVE_MUTEX_HYBRID 287 if ((ret = __db_pthread_mutex_destroy(env, mutex)) != 0) 288 return (ret); 289#endif 290 291 COMPQUIET(mutexp, NULL); /* MUTEX_DESTROY may not be defined. */ 292 return (0); 293} 294