#include "db_config.h"
#include "db_int.h"
#include "dbinc/lock.h"
#include "dbinc/log.h"
static int __lock_freelocker_int
__P((DB_LOCKTAB *, DB_LOCKREGION *, DB_LOCKER *, int));
int
__lock_id_pp(dbenv, idp)
DB_ENV *dbenv;
u_int32_t *idp;
{
DB_THREAD_INFO *ip;
ENV *env;
int ret;
env = dbenv->env;
ENV_REQUIRES_CONFIG(env,
env->lk_handle, "DB_ENV->lock_id", DB_INIT_LOCK);
ENV_ENTER(env, ip);
REPLICATION_WRAP(env, (__lock_id(env, idp, NULL)), 0, ret);
ENV_LEAVE(env, ip);
return (ret);
}
int
__lock_id(env, idp, lkp)
ENV *env;
u_int32_t *idp;
DB_LOCKER **lkp;
{
DB_LOCKER *lk;
DB_LOCKREGION *region;
DB_LOCKTAB *lt;
u_int32_t id, *ids;
int nids, ret;
lk = NULL;
lt = env->lk_handle;
region = lt->reginfo.primary;
id = DB_LOCK_INVALIDID;
ret = 0;
id = DB_LOCK_INVALIDID;
lk = NULL;
LOCK_LOCKERS(env, region);
if (region->lock_id == DB_LOCK_MAXID &&
region->cur_maxid != DB_LOCK_MAXID)
region->lock_id = DB_LOCK_INVALIDID;
if (region->lock_id == region->cur_maxid) {
if ((ret = __os_malloc(env,
sizeof(u_int32_t) * region->nlockers, &ids)) != 0)
goto err;
nids = 0;
SH_TAILQ_FOREACH(lk, ®ion->lockers, ulinks, __db_locker)
ids[nids++] = lk->id;
region->lock_id = DB_LOCK_INVALIDID;
region->cur_maxid = DB_LOCK_MAXID;
if (nids != 0)
__db_idspace(ids, nids,
®ion->lock_id, ®ion->cur_maxid);
__os_free(env, ids);
}
id = ++region->lock_id;
ret = __lock_getlocker_int(lt, id, 1, &lk);
err: UNLOCK_LOCKERS(env, region);
if (idp != NULL)
*idp = id;
if (lkp != NULL)
*lkp = lk;
return (ret);
}
void
__lock_set_thread_id(lref_arg, pid, tid)
void *lref_arg;
pid_t pid;
db_threadid_t tid;
{
DB_LOCKER *lref;
lref = lref_arg;
lref->pid = pid;
lref->tid = tid;
}
int
__lock_id_free_pp(dbenv, id)
DB_ENV *dbenv;
u_int32_t id;
{
DB_LOCKER *sh_locker;
DB_LOCKREGION *region;
DB_LOCKTAB *lt;
DB_THREAD_INFO *ip;
ENV *env;
int handle_check, ret, t_ret;
env = dbenv->env;
ENV_REQUIRES_CONFIG(env,
env->lk_handle, "DB_ENV->lock_id_free", DB_INIT_LOCK);
ENV_ENTER(env, ip);
handle_check = IS_ENV_REPLICATED(env);
if (handle_check && (ret = __env_rep_enter(env, 0)) != 0) {
handle_check = 0;
goto err;
}
lt = env->lk_handle;
region = lt->reginfo.primary;
LOCK_LOCKERS(env, region);
if ((ret =
__lock_getlocker_int(env->lk_handle, id, 0, &sh_locker)) == 0) {
if (sh_locker != NULL)
ret = __lock_freelocker_int(lt, region, sh_locker, 1);
else {
__db_errx(env, DB_STR_A("2045",
"Unknown locker id: %lx", "%lx"), (u_long)id);
ret = EINVAL;
}
}
UNLOCK_LOCKERS(env, region);
if (handle_check && (t_ret = __env_db_rep_exit(env)) != 0 && ret == 0)
ret = t_ret;
err: ENV_LEAVE(env, ip);
return (ret);
}
int
__lock_id_free(env, sh_locker)
ENV *env;
DB_LOCKER *sh_locker;
{
DB_LOCKREGION *region;
DB_LOCKTAB *lt;
int ret;
lt = env->lk_handle;
region = lt->reginfo.primary;
ret = 0;
if (sh_locker->nlocks != 0) {
__db_errx(env, DB_STR("2046",
"Locker still has locks"));
ret = EINVAL;
goto err;
}
LOCK_LOCKERS(env, region);
ret = __lock_freelocker_int(lt, region, sh_locker, 1);
UNLOCK_LOCKERS(env, region);
err: return (ret);
}
int
__lock_id_set(env, cur_id, max_id)
ENV *env;
u_int32_t cur_id, max_id;
{
DB_LOCKREGION *region;
DB_LOCKTAB *lt;
ENV_REQUIRES_CONFIG(env,
env->lk_handle, "lock_id_set", DB_INIT_LOCK);
lt = env->lk_handle;
region = lt->reginfo.primary;
region->lock_id = cur_id;
region->cur_maxid = max_id;
return (0);
}
int
__lock_getlocker(lt, locker, create, retp)
DB_LOCKTAB *lt;
u_int32_t locker;
int create;
DB_LOCKER **retp;
{
DB_LOCKREGION *region;
ENV *env;
int ret;
COMPQUIET(region, NULL);
env = lt->env;
region = lt->reginfo.primary;
LOCK_LOCKERS(env, region);
ret = __lock_getlocker_int(lt, locker, create, retp);
UNLOCK_LOCKERS(env, region);
return (ret);
}
int
__lock_getlocker_int(lt, locker, create, retp)
DB_LOCKTAB *lt;
u_int32_t locker;
int create;
DB_LOCKER **retp;
{
DB_LOCKER *sh_locker;
DB_LOCKREGION *region;
DB_THREAD_INFO *ip;
ENV *env;
db_mutex_t mutex;
u_int32_t i, indx, nlockers;
int ret;
env = lt->env;
region = lt->reginfo.primary;
LOCKER_HASH(lt, region, locker, indx);
SH_TAILQ_FOREACH(sh_locker, <->locker_tab[indx], links, __db_locker)
if (sh_locker->id == locker)
break;
if (sh_locker == NULL && create) {
nlockers = 0;
if ((ret = __mutex_alloc(env, MTX_LOGICAL_LOCK,
DB_MUTEX_LOGICAL_LOCK | DB_MUTEX_SELF_BLOCK,
&mutex)) != 0)
return (ret);
else
MUTEX_LOCK(env, mutex);
if ((sh_locker = SH_TAILQ_FIRST(
®ion->free_lockers, __db_locker)) == NULL) {
nlockers = region->stat.st_lockers >> 2;
if (nlockers == 0)
nlockers = 1;
if (region->stat.st_maxlockers != 0 &&
region->stat.st_maxlockers <
region->stat.st_lockers + nlockers)
nlockers = region->stat.st_maxlockers -
region->stat.st_lockers;
UNLOCK_LOCKERS(env, region);
LOCK_REGION_LOCK(env);
F_SET(<->reginfo, REGION_TRACKED);
while (__env_alloc(<->reginfo, nlockers *
sizeof(struct __db_locker), &sh_locker) != 0)
if ((nlockers >> 1) == 0)
break;
F_CLR(<->reginfo, REGION_TRACKED);
LOCK_REGION_UNLOCK(lt->env);
LOCK_LOCKERS(env, region);
for (i = 0; i < nlockers; i++) {
SH_TAILQ_INSERT_HEAD(®ion->free_lockers,
sh_locker, links, __db_locker);
sh_locker++;
}
if (nlockers == 0)
return (__lock_nomem(env, "locker entries"));
region->stat.st_lockers += nlockers;
sh_locker = SH_TAILQ_FIRST(
®ion->free_lockers, __db_locker);
}
SH_TAILQ_REMOVE(
®ion->free_lockers, sh_locker, links, __db_locker);
++region->nlockers;
#ifdef HAVE_STATISTICS
STAT_PERFMON2(env, lock, nlockers, region->nlockers, locker);
if (region->nlockers > region->stat.st_maxnlockers)
STAT_SET(env, lock, maxnlockers,
region->stat.st_maxnlockers,
region->nlockers, locker);
#endif
sh_locker->id = locker;
env->dbenv->thread_id(
env->dbenv, &sh_locker->pid, &sh_locker->tid);
sh_locker->mtx_locker = mutex;
sh_locker->dd_id = 0;
sh_locker->master_locker = INVALID_ROFF;
sh_locker->parent_locker = INVALID_ROFF;
SH_LIST_INIT(&sh_locker->child_locker);
sh_locker->flags = 0;
SH_LIST_INIT(&sh_locker->heldby);
sh_locker->nlocks = 0;
sh_locker->nwrites = 0;
sh_locker->priority = DB_LOCK_DEFPRIORITY;
sh_locker->lk_timeout = 0;
timespecclear(&sh_locker->tx_expire);
timespecclear(&sh_locker->lk_expire);
SH_TAILQ_INSERT_HEAD(
<->locker_tab[indx], sh_locker, links, __db_locker);
SH_TAILQ_INSERT_HEAD(®ion->lockers,
sh_locker, ulinks, __db_locker);
ENV_GET_THREAD_INFO(env, ip);
#ifdef DIAGNOSTIC
if (ip != NULL)
ip->dbth_locker = R_OFFSET(<->reginfo, sh_locker);
#endif
}
*retp = sh_locker;
return (0);
}
int
__lock_addfamilylocker(env, pid, id, is_family)
ENV *env;
u_int32_t pid, id, is_family;
{
DB_LOCKER *lockerp, *mlockerp;
DB_LOCKREGION *region;
DB_LOCKTAB *lt;
int ret;
COMPQUIET(region, NULL);
lt = env->lk_handle;
region = lt->reginfo.primary;
LOCK_LOCKERS(env, region);
if ((ret = __lock_getlocker_int(lt, pid, 1, &mlockerp)) != 0)
goto err;
if ((ret = __lock_getlocker_int(lt, id, 1, &lockerp)) != 0)
goto err;
lockerp->parent_locker = R_OFFSET(<->reginfo, mlockerp);
if (mlockerp->master_locker == INVALID_ROFF)
lockerp->master_locker = R_OFFSET(<->reginfo, mlockerp);
else {
lockerp->master_locker = mlockerp->master_locker;
mlockerp = R_ADDR(<->reginfo, mlockerp->master_locker);
}
if (is_family)
F_SET(mlockerp, DB_LOCKER_FAMILY_LOCKER);
SH_LIST_INSERT_HEAD(
&mlockerp->child_locker, lockerp, child_link, __db_locker);
err: UNLOCK_LOCKERS(env, region);
return (ret);
}
static int
__lock_freelocker_int(lt, region, sh_locker, reallyfree)
DB_LOCKTAB *lt;
DB_LOCKREGION *region;
DB_LOCKER *sh_locker;
int reallyfree;
{
ENV *env;
u_int32_t indx;
int ret;
env = lt->env;
if (SH_LIST_FIRST(&sh_locker->heldby, __db_lock) != NULL) {
__db_errx(env, DB_STR("2047",
"Freeing locker with locks"));
return (EINVAL);
}
if (sh_locker->master_locker != INVALID_ROFF) {
SH_LIST_REMOVE(sh_locker, child_link, __db_locker);
sh_locker->master_locker = INVALID_ROFF;
}
if (reallyfree) {
LOCKER_HASH(lt, region, sh_locker->id, indx);
SH_TAILQ_REMOVE(<->locker_tab[indx], sh_locker,
links, __db_locker);
if (sh_locker->mtx_locker != MUTEX_INVALID &&
(ret = __mutex_free(env, &sh_locker->mtx_locker)) != 0)
return (ret);
SH_TAILQ_INSERT_HEAD(®ion->free_lockers, sh_locker,
links, __db_locker);
SH_TAILQ_REMOVE(®ion->lockers, sh_locker,
ulinks, __db_locker);
region->nlockers--;
STAT_PERFMON2(env,
lock, nlockers, region->nlockers, sh_locker->id);
}
return (0);
}
int
__lock_freelocker(lt, sh_locker)
DB_LOCKTAB *lt;
DB_LOCKER *sh_locker;
{
DB_LOCKREGION *region;
ENV *env;
int ret;
region = lt->reginfo.primary;
env = lt->env;
if (sh_locker == NULL)
return (0);
LOCK_LOCKERS(env, region);
ret = __lock_freelocker_int(lt, region, sh_locker, 1);
UNLOCK_LOCKERS(env, region);
return (ret);
}
int
__lock_familyremove(lt, sh_locker)
DB_LOCKTAB *lt;
DB_LOCKER *sh_locker;
{
DB_LOCKREGION *region;
ENV *env;
int ret;
region = lt->reginfo.primary;
env = lt->env;
LOCK_LOCKERS(env, region);
ret = __lock_freelocker_int(lt, region, sh_locker, 0);
UNLOCK_LOCKERS(env, region);
return (ret);
}