summaryrefslogtreecommitdiff
path: root/db-4.8.30/mutex/mut_region.c
diff options
context:
space:
mode:
Diffstat (limited to 'db-4.8.30/mutex/mut_region.c')
-rw-r--r--db-4.8.30/mutex/mut_region.c407
1 files changed, 407 insertions, 0 deletions
diff --git a/db-4.8.30/mutex/mut_region.c b/db-4.8.30/mutex/mut_region.c
new file mode 100644
index 0000000..e985ac2
--- /dev/null
+++ b/db-4.8.30/mutex/mut_region.c
@@ -0,0 +1,407 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 1996-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+#include "db_config.h"
+
+#include "db_int.h"
+#include "dbinc/log.h"
+#include "dbinc/lock.h"
+#include "dbinc/mp.h"
+#include "dbinc/txn.h"
+
+static size_t __mutex_align_size __P((ENV *));
+static int __mutex_region_init __P((ENV *, DB_MUTEXMGR *));
+static size_t __mutex_region_size __P((ENV *));
+
+/*
+ * __mutex_open --
+ * Open a mutex region.
+ *
+ * PUBLIC: int __mutex_open __P((ENV *, int));
+ */
+int
+__mutex_open(env, create_ok)
+ ENV *env;
+ int create_ok;
+{
+ DB_ENV *dbenv;
+ DB_MUTEXMGR *mtxmgr;
+ DB_MUTEXREGION *mtxregion;
+ db_mutex_t mutex;
+ u_int32_t cpu_count;
+ u_int i;
+ int ret;
+
+ dbenv = env->dbenv;
+
+ /*
+ * Initialize the ENV handle information if not already initialized.
+ *
+ * Align mutexes on the byte boundaries specified by the application.
+ */
+ if (dbenv->mutex_align == 0)
+ dbenv->mutex_align = MUTEX_ALIGN;
+ if (dbenv->mutex_tas_spins == 0) {
+ cpu_count = __os_cpu_count();
+ if ((ret = __mutex_set_tas_spins(dbenv, cpu_count == 1 ?
+ cpu_count : cpu_count * MUTEX_SPINS_PER_PROCESSOR)) != 0)
+ return (ret);
+ }
+
+ /*
+ * If the user didn't set an absolute value on the number of mutexes
+ * we'll need, figure it out. We're conservative in our allocation,
+ * we need mutexes for DB handles, group-commit queues and other things
+ * applications allocate at run-time. The application may have kicked
+ * up our count to allocate its own mutexes, add that in.
+ */
+ if (dbenv->mutex_cnt == 0)
+ dbenv->mutex_cnt =
+ __lock_region_mutex_count(env) +
+ __log_region_mutex_count(env) +
+ __memp_region_mutex_count(env) +
+ __txn_region_mutex_count(env) +
+ dbenv->mutex_inc + 100;
+
+ /* Create/initialize the mutex manager structure. */
+ if ((ret = __os_calloc(env, 1, sizeof(DB_MUTEXMGR), &mtxmgr)) != 0)
+ return (ret);
+
+ /* Join/create the mutex region. */
+ mtxmgr->reginfo.env = env;
+ mtxmgr->reginfo.type = REGION_TYPE_MUTEX;
+ mtxmgr->reginfo.id = INVALID_REGION_ID;
+ mtxmgr->reginfo.flags = REGION_JOIN_OK;
+ if (create_ok)
+ F_SET(&mtxmgr->reginfo, REGION_CREATE_OK);
+ if ((ret = __env_region_attach(env,
+ &mtxmgr->reginfo, __mutex_region_size(env))) != 0)
+ goto err;
+
+ /* If we created the region, initialize it. */
+ if (F_ISSET(&mtxmgr->reginfo, REGION_CREATE))
+ if ((ret = __mutex_region_init(env, mtxmgr)) != 0)
+ goto err;
+
+ /* Set the local addresses. */
+ mtxregion = mtxmgr->reginfo.primary =
+ R_ADDR(&mtxmgr->reginfo, mtxmgr->reginfo.rp->primary);
+ mtxmgr->mutex_array = R_ADDR(&mtxmgr->reginfo, mtxregion->mutex_off);
+
+ env->mutex_handle = mtxmgr;
+
+ /* Allocate initial queue of mutexes. */
+ if (env->mutex_iq != NULL) {
+ DB_ASSERT(env, F_ISSET(&mtxmgr->reginfo, REGION_CREATE));
+ for (i = 0; i < env->mutex_iq_next; ++i) {
+ if ((ret = __mutex_alloc_int(
+ env, 0, env->mutex_iq[i].alloc_id,
+ env->mutex_iq[i].flags, &mutex)) != 0)
+ goto err;
+ /*
+ * Confirm we allocated the right index, correcting
+ * for avoiding slot 0 (MUTEX_INVALID).
+ */
+ DB_ASSERT(env, mutex == i + 1);
+ }
+ __os_free(env, env->mutex_iq);
+ env->mutex_iq = NULL;
+#ifndef HAVE_ATOMIC_SUPPORT
+ /* If necessary allocate the atomic emulation mutexes. */
+ for (i = 0; i != MAX_ATOMIC_MUTEXES; i++)
+ if ((ret = __mutex_alloc_int(
+ env, 0, MTX_ATOMIC_EMULATION,
+ 0, &mtxregion->mtx_atomic[i])) != 0)
+ return (ret);
+#endif
+
+ /*
+ * This is the first place we can test mutexes and we need to
+ * know if they're working. (They CAN fail, for example on
+ * SunOS, when using fcntl(2) for locking and using an
+ * in-memory filesystem as the database environment directory.
+ * But you knew that, I'm sure -- it probably wasn't worth
+ * mentioning.)
+ */
+ mutex = MUTEX_INVALID;
+ if ((ret =
+ __mutex_alloc(env, MTX_MUTEX_TEST, 0, &mutex) != 0) ||
+ (ret = __mutex_lock(env, mutex)) != 0 ||
+ (ret = __mutex_unlock(env, mutex)) != 0 ||
+ (ret = __mutex_trylock(env, mutex)) != 0 ||
+ (ret = __mutex_unlock(env, mutex)) != 0 ||
+ (ret = __mutex_free(env, &mutex)) != 0) {
+ __db_errx(env,
+ "Unable to acquire/release a mutex; check configuration");
+ goto err;
+ }
+#ifdef HAVE_SHARED_LATCHES
+ if ((ret =
+ __mutex_alloc(env,
+ MTX_MUTEX_TEST, DB_MUTEX_SHARED, &mutex) != 0) ||
+ (ret = __mutex_lock(env, mutex)) != 0 ||
+ (ret = __mutex_unlock(env, mutex)) != 0 ||
+ (ret = __mutex_rdlock(env, mutex)) != 0 ||
+ (ret = __mutex_rdlock(env, mutex)) != 0 ||
+ (ret = __mutex_unlock(env, mutex)) != 0 ||
+ (ret = __mutex_unlock(env, mutex)) != 0 ||
+ (ret = __mutex_free(env, &mutex)) != 0) {
+ __db_errx(env,
+ "Unable to acquire/release a shared latch; check configuration");
+ goto err;
+ }
+#endif
+ }
+ return (0);
+
+err: env->mutex_handle = NULL;
+ if (mtxmgr->reginfo.addr != NULL)
+ (void)__env_region_detach(env, &mtxmgr->reginfo, 0);
+
+ __os_free(env, mtxmgr);
+ return (ret);
+}
+
+/*
+ * __mutex_region_init --
+ * Initialize a mutex region in shared memory.
+ */
+static int
+__mutex_region_init(env, mtxmgr)
+ ENV *env;
+ DB_MUTEXMGR *mtxmgr;
+{
+ DB_ENV *dbenv;
+ DB_MUTEX *mutexp;
+ DB_MUTEXREGION *mtxregion;
+ db_mutex_t i;
+ int ret;
+ void *mutex_array;
+
+ dbenv = env->dbenv;
+
+ COMPQUIET(mutexp, NULL);
+
+ if ((ret = __env_alloc(&mtxmgr->reginfo,
+ sizeof(DB_MUTEXREGION), &mtxmgr->reginfo.primary)) != 0) {
+ __db_errx(env,
+ "Unable to allocate memory for the mutex region");
+ return (ret);
+ }
+ mtxmgr->reginfo.rp->primary =
+ R_OFFSET(&mtxmgr->reginfo, mtxmgr->reginfo.primary);
+ mtxregion = mtxmgr->reginfo.primary;
+ memset(mtxregion, 0, sizeof(*mtxregion));
+
+ if ((ret = __mutex_alloc(
+ env, MTX_MUTEX_REGION, 0, &mtxregion->mtx_region)) != 0)
+ return (ret);
+ mtxmgr->reginfo.mtx_alloc = mtxregion->mtx_region;
+
+ mtxregion->mutex_size = __mutex_align_size(env);
+
+ mtxregion->stat.st_mutex_align = dbenv->mutex_align;
+ mtxregion->stat.st_mutex_cnt = dbenv->mutex_cnt;
+ mtxregion->stat.st_mutex_tas_spins = dbenv->mutex_tas_spins;
+
+ /*
+ * Get a chunk of memory to be used for the mutexes themselves. Each
+ * piece of the memory must be properly aligned, and that alignment
+ * may be more restrictive than the memory alignment returned by the
+ * underlying allocation code. We already know how much memory each
+ * mutex in the array will take up, but we need to offset the first
+ * mutex in the array so the array begins properly aligned.
+ *
+ * The OOB mutex (MUTEX_INVALID) is 0. To make this work, we ignore
+ * the first allocated slot when we build the free list. We have to
+ * correct the count by 1 here, though, otherwise our counter will be
+ * off by 1.
+ */
+ if ((ret = __env_alloc(&mtxmgr->reginfo,
+ mtxregion->stat.st_mutex_align +
+ (mtxregion->stat.st_mutex_cnt + 1) * mtxregion->mutex_size,
+ &mutex_array)) != 0) {
+ __db_errx(env,
+ "Unable to allocate memory for mutexes from the region");
+ return (ret);
+ }
+
+ mtxregion->mutex_off_alloc = R_OFFSET(&mtxmgr->reginfo, mutex_array);
+ mutex_array = ALIGNP_INC(mutex_array, mtxregion->stat.st_mutex_align);
+ mtxregion->mutex_off = R_OFFSET(&mtxmgr->reginfo, mutex_array);
+ mtxmgr->mutex_array = mutex_array;
+
+ /*
+ * Put the mutexes on a free list and clear the allocated flag.
+ *
+ * The OOB mutex (MUTEX_INVALID) is 0, skip it.
+ *
+ * The comparison is <, not <=, because we're looking ahead one
+ * in each link.
+ */
+ for (i = 1; i < mtxregion->stat.st_mutex_cnt; ++i) {
+ mutexp = MUTEXP_SET(mtxmgr, i);
+ mutexp->flags = 0;
+ mutexp->mutex_next_link = i + 1;
+ }
+ mutexp = MUTEXP_SET(mtxmgr, i);
+ mutexp->flags = 0;
+ mutexp->mutex_next_link = MUTEX_INVALID;
+ mtxregion->mutex_next = 1;
+ mtxregion->stat.st_mutex_free = mtxregion->stat.st_mutex_cnt;
+ mtxregion->stat.st_mutex_inuse = mtxregion->stat.st_mutex_inuse_max = 0;
+
+ return (0);
+}
+
+/*
+ * __mutex_env_refresh --
+ * Clean up after the mutex region on a close or failed open.
+ *
+ * PUBLIC: int __mutex_env_refresh __P((ENV *));
+ */
+int
+__mutex_env_refresh(env)
+ ENV *env;
+{
+ DB_MUTEXMGR *mtxmgr;
+ DB_MUTEXREGION *mtxregion;
+ REGINFO *reginfo;
+ int ret;
+
+ mtxmgr = env->mutex_handle;
+ reginfo = &mtxmgr->reginfo;
+ mtxregion = mtxmgr->reginfo.primary;
+
+ /*
+ * If a private region, return the memory to the heap. Not needed for
+ * filesystem-backed or system shared memory regions, that memory isn't
+ * owned by any particular process.
+ */
+ if (F_ISSET(env, ENV_PRIVATE)) {
+ reginfo->mtx_alloc = MUTEX_INVALID;
+
+#ifdef HAVE_MUTEX_SYSTEM_RESOURCES
+ /*
+ * If destroying the mutex region, return any system resources
+ * to the system.
+ */
+ __mutex_resource_return(env, reginfo);
+#endif
+ /* Discard the mutex array. */
+ __env_alloc_free(
+ reginfo, R_ADDR(reginfo, mtxregion->mutex_off_alloc));
+ }
+
+ /* Detach from the region. */
+ ret = __env_region_detach(env, reginfo, 0);
+
+ __os_free(env, mtxmgr);
+
+ env->mutex_handle = NULL;
+
+ return (ret);
+}
+
+/*
+ * __mutex_align_size --
+ * Return how much memory each mutex will take up if an array of them
+ * are to be properly aligned, individually, within the array.
+ */
+static size_t
+__mutex_align_size(env)
+ ENV *env;
+{
+ DB_ENV *dbenv;
+
+ dbenv = env->dbenv;
+
+ return ((size_t)DB_ALIGN(sizeof(DB_MUTEX), dbenv->mutex_align));
+}
+
+/*
+ * __mutex_region_size --
+ * Return the amount of space needed for the mutex region.
+ */
+static size_t
+__mutex_region_size(env)
+ ENV *env;
+{
+ DB_ENV *dbenv;
+ size_t s;
+
+ dbenv = env->dbenv;
+
+ s = sizeof(DB_MUTEXMGR) + 1024;
+
+ /* We discard one mutex for the OOB slot. */
+ s += __env_alloc_size(
+ (dbenv->mutex_cnt + 1) *__mutex_align_size(env));
+
+ return (s);
+}
+
+#ifdef HAVE_MUTEX_SYSTEM_RESOURCES
+/*
+ * __mutex_resource_return
+ * Return any system-allocated mutex resources to the system.
+ *
+ * PUBLIC: void __mutex_resource_return __P((ENV *, REGINFO *));
+ */
+void
+__mutex_resource_return(env, infop)
+ ENV *env;
+ REGINFO *infop;
+{
+ DB_MUTEX *mutexp;
+ DB_MUTEXMGR *mtxmgr, mtxmgr_st;
+ DB_MUTEXREGION *mtxregion;
+ db_mutex_t i;
+ void *orig_handle;
+
+ /*
+ * This routine is called in two cases: when discarding the regions
+ * from a previous Berkeley DB run, during recovery, and two, when
+ * discarding regions as we shut down the database environment.
+ *
+ * Walk the list of mutexes and destroy any live ones.
+ *
+ * This is just like joining a region -- the REGINFO we're handed is
+ * the same as the one returned by __env_region_attach(), all we have
+ * to do is fill in the links.
+ *
+ * !!!
+ * The region may be corrupted, of course. We're safe because the
+ * only things we look at are things that are initialized when the
+ * region is created, and never modified after that.
+ */
+ memset(&mtxmgr_st, 0, sizeof(mtxmgr_st));
+ mtxmgr = &mtxmgr_st;
+ mtxmgr->reginfo = *infop;
+ mtxregion = mtxmgr->reginfo.primary =
+ R_ADDR(&mtxmgr->reginfo, mtxmgr->reginfo.rp->primary);
+ mtxmgr->mutex_array = R_ADDR(&mtxmgr->reginfo, mtxregion->mutex_off);
+
+ /*
+ * This is a little strange, but the mutex_handle is what all of the
+ * underlying mutex routines will use to determine if they should do
+ * any work and to find their information. Save/restore the handle
+ * around the work loop.
+ *
+ * The OOB mutex (MUTEX_INVALID) is 0, skip it.
+ */
+ orig_handle = env->mutex_handle;
+ env->mutex_handle = mtxmgr;
+ for (i = 1; i <= mtxregion->stat.st_mutex_cnt; ++i, ++mutexp) {
+ mutexp = MUTEXP_SET(mtxmgr, i);
+ if (F_ISSET(mutexp, DB_MUTEX_ALLOCATED))
+ (void)__mutex_destroy(env, i);
+ }
+ env->mutex_handle = orig_handle;
+}
+#endif