#include "db_config.h"
#include "db_int.h"
#include "dbinc/db_page.h"
#include "dbinc/lock.h"
#include "dbinc/mp.h"
#include "dbinc/txn.h"
#include "dbinc/db_am.h"
typedef struct __txn_event TXN_EVENT;
struct __txn_event {
TXN_EVENT_T op;
TAILQ_ENTRY(__txn_event) links;
union {
struct {
DB *dbp;
} c;
struct {
char *name;
u_int8_t *fileid;
int inmem;
} r;
struct {
DB_LOCK lock;
DB_LOCKER *locker;
DB *dbp;
} t;
} u;
};
#define TXN_TOP_PARENT(txn) do { \
while (txn->parent != NULL) \
txn = txn->parent; \
} while (0)
static void __clear_fe_watermark __P((DB_TXN *, DB *));
int
__txn_closeevent(env, txn, dbp)
ENV *env;
DB_TXN *txn;
DB *dbp;
{
int ret;
TXN_EVENT *e;
e = NULL;
if ((ret = __os_calloc(env, 1, sizeof(TXN_EVENT), &e)) != 0)
return (ret);
e->u.c.dbp = dbp;
e->op = TXN_CLOSE;
TXN_TOP_PARENT(txn);
TAILQ_INSERT_TAIL(&txn->events, e, links);
return (0);
}
int
__txn_remevent(env, txn, name, fileid, inmem)
ENV *env;
DB_TXN *txn;
const char *name;
u_int8_t *fileid;
int inmem;
{
int ret;
TXN_EVENT *e;
e = NULL;
if ((ret = __os_calloc(env, 1, sizeof(TXN_EVENT), &e)) != 0)
return (ret);
if ((ret = __os_strdup(env, name, &e->u.r.name)) != 0)
goto err;
if (fileid != NULL) {
if ((ret = __os_calloc(env,
1, DB_FILE_ID_LEN, &e->u.r.fileid)) != 0) {
__os_free(env, e->u.r.name);
goto err;
}
memcpy(e->u.r.fileid, fileid, DB_FILE_ID_LEN);
}
e->u.r.inmem = inmem;
e->op = TXN_REMOVE;
TAILQ_INSERT_TAIL(&txn->events, e, links);
return (0);
err: __os_free(env, e);
return (ret);
}
void
__txn_remrem(env, txn, name)
ENV *env;
DB_TXN *txn;
const char *name;
{
TXN_EVENT *e, *next_e;
for (e = TAILQ_FIRST(&txn->events); e != NULL; e = next_e) {
next_e = TAILQ_NEXT(e, links);
if (e->op != TXN_REMOVE || strcmp(name, e->u.r.name) != 0)
continue;
TAILQ_REMOVE(&txn->events, e, links);
__os_free(env, e->u.r.name);
if (e->u.r.fileid != NULL)
__os_free(env, e->u.r.fileid);
__os_free(env, e);
}
return;
}
int
__txn_lockevent(env, txn, dbp, lock, locker)
ENV *env;
DB_TXN *txn;
DB *dbp;
DB_LOCK *lock;
DB_LOCKER *locker;
{
int ret;
TXN_EVENT *e;
if (!LOCKING_ON(env))
return (0);
e = NULL;
if ((ret = __os_calloc(env, 1, sizeof(TXN_EVENT), &e)) != 0)
return (ret);
e->u.t.locker = locker;
e->u.t.lock = *lock;
e->u.t.dbp = dbp;
if (F2_ISSET(dbp, DB2_AM_EXCL))
e->op = TXN_XTRADE;
else
e->op = TXN_TRADE;
TAILQ_INSERT_TAIL(&txn->events, e, links);
dbp->cur_txn = txn;
return (0);
}
void
__txn_remlock(env, txn, lock, locker)
ENV *env;
DB_TXN *txn;
DB_LOCK *lock;
DB_LOCKER *locker;
{
TXN_EVENT *e, *next_e;
for (e = TAILQ_FIRST(&txn->events); e != NULL; e = next_e) {
next_e = TAILQ_NEXT(e, links);
if ((e->op != TXN_TRADE && e->op != TXN_TRADED &&
e->op != TXN_XTRADE) ||
(e->u.t.lock.off != lock->off && e->u.t.locker != locker))
continue;
TAILQ_REMOVE(&txn->events, e, links);
__os_free(env, e);
}
return;
}
#define DO_TRADE do { \
memset(&req, 0, sizeof(req)); \
req.lock = e->u.t.lock; \
req.op = DB_LOCK_TRADE; \
t_ret = __lock_vec(env, txn->parent ? \
txn->parent->locker : e->u.t.locker, 0, &req, 1, NULL); \
if (t_ret == 0) { \
if (txn->parent != NULL) { \
e->u.t.dbp->cur_txn = txn->parent; \
e->u.t.dbp->cur_locker = txn->parent->locker; \
} else { \
e->op = TXN_TRADED; \
e->u.t.dbp->cur_locker = e->u.t.locker; \
F_SET(e->u.t.dbp->cur_locker, \
DB_LOCKER_HANDLE_LOCKER); \
if (opcode != TXN_PREPARE) \
e->u.t.dbp->cur_txn = NULL; \
} \
} else if (t_ret == DB_NOTFOUND) \
t_ret = 0; \
if (t_ret != 0 && ret == 0) \
ret = t_ret; \
} while (0)
int
__txn_doevents(env, txn, opcode, preprocess)
ENV *env;
DB_TXN *txn;
int opcode, preprocess;
{
DB_LOCKREQ req;
TXN_EVENT *e, *enext;
int ret, t_ret;
ret = 0;
if (preprocess) {
for (e = TAILQ_FIRST(&txn->events);
e != NULL; e = enext) {
enext = TAILQ_NEXT(e, links);
if (!(opcode == TXN_COMMIT && e->op == TXN_XTRADE) &&
(e->op != TXN_TRADE ||
IS_WRITELOCK(e->u.t.lock.mode)))
continue;
DO_TRADE;
if (txn->parent != NULL) {
TAILQ_REMOVE(&txn->events, e, links);
TAILQ_INSERT_HEAD(
&txn->parent->events, e, links);
}
}
return (ret);
}
DB_ASSERT(env, opcode != TXN_PREPARE);
while ((e = TAILQ_FIRST(&txn->events)) != NULL) {
TAILQ_REMOVE(&txn->events, e, links);
if (opcode == TXN_ABORT && (e->op != TXN_CLOSE &&
e->op != TXN_XTRADE))
goto dofree;
switch (e->op) {
case TXN_CLOSE:
if ((t_ret = __db_close(e->u.c.dbp,
NULL, DB_NOSYNC)) != 0 && ret == 0)
ret = t_ret;
break;
case TXN_REMOVE:
if (txn->parent != NULL)
TAILQ_INSERT_TAIL(
&txn->parent->events, e, links);
else if (e->u.r.fileid != NULL) {
if ((t_ret = __memp_nameop(env,
e->u.r.fileid, NULL, e->u.r.name,
NULL, e->u.r.inmem)) != 0 && ret == 0)
ret = t_ret;
} else if ((t_ret =
__os_unlink(env, e->u.r.name, 0)) != 0 && ret == 0)
ret = t_ret;
break;
case TXN_TRADE:
case TXN_XTRADE:
DO_TRADE;
if (txn->parent != NULL) {
TAILQ_INSERT_HEAD(
&txn->parent->events, e, links);
continue;
}
case TXN_TRADED:
if (ret == 0 && !F2_ISSET(e->u.t.dbp, DB2_AM_EXCL)) {
if ((t_ret = __lock_downgrade(env,
&e->u.t.lock, DB_LOCK_READ, 0)) != 0 &&
ret == 0)
ret = t_ret;
if (ret == 0 && e->u.t.lock.off ==
e->u.t.dbp->handle_lock.off &&
e->u.t.lock.ndx ==
e->u.t.dbp->handle_lock.ndx)
e->u.t.dbp->handle_lock.mode =
DB_LOCK_READ;
}
break;
default:
DB_ASSERT(env, 0);
}
dofree:
switch (e->op) {
case TXN_REMOVE:
if (txn->parent != NULL)
continue;
if (e->u.r.fileid != NULL)
__os_free(env, e->u.r.fileid);
__os_free(env, e->u.r.name);
break;
case TXN_TRADE:
case TXN_XTRADE:
if (opcode == TXN_ABORT)
e->u.t.dbp->cur_txn = NULL;
break;
case TXN_CLOSE:
case TXN_TRADED:
default:
break;
}
__os_free(env, e);
}
return (ret);
}
int
__txn_record_fname(env, txn, fname)
ENV *env;
DB_TXN *txn;
FNAME *fname;
{
DB_LOG *dblp;
DB_TXNMGR *mgr;
TXN_DETAIL *td;
roff_t fname_off;
roff_t *np, *ldbs;
u_int32_t i;
int ret;
if ((td = txn->td) == NULL)
return (0);
mgr = env->tx_handle;
dblp = env->lg_handle;
fname_off = R_OFFSET(&dblp->reginfo, fname);
ldbs = R_ADDR(&mgr->reginfo, td->log_dbs);
for (i = 0, np = ldbs; i < td->nlog_dbs; i++, np++)
if (*np == fname_off)
return (0);
if (td->nlog_slots <= td->nlog_dbs) {
TXN_SYSTEM_LOCK(env);
if ((ret = __env_alloc(&mgr->reginfo,
sizeof(roff_t) * (td->nlog_slots << 1), &np)) != 0) {
TXN_SYSTEM_UNLOCK(env);
return (ret);
}
memcpy(np, ldbs, td->nlog_dbs * sizeof(roff_t));
if (td->nlog_slots > TXN_NSLOTS)
__env_alloc_free(&mgr->reginfo, ldbs);
TXN_SYSTEM_UNLOCK(env);
td->log_dbs = R_OFFSET(&mgr->reginfo, np);
ldbs = np;
td->nlog_slots = td->nlog_slots << 1;
}
ldbs[td->nlog_dbs] = fname_off;
td->nlog_dbs++;
fname->txn_ref++;
return (0);
}
int
__txn_dref_fname(env, txn)
ENV *env;
DB_TXN *txn;
{
DB_LOG *dblp;
DB_TXNMGR *mgr;
FNAME *fname;
roff_t *np;
TXN_DETAIL *ptd, *td;
u_int32_t i;
int ret;
td = txn->td;
if (td->nlog_dbs == 0)
return (0);
mgr = env->tx_handle;
dblp = env->lg_handle;
ret = 0;
ptd = txn->parent != NULL ? txn->parent->td : NULL;
np = R_ADDR(&mgr->reginfo, td->log_dbs);
if (ptd != NULL) {
for (i = 0; i < td->nlog_dbs; i++, np++) {
fname = R_ADDR(&dblp->reginfo, *np);
MUTEX_LOCK(env, fname->mutex);
ret = __txn_record_fname(env, txn->parent, fname);
fname->txn_ref--;
MUTEX_UNLOCK(env, fname->mutex);
if (ret != 0)
break;
}
} else {
np += td->nlog_dbs - 1;
for (i = 0; i < td->nlog_dbs; i++, np--) {
fname = R_ADDR(&dblp->reginfo, *np);
MUTEX_LOCK(env, fname->mutex);
if (fname->txn_ref == 1) {
MUTEX_UNLOCK(env, fname->mutex);
DB_ASSERT(env, fname->txn_ref != 0);
ret = __dbreg_close_id_int(
env, fname, DBREG_CLOSE, 0);
} else {
fname->txn_ref--;
MUTEX_UNLOCK(env, fname->mutex);
}
if (ret != 0 && ret != EIO)
break;
}
}
return (ret);
}
static void
__clear_fe_watermark(txn, db)
DB_TXN *txn;
DB *db;
{
MPOOLFILE *mpf;
mpf = db->mpf->mfp;
mpf->fe_watermark = PGNO_INVALID;
mpf->fe_txnid = 0U;
mpf->fe_nlws = 0U;
TAILQ_REMOVE(&txn->femfs, db, felink);
}
void
__txn_reset_fe_watermarks(txn)
DB_TXN *txn;
{
DB *db;
if (txn->parent) {
DB_ASSERT(txn->mgrp->env, TAILQ_FIRST(&txn->femfs) == NULL);
}
while ((db = TAILQ_FIRST(&txn->femfs)))
__clear_fe_watermark(txn, db);
}
void
__txn_remove_fe_watermark(txn, db)
DB_TXN *txn;
DB *db;
{
DB *db_tmp;
if (txn == NULL || !F_ISSET(txn, TXN_BULK))
return;
TAILQ_FOREACH(db_tmp, &txn->femfs, felink) {
if (db_tmp == db) {
__clear_fe_watermark(txn, db);
break;
}
}
}
void
__txn_add_fe_watermark(txn, db, pgno)
DB_TXN *txn;
DB *db;
db_pgno_t pgno;
{
MPOOLFILE *mfp;
if (txn == NULL || !F_ISSET(txn, TXN_BULK))
return;
mfp = db->mpf->mfp;
if (mfp->fe_watermark != PGNO_INVALID) {
#ifdef DIAGNOSTIC
DB_ASSERT(txn->mgrp->env, mfp->fe_txnid == txn->txnid);
#endif
return;
}
mfp->fe_watermark = pgno;
mfp->fe_txnid = txn->txnid;
TAILQ_INSERT_TAIL(&txn->femfs, db, felink);
}
int
__txn_flush_fe_files(txn)
DB_TXN *txn;
{
DB *db;
ENV *env;
int ret;
env = txn->mgrp->env;
DB_ASSERT(env, txn->mgrp != NULL);
DB_ASSERT(env, env != NULL);
#ifdef DIAGNOSTIC
DB_ASSERT(env, txn->parent == NULL);
#endif
TAILQ_FOREACH(db, &txn->femfs, felink) {
if (db->mpf->mfp->fe_nlws > 0 &&
(ret = __memp_sync_int(env, db->mpf, 0,
DB_SYNC_FILE, NULL, NULL)))
return (ret);
}
return (0);
}
int
__txn_pg_above_fe_watermark(txn, mpf, pgno)
DB_TXN *txn;
MPOOLFILE *mpf;
db_pgno_t pgno;
{
ENV *env;
int skip;
if (txn == NULL || (!F_ISSET(txn, TXN_BULK)) ||
mpf->fe_watermark == PGNO_INVALID)
return (0);
env = txn->mgrp->env;
skip = 0;
TXN_SYSTEM_LOCK(env);
if (((DB_TXNREGION *)env->tx_handle->reginfo.primary)->n_hotbackup > 0)
skip = 1;
TXN_SYSTEM_UNLOCK(env);
if (skip)
return (0);
DB_ASSERT(txn->mgrp->env, mpf->fe_txnid == txn->txnid);
return (mpf->fe_watermark <= pgno);
}