Files
cpython-source-deps/mutex/mut_region.c
2017-09-04 13:40:25 -05:00

381 lines
10 KiB
C

/*-
* See the file LICENSE for redistribution information.
*
* Copyright (c) 1996,2008 Oracle. All rights reserved.
*
* $Id: mut_region.c 63573 2008-05-23 21:43:21Z trent.nelson $
*/
#include "db_config.h"
#include "db_int.h"
#include "dbinc/log.h"
#include "dbinc/lock.h"
#include "dbinc/mp.h"
#include "dbinc/txn.h"
#include "dbinc/mutex_int.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;
/*
* 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_free(env, &mutex)) != 0) {
__db_errx(env,
"Unable to acquire/release a mutex; check configuration");
goto err;
}
}
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);
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(i);
mutexp->flags = 0;
mutexp->mutex_next_link = i + 1;
}
mutexp = MUTEXP_SET(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)) {
#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(i);
if (F_ISSET(mutexp, DB_MUTEX_ALLOCATED))
(void)__mutex_destroy(env, i);
}
env->mutex_handle = orig_handle;
}
#endif