summaryrefslogtreecommitdiff
path: root/src/mutex/mut_alloc.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/mutex/mut_alloc.c')
-rw-r--r--src/mutex/mut_alloc.c291
1 files changed, 291 insertions, 0 deletions
diff --git a/src/mutex/mut_alloc.c b/src/mutex/mut_alloc.c
new file mode 100644
index 00000000..5df3de53
--- /dev/null
+++ b/src/mutex/mut_alloc.c
@@ -0,0 +1,291 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 1999, 2012 Oracle and/or its affiliates. All rights reserved.
+ *
+ * $Id$
+ */
+
+#include "db_config.h"
+
+#include "db_int.h"
+
+/*
+ * __mutex_alloc --
+ * Allocate a mutex from the mutex region.
+ *
+ * PUBLIC: int __mutex_alloc __P((ENV *, int, u_int32_t, db_mutex_t *));
+ */
+int
+__mutex_alloc(env, alloc_id, flags, indxp)
+ ENV *env;
+ int alloc_id;
+ u_int32_t flags;
+ db_mutex_t *indxp;
+{
+ /* The caller may depend on us to initialize. */
+ *indxp = MUTEX_INVALID;
+
+ /*
+ * If this is not an application lock, and we've turned off locking,
+ * or the ENV handle isn't thread-safe, and this is a thread lock
+ * or the environment isn't multi-process by definition, there's no
+ * need to mutex at all.
+ */
+ if (alloc_id != MTX_APPLICATION && alloc_id != MTX_MUTEX_TEST &&
+ (F_ISSET(env->dbenv, DB_ENV_NOLOCKING) ||
+ (!F_ISSET(env, ENV_THREAD) &&
+ (LF_ISSET(DB_MUTEX_PROCESS_ONLY) ||
+ F_ISSET(env, ENV_PRIVATE)))))
+ return (0);
+
+ /* Private environments never share mutexes. */
+ if (F_ISSET(env, ENV_PRIVATE))
+ LF_SET(DB_MUTEX_PROCESS_ONLY);
+
+ /*
+ * If we have a region in which to allocate the mutexes, lock it and
+ * do the allocation.
+ */
+ if (!MUTEX_ON(env)) {
+ __db_errx(env, DB_STR("2033",
+ "Mutex allocated before mutex region."));
+ return (__env_panic(env, EINVAL));
+ }
+ return (__mutex_alloc_int(env, 1, alloc_id, flags, indxp));
+}
+
+/*
+ * __mutex_alloc_int --
+ * Internal routine to allocate a mutex.
+ *
+ * PUBLIC: int __mutex_alloc_int
+ * PUBLIC: __P((ENV *, int, int, u_int32_t, db_mutex_t *));
+ */
+int
+__mutex_alloc_int(env, locksys, alloc_id, flags, indxp)
+ ENV *env;
+ int locksys, alloc_id;
+ u_int32_t flags;
+ db_mutex_t *indxp;
+{
+ DB_ENV *dbenv;
+ DB_MUTEX *mutexp;
+ DB_MUTEXMGR *mtxmgr;
+ DB_MUTEXREGION *mtxregion;
+ db_mutex_t i;
+ size_t len;
+ u_int32_t cnt;
+ int ret;
+
+ dbenv = env->dbenv;
+ mtxmgr = env->mutex_handle;
+ mtxregion = mtxmgr->reginfo.primary;
+ ret = 0;
+
+ /*
+ * If we're not initializing the mutex region, then lock the region to
+ * allocate new mutexes. Drop the lock before initializing the mutex,
+ * mutex initialization may require a system call.
+ */
+ if (locksys)
+ MUTEX_SYSTEM_LOCK(env);
+
+ if (mtxregion->mutex_next == MUTEX_INVALID) {
+ if (mtxregion->stat.st_mutex_max != 0 &&
+ mtxregion->stat.st_mutex_cnt >=
+ mtxregion->stat.st_mutex_max) {
+nomem: __db_errx(env, DB_STR("2034",
+ "unable to allocate memory for mutex; resize mutex region"));
+ if (locksys)
+ MUTEX_SYSTEM_UNLOCK(env);
+ return (ret == 0 ? ENOMEM : ret);
+ }
+ cnt = mtxregion->stat.st_mutex_cnt / 2;
+ if (cnt < 8)
+ cnt = 8;
+ if (mtxregion->stat.st_mutex_max != 0 &&
+ mtxregion->stat.st_mutex_cnt + cnt >
+ mtxregion->stat.st_mutex_max)
+ cnt = mtxregion->stat.st_mutex_max -
+ mtxregion->stat.st_mutex_cnt;
+ if (F_ISSET(env, ENV_PRIVATE)) {
+ F_SET(&mtxmgr->reginfo, REGION_TRACKED);
+ while (__env_alloc(&mtxmgr->reginfo,
+ (cnt * mtxregion->mutex_size) +
+ mtxregion->stat.st_mutex_align, &i) != 0)
+ if ((cnt >> 1) == 0)
+ break;
+ F_CLR(&mtxmgr->reginfo, REGION_TRACKED);
+ i = (db_mutex_t)ALIGNP_INC(i,
+ mtxregion->stat.st_mutex_align);
+ } else {
+ len = cnt * mtxregion->mutex_size;
+ if ((ret = __env_alloc_extend(&mtxmgr->reginfo,
+ R_ADDR(&mtxmgr->reginfo,
+ mtxregion->mutex_off_alloc), &len)) != 0)
+ goto nomem;
+ cnt = (u_int32_t)(len / mtxregion->mutex_size);
+ i = mtxregion->stat.st_mutex_cnt + 1;
+ }
+ if (cnt == 0)
+ goto nomem;
+ mutexp = MUTEXP_SET(env, i);
+ mtxregion->stat.st_mutex_free = cnt;
+ mtxregion->mutex_next = i;
+ mtxregion->stat.st_mutex_cnt += cnt;
+ while (--cnt > 0) {
+ mutexp->flags = 0;
+ if (F_ISSET(env, ENV_PRIVATE))
+ mutexp->mutex_next_link =
+ (uintptr_t)(mutexp + 1);
+ else
+ mutexp->mutex_next_link = ++i;
+ mutexp++;
+ }
+ mutexp->flags = 0;
+ mutexp->mutex_next_link = MUTEX_INVALID;
+ }
+
+ *indxp = mtxregion->mutex_next;
+ mutexp = MUTEXP_SET(env, *indxp);
+ DB_ASSERT(env,
+ ((uintptr_t)mutexp & (dbenv->mutex_align - 1)) == 0);
+ mtxregion->mutex_next = mutexp->mutex_next_link;
+
+ --mtxregion->stat.st_mutex_free;
+ ++mtxregion->stat.st_mutex_inuse;
+ if (mtxregion->stat.st_mutex_inuse > mtxregion->stat.st_mutex_inuse_max)
+ mtxregion->stat.st_mutex_inuse_max =
+ mtxregion->stat.st_mutex_inuse;
+ if (locksys)
+ MUTEX_SYSTEM_UNLOCK(env);
+
+ /* Initialize the mutex. */
+ memset(mutexp, 0, sizeof(*mutexp));
+ F_SET(mutexp, DB_MUTEX_ALLOCATED |
+ LF_ISSET(DB_MUTEX_LOGICAL_LOCK |
+ DB_MUTEX_PROCESS_ONLY | DB_MUTEX_SHARED));
+
+ /*
+ * If the mutex is associated with a single process, set the process
+ * ID. If the application ever calls DbEnv::failchk, we'll need the
+ * process ID to know if the mutex is still in use.
+ */
+ if (LF_ISSET(DB_MUTEX_PROCESS_ONLY))
+ dbenv->thread_id(dbenv, &mutexp->pid, NULL);
+
+#ifdef HAVE_STATISTICS
+ mutexp->alloc_id = alloc_id;
+#else
+ COMPQUIET(alloc_id, 0);
+#endif
+
+ if ((ret = __mutex_init(env, *indxp, flags)) != 0)
+ (void)__mutex_free_int(env, locksys, indxp);
+
+ return (ret);
+}
+
+/*
+ * __mutex_free --
+ * Free a mutex.
+ *
+ * PUBLIC: int __mutex_free __P((ENV *, db_mutex_t *));
+ */
+int
+__mutex_free(env, indxp)
+ ENV *env;
+ db_mutex_t *indxp;
+{
+ /*
+ * There is no explicit ordering in how the regions are cleaned up
+ * up and/or discarded when an environment is destroyed (either a
+ * private environment is closed or a public environment is removed).
+ * The way we deal with mutexes is to clean up all remaining mutexes
+ * when we close the mutex environment (because we have to be able to
+ * do that anyway, after a crash), which means we don't have to deal
+ * with region cleanup ordering on normal environment destruction.
+ * All that said, what it really means is we can get here without a
+ * mpool region. It's OK, the mutex has been, or will be, destroyed.
+ *
+ * If the mutex has never been configured, we're done.
+ */
+ if (!MUTEX_ON(env) || *indxp == MUTEX_INVALID)
+ return (0);
+
+ return (__mutex_free_int(env, 1, indxp));
+}
+
+/*
+ * __mutex_free_int --
+ * Internal routine to free a mutex.
+ *
+ * PUBLIC: int __mutex_free_int __P((ENV *, int, db_mutex_t *));
+ */
+int
+__mutex_free_int(env, locksys, indxp)
+ ENV *env;
+ int locksys;
+ db_mutex_t *indxp;
+{
+ DB_MUTEX *mutexp;
+ DB_MUTEXMGR *mtxmgr;
+ DB_MUTEXREGION *mtxregion;
+ db_mutex_t mutex;
+ int ret;
+
+ mutex = *indxp;
+ *indxp = MUTEX_INVALID;
+
+ mtxmgr = env->mutex_handle;
+ mtxregion = mtxmgr->reginfo.primary;
+ mutexp = MUTEXP_SET(env, mutex);
+
+ DB_ASSERT(env, F_ISSET(mutexp, DB_MUTEX_ALLOCATED));
+ F_CLR(mutexp, DB_MUTEX_ALLOCATED);
+
+ ret = __mutex_destroy(env, mutex);
+
+ if (locksys)
+ MUTEX_SYSTEM_LOCK(env);
+
+ /* Link the mutex on the head of the free list. */
+ mutexp->mutex_next_link = mtxregion->mutex_next;
+ mtxregion->mutex_next = mutex;
+ ++mtxregion->stat.st_mutex_free;
+ --mtxregion->stat.st_mutex_inuse;
+
+ if (locksys)
+ MUTEX_SYSTEM_UNLOCK(env);
+
+ return (ret);
+}
+
+/*
+ * __mutex_refresh --
+ * Reinitialize a mutex, if we are not sure of its state.
+ *
+ * PUBLIC: int __mutex_refresh __P((ENV *, db_mutex_t));
+ */
+int
+__mutex_refresh(env, mutex)
+ ENV *env;
+ db_mutex_t mutex;
+{
+ DB_MUTEX *mutexp;
+ u_int32_t flags;
+ int ret;
+
+ mutexp = MUTEXP_SET(env, mutex);
+ flags = mutexp->flags;
+ if ((ret = __mutex_destroy(env, mutex)) == 0) {
+ memset(mutexp, 0, sizeof(*mutexp));
+ F_SET(mutexp, DB_MUTEX_ALLOCATED |
+ LF_ISSET(DB_MUTEX_LOGICAL_LOCK |
+ DB_MUTEX_PROCESS_ONLY | DB_MUTEX_SHARED));
+ LF_CLR(DB_MUTEX_LOCKED);
+ ret = __mutex_init(env, mutex, flags);
+ }
+ return (ret);
+}