diff options
Diffstat (limited to 'storage/bdb/mutex/mutex.c')
-rw-r--r-- | storage/bdb/mutex/mutex.c | 395 |
1 files changed, 395 insertions, 0 deletions
diff --git a/storage/bdb/mutex/mutex.c b/storage/bdb/mutex/mutex.c new file mode 100644 index 00000000000..5418764a889 --- /dev/null +++ b/storage/bdb/mutex/mutex.c @@ -0,0 +1,395 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 1999-2002 + * Sleepycat Software. All rights reserved. + */ + +#include "db_config.h" + +#ifndef lint +static const char revid[] = "$Id: mutex.c,v 11.37 2002/05/31 19:37:46 bostic Exp $"; +#endif /* not lint */ + +#ifndef NO_SYSTEM_INCLUDES +#include <sys/types.h> + +#include <string.h> +#endif + +#include "db_int.h" + +#if defined(MUTEX_NO_MALLOC_LOCKS) || defined(HAVE_MUTEX_SYSTEM_RESOURCES) +#include "dbinc/db_shash.h" +#include "dbinc/lock.h" +#include "dbinc/log.h" +#include "dbinc/mp.h" +#include "dbinc/txn.h" +#endif + +static int __db_mutex_alloc_int __P((DB_ENV *, REGINFO *, DB_MUTEX **)); +#ifdef HAVE_MUTEX_SYSTEM_RESOURCES +static REGMAINT * __db_mutex_maint __P((DB_ENV *, REGINFO *)); +#endif + +/* + * __db_mutex_setup -- + * External interface to allocate, and/or initialize, record + * mutexes. + * + * PUBLIC: int __db_mutex_setup __P((DB_ENV *, REGINFO *, void *, u_int32_t)); + */ +int +__db_mutex_setup(dbenv, infop, ptr, flags) + DB_ENV *dbenv; + REGINFO *infop; + void *ptr; + u_int32_t flags; +{ + DB_MUTEX *mutex; + REGMAINT *maint; + u_int32_t iflags, offset; + int ret; + + ret = 0; + /* + * If they indicated the region is not locked, then lock it. + * This is only needed when we have unusual mutex resources. + * (I.e. MUTEX_NO_MALLOC_LOCKS or HAVE_MUTEX_SYSTEM_RESOURCES) + */ +#if defined(MUTEX_NO_MALLOC_LOCKS) || defined(HAVE_MUTEX_SYSTEM_RESOURCES) + if (!LF_ISSET(MUTEX_NO_RLOCK)) + R_LOCK(dbenv, infop); +#endif + /* + * Allocate the mutex if they asked us to. + */ + mutex = NULL; + if (LF_ISSET(MUTEX_ALLOC)) { + if ((ret = __db_mutex_alloc_int(dbenv, infop, ptr)) != 0) + goto err; + mutex = *(DB_MUTEX **)ptr; + } else + mutex = (DB_MUTEX *)ptr; + + /* + * Set up to initialize the mutex. + */ + iflags = LF_ISSET(MUTEX_THREAD | MUTEX_SELF_BLOCK); + switch (infop->type) { + case REGION_TYPE_LOCK: + offset = P_TO_UINT32(mutex) + DB_FCNTL_OFF_LOCK; + break; + case REGION_TYPE_MPOOL: + offset = P_TO_UINT32(mutex) + DB_FCNTL_OFF_MPOOL; + break; + default: + offset = P_TO_UINT32(mutex) + DB_FCNTL_OFF_GEN; + break; + } + maint = NULL; +#ifdef HAVE_MUTEX_SYSTEM_RESOURCES + if (!LF_ISSET(MUTEX_NO_RECORD)) + maint = (REGMAINT *)__db_mutex_maint(dbenv, infop); +#endif + + ret = __db_mutex_init(dbenv, mutex, offset, iflags, infop, maint); +err: +#if defined(MUTEX_NO_MALLOC_LOCKS) || defined(HAVE_MUTEX_SYSTEM_RESOURCES) + if (!LF_ISSET(MUTEX_NO_RLOCK)) + R_UNLOCK(dbenv, infop); +#endif + /* + * If we allocated the mutex but had an error on init'ing, + * then we must free it before returning. + * !!! + * Free must be done after releasing region lock. + */ + if (ret != 0 && LF_ISSET(MUTEX_ALLOC) && mutex != NULL) { + __db_mutex_free(dbenv, infop, mutex); + *(DB_MUTEX **)ptr = NULL; + } + return (ret); +} + +/* + * __db_mutex_alloc_int -- + * Allocate and initialize a mutex. + */ +static int +__db_mutex_alloc_int(dbenv, infop, storep) + DB_ENV *dbenv; + REGINFO *infop; + DB_MUTEX **storep; +{ + int ret; + + /* + * If the architecture supports mutexes in heap memory, use heap memory. + * If it doesn't, we have to allocate space in a region. If allocation + * in the region fails, fallback to allocating from the mpool region, + * because it's big, it almost always exists and if it's entirely dirty, + * we can free buffers until memory is available. + */ +#if defined(MUTEX_NO_MALLOC_LOCKS) || defined(HAVE_MUTEX_SYSTEM_RESOURCES) + ret = __db_shalloc(infop->addr, sizeof(DB_MUTEX), MUTEX_ALIGN, storep); + + if (ret == ENOMEM && MPOOL_ON(dbenv)) { + DB_MPOOL *dbmp; + + dbmp = dbenv->mp_handle; + if ((ret = __memp_alloc(dbmp, + dbmp->reginfo, NULL, sizeof(DB_MUTEX), NULL, storep)) == 0) + (*storep)->flags = MUTEX_MPOOL; + } else + (*storep)->flags = 0; +#else + COMPQUIET(dbenv, NULL); + COMPQUIET(infop, NULL); + ret = __os_calloc(dbenv, 1, sizeof(DB_MUTEX), storep); +#endif + if (ret != 0) + __db_err(dbenv, "Unable to allocate memory for mutex"); + return (ret); +} + +/* + * __db_mutex_free -- + * Free a mutex. + * + * PUBLIC: void __db_mutex_free __P((DB_ENV *, REGINFO *, DB_MUTEX *)); + */ +void +__db_mutex_free(dbenv, infop, mutexp) + DB_ENV *dbenv; + REGINFO *infop; + DB_MUTEX *mutexp; +{ +#if defined(MUTEX_NO_MALLOC_LOCKS) || defined(HAVE_MUTEX_SYSTEM_RESOURCES) + R_LOCK(dbenv, infop); +#if defined(HAVE_MUTEX_SYSTEM_RESOURCES) + if (F_ISSET(mutexp, MUTEX_INITED)) + __db_shlocks_clear(mutexp, infop, NULL); +#endif + if (F_ISSET(mutexp, MUTEX_MPOOL)) { + DB_MPOOL *dbmp; + + dbmp = dbenv->mp_handle; + R_LOCK(dbenv, dbmp->reginfo); + __db_shalloc_free(dbmp->reginfo[0].addr, mutexp); + R_UNLOCK(dbenv, dbmp->reginfo); + } else + __db_shalloc_free(infop->addr, mutexp); + R_UNLOCK(dbenv, infop); +#else + COMPQUIET(dbenv, NULL); + COMPQUIET(infop, NULL); + __os_free(dbenv, mutexp); +#endif +} + +#ifdef HAVE_MUTEX_SYSTEM_RESOURCES +/* + * __db_shreg_locks_record -- + * Record an entry in the shared locks area. + * Region lock must be held in caller. + */ +static int +__db_shreg_locks_record(dbenv, mutexp, infop, rp) + DB_ENV *dbenv; + DB_MUTEX *mutexp; + REGINFO *infop; + REGMAINT *rp; +{ + u_int i; + + if (!F_ISSET(mutexp, MUTEX_INITED)) + return (0); + DB_ASSERT(mutexp->reg_off == INVALID_ROFF); + rp->stat.st_records++; + i = (roff_t *)R_ADDR(infop, rp->regmutex_hint) - &rp->regmutexes[0]; + if (rp->regmutexes[i] != INVALID_ROFF) { + /* + * Our hint failed, search for an open slot. + */ + rp->stat.st_hint_miss++; + for (i = 0; i < rp->reglocks; i++) + if (rp->regmutexes[i] == INVALID_ROFF) + break; + if (i == rp->reglocks) { + rp->stat.st_max_locks++; + __db_err(dbenv, + "Region mutexes: Exceeded maximum lock slots %lu", + (u_long)rp->reglocks); + return (ENOMEM); + } + } else + rp->stat.st_hint_hit++; + /* + * When we get here, i is an empty slot. Record this + * mutex, set hint to point to the next slot and we are done. + */ + rp->regmutexes[i] = R_OFFSET(infop, mutexp); + mutexp->reg_off = R_OFFSET(infop, &rp->regmutexes[i]); + rp->regmutex_hint = (i < rp->reglocks - 1) ? + R_OFFSET(infop, &rp->regmutexes[i+1]) : + R_OFFSET(infop, &rp->regmutexes[0]); + return (0); +} + +/* + * __db_shreg_locks_clear -- + * Erase an entry in the shared locks area. + * + * PUBLIC: void __db_shreg_locks_clear __P((DB_MUTEX *, REGINFO *, REGMAINT *)); + */ +void +__db_shreg_locks_clear(mutexp, infop, rp) + DB_MUTEX *mutexp; + REGINFO *infop; + REGMAINT *rp; +{ + /* + * !!! + * Assumes the caller's region lock is held. + */ + if (!F_ISSET(mutexp, MUTEX_INITED)) + return; + /* + * This function is generally only called on a forcible remove of an + * environment. We recorded our index in the mutex, find and clear it. + */ + DB_ASSERT(mutexp->reg_off != INVALID_ROFF); + DB_ASSERT(*(roff_t *)R_ADDR(infop, mutexp->reg_off) == \ + R_OFFSET(infop, mutexp)); + *(roff_t *)R_ADDR(infop, mutexp->reg_off) = 0; + if (rp != NULL) { + rp->regmutex_hint = mutexp->reg_off; + rp->stat.st_clears++; + } + mutexp->reg_off = INVALID_ROFF; + __db_mutex_destroy(mutexp); +} + +/* + * __db_shreg_locks_destroy -- + * Destroy all mutexes in a region's range. + * + * PUBLIC: void __db_shreg_locks_destroy __P((REGINFO *, REGMAINT *)); + */ +void +__db_shreg_locks_destroy(infop, rp) + REGINFO *infop; + REGMAINT *rp; +{ + u_int32_t i; + + /* + * Go through the list of all mutexes and destroy them. + */ + for (i = 0; i < rp->reglocks; i++) + if (rp->regmutexes[i] != 0) { + rp->stat.st_destroys++; + __db_mutex_destroy((DB_MUTEX *)R_ADDR(infop, + rp->regmutexes[i])); + } +} + +/* + * __db_shreg_mutex_init -- + * Initialize a shared memory mutex. + * + * PUBLIC: int __db_shreg_mutex_init __P((DB_ENV *, DB_MUTEX *, u_int32_t, + * PUBLIC: u_int32_t, REGINFO *, REGMAINT *)); + */ +int +__db_shreg_mutex_init(dbenv, mutexp, offset, flags, infop, rp) + DB_ENV *dbenv; + DB_MUTEX *mutexp; + u_int32_t offset; + u_int32_t flags; + REGINFO *infop; + REGMAINT *rp; +{ + int ret; + + if ((ret = __db_mutex_init_int(dbenv, mutexp, offset, flags)) != 0) + return (ret); + /* + * Some mutexes cannot be recorded, but we want one interface. + * So, if we have no REGMAINT, then just return. + */ + if (rp == NULL) + return (ret); + /* + * !!! + * Since __db_mutex_init_int is a macro, we may not be + * using the 'offset' as it is only used for one type + * of mutex. We COMPQUIET it here, after the call above. + */ + COMPQUIET(offset, 0); + ret = __db_shreg_locks_record(dbenv, mutexp, infop, rp); + + /* + * If we couldn't record it and we are returning an error, + * we need to destroy the mutex we just created. + */ + if (ret) + __db_mutex_destroy(mutexp); + + return (ret); +} + +/* + * __db_shreg_maintinit -- + * Initialize a region's maintenance information. + * + * PUBLIC: void __db_shreg_maintinit __P((REGINFO *, void *addr, size_t)); + */ +void +__db_shreg_maintinit(infop, addr, size) + REGINFO *infop; + void *addr; + size_t size; +{ + REGMAINT *rp; + u_int32_t i; + + rp = (REGMAINT *)addr; + memset(addr, 0, sizeof(REGMAINT)); + rp->reglocks = size / sizeof(roff_t); + rp->regmutex_hint = R_OFFSET(infop, &rp->regmutexes[0]); + for (i = 0; i < rp->reglocks; i++) + rp->regmutexes[i] = INVALID_ROFF; +} + +static REGMAINT * +__db_mutex_maint(dbenv, infop) + DB_ENV *dbenv; + REGINFO *infop; +{ + roff_t moff; + + switch (infop->type) { + case REGION_TYPE_LOCK: + moff = ((DB_LOCKREGION *)R_ADDR(infop, + infop->rp->primary))->maint_off; + break; + case REGION_TYPE_LOG: + moff = ((LOG *)R_ADDR(infop, infop->rp->primary))->maint_off; + break; + case REGION_TYPE_MPOOL: + moff = ((MPOOL *)R_ADDR(infop, infop->rp->primary))->maint_off; + break; + case REGION_TYPE_TXN: + moff = ((DB_TXNREGION *)R_ADDR(infop, + infop->rp->primary))->maint_off; + break; + default: + __db_err(dbenv, + "Attempting to record mutex in a region not set up to do so"); + return (NULL); + } + return ((REGMAINT *)R_ADDR(infop, moff)); +} +#endif /* HAVE_MUTEX_SYSTEM_RESOURCES */ |