1/*- 2 * See the file LICENSE for redistribution information. 3 * 4 * Copyright (c) 1996-2009 Oracle. All rights reserved. 5 * 6 * $Id$ 7 */ 8 9#include "db_config.h" 10 11#include "db_int.h" 12 13/* 14 * __mutex_alloc_pp -- 15 * Allocate a mutex, application method. 16 * 17 * PUBLIC: int __mutex_alloc_pp __P((DB_ENV *, u_int32_t, db_mutex_t *)); 18 */ 19int 20__mutex_alloc_pp(dbenv, flags, indxp) 21 DB_ENV *dbenv; 22 u_int32_t flags; 23 db_mutex_t *indxp; 24{ 25 DB_THREAD_INFO *ip; 26 ENV *env; 27 int ret; 28 29 env = dbenv->env; 30 31 if ((ret = __db_fchk(env, "DB_ENV->mutex_alloc", 32 flags, DB_MUTEX_PROCESS_ONLY | DB_MUTEX_SELF_BLOCK)) != 0) 33 return (ret); 34 35 ENV_ENTER(env, ip); 36 ret = __mutex_alloc(env, MTX_APPLICATION, flags, indxp); 37 ENV_LEAVE(env, ip); 38 39 return (ret); 40} 41 42/* 43 * __mutex_free_pp -- 44 * Destroy a mutex, application method. 45 * 46 * PUBLIC: int __mutex_free_pp __P((DB_ENV *, db_mutex_t)); 47 */ 48int 49__mutex_free_pp(dbenv, indx) 50 DB_ENV *dbenv; 51 db_mutex_t indx; 52{ 53 DB_THREAD_INFO *ip; 54 ENV *env; 55 int ret; 56 57 env = dbenv->env; 58 59 if (indx == MUTEX_INVALID) 60 return (EINVAL); 61 62 /* 63 * Internally Berkeley DB passes around the db_mutex_t address on 64 * free, because we want to make absolutely sure the slot gets 65 * overwritten with MUTEX_INVALID. We don't export MUTEX_INVALID, 66 * so we don't export that part of the API, either. 67 */ 68 ENV_ENTER(env, ip); 69 ret = __mutex_free(env, &indx); 70 ENV_LEAVE(env, ip); 71 72 return (ret); 73} 74 75/* 76 * __mutex_lock -- 77 * Lock a mutex, application method. 78 * 79 * PUBLIC: int __mutex_lock_pp __P((DB_ENV *, db_mutex_t)); 80 */ 81int 82__mutex_lock_pp(dbenv, indx) 83 DB_ENV *dbenv; 84 db_mutex_t indx; 85{ 86 DB_THREAD_INFO *ip; 87 ENV *env; 88 int ret; 89 90 env = dbenv->env; 91 92 if (indx == MUTEX_INVALID) 93 return (EINVAL); 94 95 ENV_ENTER(env, ip); 96 ret = __mutex_lock(env, indx); 97 ENV_LEAVE(env, ip); 98 return (ret); 99} 100 101/* 102 * __mutex_unlock -- 103 * Unlock a mutex, application method. 104 * 105 * PUBLIC: int __mutex_unlock_pp __P((DB_ENV *, db_mutex_t)); 106 */ 107int 108__mutex_unlock_pp(dbenv, indx) 109 DB_ENV *dbenv; 110 db_mutex_t indx; 111{ 112 DB_THREAD_INFO *ip; 113 ENV *env; 114 int ret; 115 116 env = dbenv->env; 117 118 if (indx == MUTEX_INVALID) 119 return (EINVAL); 120 121 ENV_ENTER(env, ip); 122 ret = __mutex_unlock(env, indx); 123 ENV_LEAVE(env, ip); 124 return (ret); 125} 126 127/* 128 * __mutex_get_align -- 129 * DB_ENV->mutex_get_align. 130 * 131 * PUBLIC: int __mutex_get_align __P((DB_ENV *, u_int32_t *)); 132 */ 133int 134__mutex_get_align(dbenv, alignp) 135 DB_ENV *dbenv; 136 u_int32_t *alignp; 137{ 138 ENV *env; 139 140 env = dbenv->env; 141 142 if (MUTEX_ON(env)) { 143 /* Cannot be set after open, no lock required to read. */ 144 *alignp = ((DB_MUTEXREGION *) 145 env->mutex_handle->reginfo.primary)->stat.st_mutex_align; 146 } else 147 *alignp = dbenv->mutex_align; 148 return (0); 149} 150 151/* 152 * __mutex_set_align -- 153 * DB_ENV->mutex_set_align. 154 * 155 * PUBLIC: int __mutex_set_align __P((DB_ENV *, u_int32_t)); 156 */ 157int 158__mutex_set_align(dbenv, align) 159 DB_ENV *dbenv; 160 u_int32_t align; 161{ 162 ENV *env; 163 164 env = dbenv->env; 165 166 ENV_ILLEGAL_AFTER_OPEN(env, "DB_ENV->set_mutex_align"); 167 168 if (align == 0 || !POWER_OF_TWO(align)) { 169 __db_errx(env, 170 "DB_ENV->mutex_set_align: alignment value must be a non-zero power-of-two"); 171 return (EINVAL); 172 } 173 174 dbenv->mutex_align = align; 175 return (0); 176} 177 178/* 179 * __mutex_get_increment -- 180 * DB_ENV->mutex_get_increment. 181 * 182 * PUBLIC: int __mutex_get_increment __P((DB_ENV *, u_int32_t *)); 183 */ 184int 185__mutex_get_increment(dbenv, incrementp) 186 DB_ENV *dbenv; 187 u_int32_t *incrementp; 188{ 189 /* 190 * We don't maintain the increment in the region (it just makes 191 * no sense). Return whatever we have configured on this handle, 192 * nobody is ever going to notice. 193 */ 194 *incrementp = dbenv->mutex_inc; 195 return (0); 196} 197 198/* 199 * __mutex_set_increment -- 200 * DB_ENV->mutex_set_increment. 201 * 202 * PUBLIC: int __mutex_set_increment __P((DB_ENV *, u_int32_t)); 203 */ 204int 205__mutex_set_increment(dbenv, increment) 206 DB_ENV *dbenv; 207 u_int32_t increment; 208{ 209 ENV *env; 210 211 env = dbenv->env; 212 213 ENV_ILLEGAL_AFTER_OPEN(env, "DB_ENV->set_mutex_increment"); 214 215 dbenv->mutex_cnt = 0; 216 dbenv->mutex_inc = increment; 217 return (0); 218} 219 220/* 221 * __mutex_get_max -- 222 * DB_ENV->mutex_get_max. 223 * 224 * PUBLIC: int __mutex_get_max __P((DB_ENV *, u_int32_t *)); 225 */ 226int 227__mutex_get_max(dbenv, maxp) 228 DB_ENV *dbenv; 229 u_int32_t *maxp; 230{ 231 ENV *env; 232 233 env = dbenv->env; 234 235 if (MUTEX_ON(env)) { 236 /* Cannot be set after open, no lock required to read. */ 237 *maxp = ((DB_MUTEXREGION *) 238 env->mutex_handle->reginfo.primary)->stat.st_mutex_cnt; 239 } else 240 *maxp = dbenv->mutex_cnt; 241 return (0); 242} 243 244/* 245 * __mutex_set_max -- 246 * DB_ENV->mutex_set_max. 247 * 248 * PUBLIC: int __mutex_set_max __P((DB_ENV *, u_int32_t)); 249 */ 250int 251__mutex_set_max(dbenv, max) 252 DB_ENV *dbenv; 253 u_int32_t max; 254{ 255 ENV *env; 256 257 env = dbenv->env; 258 259 ENV_ILLEGAL_AFTER_OPEN(env, "DB_ENV->set_mutex_max"); 260 261 dbenv->mutex_cnt = max; 262 dbenv->mutex_inc = 0; 263 return (0); 264} 265 266/* 267 * __mutex_get_tas_spins -- 268 * DB_ENV->mutex_get_tas_spins. 269 * 270 * PUBLIC: int __mutex_get_tas_spins __P((DB_ENV *, u_int32_t *)); 271 */ 272int 273__mutex_get_tas_spins(dbenv, tas_spinsp) 274 DB_ENV *dbenv; 275 u_int32_t *tas_spinsp; 276{ 277 ENV *env; 278 279 env = dbenv->env; 280 281 if (MUTEX_ON(env)) { 282 /* Cannot be set after open, no lock required to read. */ 283 *tas_spinsp = ((DB_MUTEXREGION *)env-> 284 mutex_handle->reginfo.primary)->stat.st_mutex_tas_spins; 285 } else 286 *tas_spinsp = dbenv->mutex_tas_spins; 287 return (0); 288} 289 290/* 291 * __mutex_set_tas_spins -- 292 * DB_ENV->mutex_set_tas_spins. 293 * 294 * PUBLIC: int __mutex_set_tas_spins __P((DB_ENV *, u_int32_t)); 295 */ 296int 297__mutex_set_tas_spins(dbenv, tas_spins) 298 DB_ENV *dbenv; 299 u_int32_t tas_spins; 300{ 301 ENV *env; 302 303 env = dbenv->env; 304 305 /* 306 * Bound the value -- less than 1 makes no sense, greater than 1M 307 * makes no sense. 308 */ 309 if (tas_spins == 0) 310 tas_spins = 1; 311 else if (tas_spins > 1000000) 312 tas_spins = 1000000; 313 314 /* 315 * There's a theoretical race here, but I'm not interested in locking 316 * the test-and-set spin count. The worst possibility is a thread 317 * reads out a bad spin count and spins until it gets the lock, but 318 * that's awfully unlikely. 319 */ 320 if (MUTEX_ON(env)) 321 ((DB_MUTEXREGION *)env->mutex_handle 322 ->reginfo.primary)->stat.st_mutex_tas_spins = tas_spins; 323 else 324 dbenv->mutex_tas_spins = tas_spins; 325 return (0); 326} 327 328#if !defined(HAVE_ATOMIC_SUPPORT) && defined(HAVE_MUTEX_SUPPORT) 329/* 330 * Provide atomic operations for platforms which have mutexes yet do not have 331 * native atomic operations configured. They are emulated by protected the 332 * operation with a mutex. The address of the atomic value selects which 333 * mutex to use. 334 */ 335/* 336 * atomic_get_mutex - 337 * Map an address to the mutex to use to atomically modify it 338 */ 339static inline db_mutex_t atomic_get_mutex(env, v) 340 ENV *env; 341 db_atomic_t *v; 342{ 343 u_int index; 344 DB_MUTEXREGION *mtxreg; 345 346 if (!MUTEX_ON(env)) 347 return (MUTEX_INVALID); 348 index = (u_int)(((uintptr_t) (v)) >> 6) % MAX_ATOMIC_MUTEXES; 349 mtxreg = (DB_MUTEXREGION *)env->mutex_handle->reginfo.primary; 350 return (mtxreg->mtx_atomic[index]); 351} 352 353/* 354 * __atomic_inc 355 * Use a mutex to provide an atomic increment function 356 * 357 * PUBLIC: #if !defined(HAVE_ATOMIC_SUPPORT) && defined(HAVE_MUTEX_SUPPORT) 358 * PUBLIC: atomic_value_t __atomic_inc __P((ENV *, db_atomic_t *)); 359 * PUBLIC: #endif 360 */ 361atomic_value_t 362__atomic_inc(env, v) 363 ENV *env; 364 db_atomic_t *v; 365{ 366 db_mutex_t mtx; 367 int ret; 368 369 mtx = atomic_get_mutex(env, v); 370 MUTEX_LOCK(env, mtx); 371 ret = ++v->value; 372 MUTEX_UNLOCK(env, mtx); 373 374 return (ret); 375} 376 377/* 378 * __atomic_dec 379 * Use a mutex to provide an atomic decrement function 380 * 381 * PUBLIC: #if !defined(HAVE_ATOMIC_SUPPORT) && defined(HAVE_MUTEX_SUPPORT) 382 * PUBLIC: atomic_value_t __atomic_dec __P((ENV *, db_atomic_t *)); 383 * PUBLIC: #endif 384 */ 385atomic_value_t 386__atomic_dec(env, v) 387 ENV *env; 388 db_atomic_t *v; 389{ 390 db_mutex_t mtx; 391 int ret; 392 393 mtx = atomic_get_mutex(env, v); 394 MUTEX_LOCK(env, mtx); 395 ret = --v->value; 396 MUTEX_UNLOCK(env, mtx); 397 398 return (ret); 399} 400 401/* 402 * atomic_compare_exchange 403 * Use a mutex to provide an atomic decrement function 404 * 405 * PRIVATE: int atomic_compare_exchange 406 * PRIVATE: __P((ENV *, db_atomic_t *, atomic_value_t, atomic_value_t)); 407 * Returns 1 if the *v was equal to oldval, else 0 408 * 409 * Side Effect: 410 * Sets the value to newval if and only if returning 1 411 */ 412int 413atomic_compare_exchange(env, v, oldval, newval) 414 ENV *env; 415 db_atomic_t *v; 416 atomic_value_t oldval; 417 atomic_value_t newval; 418{ 419 db_mutex_t mtx; 420 int ret; 421 422 if (atomic_read(v) != oldval) 423 return (0); 424 425 mtx = atomic_get_mutex(env, v); 426 MUTEX_LOCK(env, mtx); 427 ret = atomic_read(v) == oldval; 428 if (ret) 429 atomic_init(v, newval); 430 MUTEX_UNLOCK(env, mtx); 431 432 return (ret); 433} 434#endif 435