summaryrefslogtreecommitdiff
path: root/storage/bdb/mutex/mutex.c
diff options
context:
space:
mode:
Diffstat (limited to 'storage/bdb/mutex/mutex.c')
-rw-r--r--storage/bdb/mutex/mutex.c395
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 */