diff options
Diffstat (limited to 'db2/txn/txn.c')
-rw-r--r-- | db2/txn/txn.c | 349 |
1 files changed, 175 insertions, 174 deletions
diff --git a/db2/txn/txn.c b/db2/txn/txn.c index 2a2e3da..4f3ffd8 100644 --- a/db2/txn/txn.c +++ b/db2/txn/txn.c @@ -1,7 +1,7 @@ /*- * See the file LICENSE for redistribution information. * - * Copyright (c) 1996, 1997 + * Copyright (c) 1996, 1997, 1998 * Sleepycat Software. All rights reserved. */ /* @@ -43,27 +43,20 @@ #include "config.h" #ifndef lint -static const char sccsid[] = "@(#)txn.c 10.39 (Sleepycat) 1/8/98"; +static const char sccsid[] = "@(#)txn.c 10.58 (Sleepycat) 5/31/98"; #endif /* not lint */ #ifndef NO_SYSTEM_INCLUDES #include <sys/types.h> -#include <sys/mman.h> -#include <sys/stat.h> #include <errno.h> -#include <fcntl.h> -#include <stddef.h> -#include <stdio.h> -#include <stdlib.h> #include <string.h> #include <time.h> -#include <unistd.h> #endif -#include "shqueue.h" #include "db_int.h" +#include "shqueue.h" #include "db_page.h" #include "db_shash.h" #include "txn.h" @@ -74,9 +67,9 @@ static const char sccsid[] = "@(#)txn.c 10.39 (Sleepycat) 1/8/98"; #include "common_ext.h" static int __txn_check_running __P((const DB_TXN *)); -static int __txn_create __P((DB_ENV *, const char *, u_int)); static int __txn_end __P((DB_TXN *, int)); static int __txn_grow_region __P((DB_TXNMGR *)); +static int __txn_init __P((DB_TXNREGION *)); static int __txn_undo __P((DB_TXN *)); static int __txn_validate_region __P((DB_TXNMGR *)); @@ -85,30 +78,20 @@ static int __txn_validate_region __P((DB_TXNMGR *)); * It assumes that a lock manager and log manager that conform to the db_log(3) * and db_lock(3) interfaces exist. * - * Create and initialize a transaction region in shared memory. + * Initialize a transaction region in shared memory. * Return 0 on success, errno on failure. */ static int -__txn_create(dbenv, path, mode) - DB_ENV *dbenv; - const char *path; - u_int mode; -{ +__txn_init(txn_region) DB_TXNREGION *txn_region; +{ time_t now; - int fd, maxtxns, ret; - maxtxns = dbenv->tx_max != 0 ? dbenv->tx_max : 1000; (void)time(&now); - /* Region may have existed. If it didn't, the open will fail. */ - if ((ret = __db_rcreate(dbenv, DB_APP_NONE, path, DEFAULT_TXN_FILE, - mode, TXN_REGION_SIZE(maxtxns), 0, &fd, &txn_region)) != 0) - return (ret); - + /* maxtxns is already initialized. */ txn_region->magic = DB_TXNMAGIC; txn_region->version = DB_TXNVERSION; - txn_region->maxtxns = maxtxns; txn_region->last_txnid = TXN_MINIMUM; /* XXX If we ever do more types of locking and logging, this changes. */ txn_region->logtype = 0; @@ -118,33 +101,22 @@ __txn_create(dbenv, path, mode) ZERO_LSN(txn_region->pending_ckp); SH_TAILQ_INIT(&txn_region->active_txn); __db_shalloc_init((void *)&txn_region[1], - TXN_REGION_SIZE(maxtxns) - sizeof(DB_TXNREGION)); - - /* Unlock the region. */ - (void)__db_mutex_unlock(&txn_region->hdr.lock, fd); + TXN_REGION_SIZE(txn_region->maxtxns) - sizeof(DB_TXNREGION)); - /* Now unmap and close the region. */ - if ((ret = __db_rclose(dbenv, fd, txn_region)) != 0) { - (void)txn_unlink(path, 1 /* force */, dbenv); - return (ret); - } return (0); } int txn_open(path, flags, mode, dbenv, mgrpp) const char *path; - int flags, mode; + u_int32_t flags; + int mode; DB_ENV *dbenv; DB_TXNMGR **mgrpp; { DB_TXNMGR *tmgrp; - DB_TXNREGION *txn_regionp; - int fd, ret, retry_cnt; - - tmgrp = NULL; - txn_regionp = NULL; - fd = -1; + u_int32_t maxtxns; + int ret; /* Validate arguments. */ if (dbenv == NULL) @@ -157,52 +129,57 @@ txn_open(path, flags, mode, dbenv, mgrpp) if ((ret = __db_fchk(dbenv, "txn_open", flags, OKFLAGS)) != 0) return (ret); - retry_cnt = 0; -retry: if (LF_ISSET(DB_CREATE) && (ret = __txn_create(dbenv, path, mode)) != 0) - if (ret == EAGAIN && ++retry_cnt < 0) { - (void)__db_sleep(1, 0); - goto retry; - } else /* We did not really create the region */ - flags &= ~DB_CREATE; - - retry_cnt = 0; -retry1: if ((ret = __db_ropen(dbenv, DB_APP_NONE, path, DEFAULT_TXN_FILE, - flags & ~(DB_CREATE | DB_THREAD | DB_TXN_NOSYNC), - &fd, &txn_regionp)) != 0) { - if (ret == EAGAIN && ++retry_cnt < 3) { - (void)__db_sleep(1, 0); - goto retry1; - } - goto out; - } - - - /* Check if valid region. */ - if (txn_regionp->magic != DB_TXNMAGIC) { - __db_err(dbenv, "txn_open: Bad magic number"); - ret = EINVAL; - goto out; - } + maxtxns = dbenv->tx_max != 0 ? dbenv->tx_max : 20; /* Now, create the transaction manager structure and set its fields. */ - if ((tmgrp = (DB_TXNMGR *)__db_malloc(sizeof(DB_TXNMGR))) == NULL) { + if ((tmgrp = (DB_TXNMGR *)__db_calloc(1, sizeof(DB_TXNMGR))) == NULL) { __db_err(dbenv, "txn_open: %s", strerror(ENOMEM)); - ret = ENOMEM; - goto out; + return (ENOMEM); } + /* Initialize the transaction manager structure. */ + tmgrp->mutexp = NULL; tmgrp->dbenv = dbenv; tmgrp->recover = dbenv->tx_recover == NULL ? __db_dispatch : dbenv->tx_recover; - tmgrp->region = txn_regionp; - tmgrp->reg_size = txn_regionp->hdr.size; - tmgrp->fd = fd; tmgrp->flags = LF_ISSET(DB_TXN_NOSYNC | DB_THREAD); - tmgrp->mem = &txn_regionp[1]; - tmgrp->mutexp = NULL; TAILQ_INIT(&tmgrp->txn_chain); + + /* Join/create the txn region. */ + tmgrp->reginfo.dbenv = dbenv; + tmgrp->reginfo.appname = DB_APP_NONE; + if (path == NULL) + tmgrp->reginfo.path = NULL; + else + if ((tmgrp->reginfo.path = (char *)__db_strdup(path)) == NULL) + goto err; + tmgrp->reginfo.file = DEFAULT_TXN_FILE; + tmgrp->reginfo.mode = mode; + tmgrp->reginfo.size = TXN_REGION_SIZE(maxtxns); + tmgrp->reginfo.dbflags = flags; + tmgrp->reginfo.addr = NULL; + tmgrp->reginfo.fd = -1; + tmgrp->reginfo.flags = dbenv->tx_max == 0 ? REGION_SIZEDEF : 0; + if ((ret = __db_rattach(&tmgrp->reginfo)) != 0) + goto err; + + /* Fill in region-related fields. */ + tmgrp->region = tmgrp->reginfo.addr; + tmgrp->mem = &tmgrp->region[1]; + + if (F_ISSET(&tmgrp->reginfo, REGION_CREATED)) { + tmgrp->region->maxtxns = maxtxns; + if ((ret = __txn_init(tmgrp->region)) != 0) + goto err; + + } else if (tmgrp->region->magic != DB_TXNMAGIC) { + /* Check if valid region. */ + __db_err(dbenv, "txn_open: Bad magic number"); + ret = EINVAL; + goto err; + } + if (LF_ISSET(DB_THREAD)) { - LOCK_TXNREGION(tmgrp); if ((ret = __db_shalloc(tmgrp->mem, sizeof(db_mutex_t), MUTEX_ALIGNMENT, &tmgrp->mutexp)) == 0) /* @@ -211,25 +188,27 @@ retry1: if ((ret = __db_ropen(dbenv, DB_APP_NONE, path, DEFAULT_TXN_FILE, * to be ignored. We put 0 here as a valid placeholder. */ __db_mutex_init(tmgrp->mutexp, 0); - UNLOCK_TXNREGION(tmgrp); if (ret != 0) - goto out; + goto err; } + + UNLOCK_TXNREGION(tmgrp); *mgrpp = tmgrp; return (0); -out: if (txn_regionp != NULL) - (void)__db_rclose(dbenv, fd, txn_regionp); - if (flags & DB_CREATE) - (void)txn_unlink(path, 1, dbenv); - if (tmgrp != NULL) { - if (tmgrp->mutexp != NULL) { - LOCK_TXNREGION(tmgrp); +err: if (tmgrp->reginfo.addr != NULL) { + if (tmgrp->mutexp != NULL) __db_shalloc_free(tmgrp->mem, tmgrp->mutexp); - UNLOCK_TXNREGION(tmgrp); - } - __db_free(tmgrp); + + UNLOCK_TXNREGION(tmgrp); + (void)__db_rdetach(&tmgrp->reginfo); + if (F_ISSET(&tmgrp->reginfo, REGION_CREATED)) + (void)txn_unlink(path, 1, dbenv); } + + if (tmgrp->reginfo.path != NULL) + FREES(tmgrp->reginfo.path); + FREE(tmgrp, sizeof(*tmgrp)); return (ret); } @@ -244,77 +223,83 @@ txn_begin(tmgrp, parent, txnpp) DB_TXN *parent; DB_TXN **txnpp; { - TXN_DETAIL *txnp; + DB_LSN begin_lsn; DB_TXN *retp; - int id, ret; + TXN_DETAIL *txnp; + size_t off; + u_int32_t id; + int ret; + + txnp = NULL; + *txnpp = NULL; + + if ((retp = (DB_TXN *)__db_malloc(sizeof(DB_TXN))) == NULL) { + __db_err(tmgrp->dbenv, "txn_begin : %s", strerror(ENOMEM)); + return (ENOMEM); + } + + /* + * We do not have to write begin records (and if we do not, then we + * need never write records for read-only transactions). However, + * we do need to find the current LSN so that we can store it in the + * transaction structure, so we can know where to take checkpoints. + */ + if (tmgrp->dbenv->lg_info != NULL && (ret = + log_put(tmgrp->dbenv->lg_info, &begin_lsn, NULL, DB_CURLSN)) != 0) + goto err2; LOCK_TXNREGION(tmgrp); + /* Make sure that last_txnid is not going to wrap around. */ + if (tmgrp->region->last_txnid == TXN_INVALID) { + __db_err(tmgrp->dbenv, "txn_begin: %s %s", + "Transaction ID wrapping.", + "Snapshot your database and start a new log."); + ret = EINVAL; + goto err1; + } + if ((ret = __txn_validate_region(tmgrp)) != 0) - goto err; + goto err1; /* Allocate a new transaction detail structure. */ if ((ret = __db_shalloc(tmgrp->mem, sizeof(TXN_DETAIL), 0, &txnp)) != 0 && ret == ENOMEM && (ret = __txn_grow_region(tmgrp)) == 0) ret = __db_shalloc(tmgrp->mem, sizeof(TXN_DETAIL), 0, &txnp); - if (ret != 0) - goto err; - - /* Make sure that last_txnid is not going to wrap around. */ - if (tmgrp->region->last_txnid == TXN_INVALID) - return (EINVAL); - - if ((retp = (DB_TXN *)__db_malloc(sizeof(DB_TXN))) == NULL) { - __db_err(tmgrp->dbenv, "txn_begin : %s", strerror(ENOMEM)); - ret = ENOMEM; goto err1; - } + + /* Place transaction on active transaction list. */ + SH_TAILQ_INSERT_HEAD(&tmgrp->region->active_txn, + txnp, links, __txn_detail); id = ++tmgrp->region->last_txnid; tmgrp->region->nbegins++; txnp->txnid = id; + txnp->begin_lsn = begin_lsn; ZERO_LSN(txnp->last_lsn); - ZERO_LSN(txnp->begin_lsn); txnp->last_lock = 0; txnp->status = TXN_RUNNING; - SH_TAILQ_INSERT_HEAD(&tmgrp->region->active_txn, - txnp, links, __txn_detail); - + off = (u_int8_t *)txnp - (u_int8_t *)tmgrp->region; UNLOCK_TXNREGION(tmgrp); ZERO_LSN(retp->last_lsn); retp->txnid = id; retp->parent = parent; - retp->off = (u_int8_t *)txnp - (u_int8_t *)tmgrp->region; retp->mgrp = tmgrp; - - if (tmgrp->dbenv->lg_info != NULL && - (ret = __txn_regop_log(tmgrp->dbenv->lg_info, - retp, &txnp->begin_lsn, 0, TXN_BEGIN)) != 0) { - - /* Deallocate transaction. */ - LOCK_TXNREGION(tmgrp); - SH_TAILQ_REMOVE(&tmgrp->region->active_txn, - txnp, links, __txn_detail); - __db_shalloc_free(tmgrp->mem, txnp); - UNLOCK_TXNREGION(tmgrp); - __db_free(retp); - return (ret); - } + retp->off = off; LOCK_TXNTHREAD(tmgrp); TAILQ_INSERT_TAIL(&tmgrp->txn_chain, retp, links); UNLOCK_TXNTHREAD(tmgrp); - *txnpp = retp; + *txnpp = retp; return (0); -err1: - __db_shalloc_free(tmgrp->mem, txnp); -err: - UNLOCK_TXNREGION(tmgrp); +err1: UNLOCK_TXNREGION(tmgrp); + +err2: __db_free(retp); return (ret); } @@ -332,12 +317,15 @@ txn_commit(txnp) if ((ret = __txn_check_running(txnp)) != 0) return (ret); - /* Sync the log. */ + /* + * If there are any log records, write a log record and sync + * the log, else do no log writes. + */ if ((logp = txnp->mgrp->dbenv->lg_info) != NULL && - (ret = __txn_regop_log(logp, - txnp, &txnp->last_lsn, - F_ISSET(txnp->mgrp, DB_TXN_NOSYNC) ? 0 : DB_FLUSH, TXN_COMMIT)) - != 0) + !IS_ZERO_LSN(txnp->last_lsn) && + (ret = __txn_regop_log(logp, txnp, &txnp->last_lsn, + F_ISSET(txnp->mgrp, DB_TXN_NOSYNC) ? 0 : DB_FLUSH, + TXN_COMMIT)) != 0) return (ret); return (__txn_end(txnp, 1)); @@ -371,8 +359,8 @@ int txn_prepare(txnp) DB_TXN *txnp; { - int ret; TXN_DETAIL *tp; + int ret; if ((ret = __txn_check_running(txnp)) != 0) return (ret); @@ -414,21 +402,23 @@ txn_close(tmgrp) DB_TXN *txnp; int ret, t_ret; + ret = 0; + /* * This function had better only be called once per process * (i.e., not per thread), so there should be no synchronization * required. */ - for (ret = 0, txnp = TAILQ_FIRST(&tmgrp->txn_chain); - txnp != TAILQ_END(&tmgrp->txn_chain); - txnp = TAILQ_FIRST(&tmgrp->txn_chain)) { - if ((t_ret = txn_abort(txnp)) != 0 && ret == 0) - ret = t_ret; - } + while ((txnp = + TAILQ_FIRST(&tmgrp->txn_chain)) != TAILQ_END(&tmgrp->txn_chain)) + if ((t_ret = txn_abort(txnp)) != 0) { + __txn_end(txnp, 0); + if (ret == 0) + ret = t_ret; + } - if (tmgrp->dbenv->lg_info && (t_ret = - log_flush(tmgrp->dbenv->lg_info, NULL)) != 0 && - ret == 0) + if (tmgrp->dbenv->lg_info && + (t_ret = log_flush(tmgrp->dbenv->lg_info, NULL)) != 0 && ret == 0) ret = t_ret; if (tmgrp->mutexp != NULL) { @@ -437,12 +427,12 @@ txn_close(tmgrp) UNLOCK_TXNREGION(tmgrp); } - if ((t_ret = __db_rclose(tmgrp->dbenv, tmgrp->fd, tmgrp->region)) != 0 - && ret == 0) + if ((t_ret = __db_rdetach(&tmgrp->reginfo)) != 0 && ret == 0) ret = t_ret; - if (ret == 0) - __db_free(tmgrp); + if (tmgrp->reginfo.path != NULL) + FREES(tmgrp->reginfo.path); + FREE(tmgrp, sizeof(*tmgrp)); return (ret); } @@ -457,8 +447,19 @@ txn_unlink(path, force, dbenv) int force; DB_ENV *dbenv; { - return (__db_runlink(dbenv, - DB_APP_NONE, path, DEFAULT_TXN_FILE, force)); + REGINFO reginfo; + int ret; + + memset(®info, 0, sizeof(reginfo)); + reginfo.dbenv = dbenv; + reginfo.appname = DB_APP_NONE; + if (path != NULL && (reginfo.path = (char *)__db_strdup(path)) == NULL) + return (ENOMEM); + reginfo.file = DEFAULT_TXN_FILE; + ret = __db_runlink(®info, force); + if (reginfo.path != NULL) + FREES(reginfo.path); + return (ret); } /* Internal routines. */ @@ -540,10 +541,10 @@ static int __txn_undo(txnp) DB_TXN *txnp; { - DB_TXNMGR *mgr; - DB_LOG *logp; DBT rdbt; + DB_LOG *logp; DB_LSN key_lsn; + DB_TXNMGR *mgr; int ret; mgr = txnp->mgrp; @@ -594,7 +595,7 @@ __txn_undo(txnp) int txn_checkpoint(mgr, kbytes, minutes) const DB_TXNMGR *mgr; - int kbytes, minutes; + u_int32_t kbytes, minutes; { TXN_DETAIL *txnp; DB_LSN ckp_lsn, last_ckp; @@ -603,10 +604,6 @@ txn_checkpoint(mgr, kbytes, minutes) time_t last_ckp_time, now; int ret; - /* Check usage. */ - if (kbytes < 0 || minutes < 0) - return (EINVAL); - /* * Check if we need to run recovery. */ @@ -678,8 +675,8 @@ do_ckp: if (mgr->dbenv->mp_info != NULL && (ret = memp_sync(mgr->dbenv->mp_info, &ckp_lsn)) != 0) { /* - * ret < 0 means that there are still buffers to flush; - * the checkpoint is not complete. Back off and try again. + * ret == DB_INCOMPLETE means that there are still buffers to + * flush, the checkpoint is not complete. Wait and try again. */ if (ret > 0) __db_err(mgr->dbenv, @@ -711,9 +708,9 @@ do_ckp: } /* - * This is called at every interface to verify if the region - * has changed size, and if so, to remap the region in and - * reset the process pointers. + * __txn_validate_region -- + * Called at every interface to verify if the region has changed size, + * and if so, to remap the region in and reset the process' pointers. */ static int __txn_validate_region(tp) @@ -721,15 +718,15 @@ __txn_validate_region(tp) { int ret; - if (tp->reg_size == tp->region->hdr.size) + if (tp->reginfo.size == tp->region->hdr.size) return (0); - /* Grow the region. */ - if ((ret = __db_rremap(tp->dbenv, tp->region, - tp->reg_size, tp->region->hdr.size, tp->fd, &tp->region)) != 0) + /* Detach/reattach the region. */ + if ((ret = __db_rreattach(&tp->reginfo, tp->region->hdr.size)) != 0) return (ret); - tp->reg_size = tp->region->hdr.size; + /* Reset region information. */ + tp->region = tp->reginfo.addr; tp->mem = &tp->region[1]; return (0); @@ -739,27 +736,26 @@ static int __txn_grow_region(tp) DB_TXNMGR *tp; { - size_t incr; + size_t incr, oldsize; u_int32_t mutex_offset, oldmax; u_int8_t *curaddr; int ret; oldmax = tp->region->maxtxns; incr = oldmax * sizeof(DB_TXN); - mutex_offset = (u_int8_t *)tp->mutexp - (u_int8_t *)tp->region; + mutex_offset = tp->mutexp != NULL ? + (u_int8_t *)tp->mutexp - (u_int8_t *)tp->region : 0; - if ((ret = __db_rgrow(tp->dbenv, tp->fd, incr)) != 0) - return (ret); - - if ((ret = __db_rremap(tp->dbenv, tp->region, - tp->reg_size, tp->reg_size + incr, tp->fd, &tp->region)) != 0) + oldsize = tp->reginfo.size; + if ((ret = __db_rgrow(&tp->reginfo, oldsize + incr)) != 0) return (ret); + tp->region = tp->reginfo.addr; /* Throw the new space on the free list. */ - curaddr = (u_int8_t *)tp->region + tp->reg_size; + curaddr = (u_int8_t *)tp->region + oldsize; tp->mem = &tp->region[1]; - tp->reg_size += incr; - tp->mutexp = (db_mutex_t *)((u_int8_t *)tp->region + mutex_offset); + tp->mutexp = mutex_offset != 0 ? + (db_mutex_t *)((u_int8_t *)tp->region + mutex_offset) : NULL; *((size_t *)curaddr) = incr - sizeof(size_t); curaddr += sizeof(size_t); @@ -826,6 +822,11 @@ txn_stat(mgr, statp, db_malloc) break; } + stats->st_region_wait = mgr->region->hdr.lock.mutex_set_wait; + stats->st_region_nowait = mgr->region->hdr.lock.mutex_set_nowait; + stats->st_refcnt = mgr->region->hdr.refcnt; + stats->st_regsize = mgr->region->hdr.size; + UNLOCK_TXNREGION(mgr); *statp = stats; return (0); |