📄 txn.c
字号:
/*- * See the file LICENSE for redistribution information. * * Copyright (c) 1996-2002 * Sleepycat Software. All rights reserved. *//* * Copyright (c) 1995, 1996 * The President and Fellows of Harvard University. All rights reserved. * * This code is derived from software contributed to Berkeley by * Margo Seltzer. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */#include "db_config.h"#ifndef lintstatic const char revid[] = "$Id: txn.c,v 11.179 2002/08/29 17:41:17 margo Exp $";#endif /* not lint */#ifndef NO_SYSTEM_INCLUDES#include <sys/types.h>#include <stdlib.h>#if TIME_WITH_SYS_TIME#include <sys/time.h>#include <time.h>#else#if HAVE_SYS_TIME_H#include <sys/time.h>#else#include <time.h>#endif#endif#include <string.h>#endif#include "db_int.h"#include "dbinc/crypto.h"#include "dbinc/hmac.h"#include "dbinc/db_page.h"#include "dbinc/db_shash.h"#include "dbinc/hash.h"#include "dbinc/lock.h"#include "dbinc/log.h"#include "dbinc/txn.h"#define SET_LOG_FLAGS(dbenv, txnp, lflags) \ do { \ lflags = DB_COMMIT | DB_PERMANENT; \ if (F_ISSET(txnp, TXN_SYNC)) \ lflags |= DB_FLUSH; \ else if (!F_ISSET(txnp, TXN_NOSYNC) && \ !F_ISSET(dbenv, DB_ENV_TXN_NOSYNC)) { \ if (F_ISSET(dbenv, DB_ENV_TXN_WRITE_NOSYNC)) \ lflags |= DB_WRNOSYNC; \ else \ lflags |= DB_FLUSH; \ } \ } while (0)/* * __txn_isvalid enumerated types. We cannot simply use the transaction * statuses, because different statuses need to be handled differently * depending on the caller. */typedef enum { TXN_OP_ABORT, TXN_OP_COMMIT, TXN_OP_DISCARD, TXN_OP_PREPARE} txnop_t;static int __txn_begin_int __P((DB_TXN *, int));static int __txn_end __P((DB_TXN *, int));static int __txn_isvalid __P((const DB_TXN *, TXN_DETAIL **, txnop_t));static int __txn_set_timeout __P(( DB_TXN *, db_timeout_t, u_int32_t));static int __txn_undo __P((DB_TXN *));#ifndef db_create/* * txn_abort -- * txn_begin -- * txn_commit -- * * When we switched to methods in 4.0, we guessed txn_{abort,begin,commit} * were the interfaces applications would likely use and not be willing to * change, due to the sheer volume of the calls. Provide wrappers -- we * could do txn_abort and txn_commit using macros, but not txn_begin, as * the name of the field is txn_begin, we didn't want to modify it. * * The issue with txn_begin hits us in another way. If configured with the * --with-uniquename option, we use #defines to re-define DB's interfaces * to unique names. We can't do that for these functions because txn_begin * is also a field name in the DB_ENV structure, and the #defines we use go * at the end of the db.h file -- we get control too late to #define a field * name. So, modify the script that generates the unique names #defines to * not generate them for these three functions, and don't include the three * functions in libraries built with that configuration option. * * EXTERN: int txn_abort __P((DB_TXN *)); * EXTERN: int txn_begin __P((DB_ENV *, DB_TXN *, DB_TXN **, u_int32_t)); * EXTERN: int txn_commit __P((DB_TXN *, u_int32_t)); */inttxn_abort(txnp) DB_TXN *txnp;{ return (txnp->abort(txnp));}inttxn_begin(dbenv, parent, txnpp, flags) DB_ENV *dbenv; DB_TXN *parent, **txnpp; u_int32_t flags;{ return (dbenv->txn_begin(dbenv, parent, txnpp, flags));}inttxn_commit(txnp, flags) DB_TXN *txnp; u_int32_t flags;{ return (txnp->commit(txnp, flags));}#endif /* !db_create *//* * __txn_begin -- * This is a wrapper to the actual begin process. Normal transaction * begin allocates a DB_TXN structure for the caller, while XA transaction * begin does not. Other than that, both call into common __txn_begin_int * code. * * Internally, we use TXN_DETAIL structures, but the DB_TXN structure * provides access to the transaction ID and the offset in the transaction * region of the TXN_DETAIL structure. * * PUBLIC: int __txn_begin __P((DB_ENV *, DB_TXN *, DB_TXN **, u_int32_t)); */int__txn_begin(dbenv, parent, txnpp, flags) DB_ENV *dbenv; DB_TXN *parent, **txnpp; u_int32_t flags;{ DB_LOCKREGION *region; DB_TXN *txn; int ret; *txnpp = NULL; PANIC_CHECK(dbenv); ENV_REQUIRES_CONFIG(dbenv, dbenv->tx_handle, "txn_begin", DB_INIT_TXN); if ((ret = __db_fchk(dbenv, "txn_begin", flags, DB_DIRTY_READ | DB_TXN_NOWAIT | DB_TXN_NOSYNC | DB_TXN_SYNC)) != 0) return (ret); if ((ret = __db_fcchk(dbenv, "txn_begin", flags, DB_TXN_NOSYNC, DB_TXN_SYNC)) != 0) return (ret); if ((ret = __os_calloc(dbenv, 1, sizeof(DB_TXN), &txn)) != 0) return (ret); txn->mgrp = dbenv->tx_handle; txn->parent = parent; TAILQ_INIT(&txn->kids); TAILQ_INIT(&txn->events); txn->flags = TXN_MALLOC; if (LF_ISSET(DB_DIRTY_READ)) F_SET(txn, TXN_DIRTY_READ); if (LF_ISSET(DB_TXN_NOSYNC)) F_SET(txn, TXN_NOSYNC); if (LF_ISSET(DB_TXN_SYNC)) F_SET(txn, TXN_SYNC); if (LF_ISSET(DB_TXN_NOWAIT)) F_SET(txn, TXN_NOWAIT); if ((ret = __txn_begin_int(txn, 0)) != 0) goto err; if (parent != NULL) TAILQ_INSERT_HEAD(&parent->kids, txn, klinks); if (LOCKING_ON(dbenv)) { region = ((DB_LOCKTAB *)dbenv->lk_handle)->reginfo.primary; if (parent != NULL) { ret = __lock_inherit_timeout(dbenv, parent->txnid, txn->txnid); /* No parent locker set yet. */ if (ret == EINVAL) { parent = NULL; ret = 0; } if (ret != 0) goto err; } /* * Parent is NULL if we have no parent * or it has no timeouts set. */ if (parent == NULL && region->tx_timeout != 0) if ((ret = __lock_set_timeout(dbenv, txn->txnid, region->tx_timeout, DB_SET_TXN_TIMEOUT)) != 0) goto err; } *txnpp = txn; return (0);err: __os_free(dbenv, txn); return (ret);}/* * __txn_xa_begin -- * XA version of txn_begin. * * PUBLIC: int __txn_xa_begin __P((DB_ENV *, DB_TXN *)); */int__txn_xa_begin(dbenv, txn) DB_ENV *dbenv; DB_TXN *txn;{ PANIC_CHECK(dbenv); memset(txn, 0, sizeof(DB_TXN)); txn->mgrp = dbenv->tx_handle; TAILQ_INIT(&txn->kids); TAILQ_INIT(&txn->events); return (__txn_begin_int(txn, 0));}/* * __txn_compensate_begin * Begin an compensation transaction. This is a special interface * that is used only for transactions that must be started to compensate * for actions during an abort. Currently only used for allocations. * * PUBLIC: int __txn_compensate_begin __P((DB_ENV *, DB_TXN **txnp)); */int__txn_compensate_begin(dbenv, txnpp) DB_ENV *dbenv; DB_TXN **txnpp;{ DB_TXN *txn; int ret; PANIC_CHECK(dbenv); if ((ret = __os_calloc(dbenv, 1, sizeof(DB_TXN), &txn)) != 0) return (ret); txn->mgrp = dbenv->tx_handle; TAILQ_INIT(&txn->kids); TAILQ_INIT(&txn->events); txn->flags = TXN_MALLOC; F_SET(txn, TXN_COMPENSATE); *txnpp = txn; return (__txn_begin_int(txn, 1));}/* * __txn_begin_int -- * Normal DB version of txn_begin. */static int__txn_begin_int(txn, internal) DB_TXN *txn; int internal;{ DB_ENV *dbenv; DB_LSN begin_lsn, null_lsn; DB_TXNMGR *mgr; DB_TXNREGION *region; TXN_DETAIL *td; size_t off; u_int32_t id, *ids; int nids, ret; mgr = txn->mgrp; dbenv = mgr->dbenv; region = mgr->reginfo.primary; /* * 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. * * XXX * We should set this value when we write the first log record, not * here. */ if (DBENV_LOGGING(dbenv)) __log_txn_lsn(dbenv, &begin_lsn, NULL, NULL); R_LOCK(dbenv, &mgr->reginfo); if (!F_ISSET(txn, TXN_COMPENSATE) && F_ISSET(region, TXN_IN_RECOVERY)) { __db_err(dbenv, "operation not permitted during recovery"); ret = EINVAL; goto err; } /* Make sure that we aren't still recovering prepared transactions. */ if (!internal && region->stat.st_nrestores != 0) { __db_err(dbenv, "recovery of prepared but not yet committed transactions is incomplete"); ret = EINVAL; goto err; } /* * Allocate a new transaction id. Our current valid range can span * the maximum valid value, so check for it and wrap manually. */ if (region->last_txnid == TXN_MAXIMUM && region->cur_maxid != TXN_MAXIMUM) region->last_txnid = TXN_MINIMUM - 1; if (region->last_txnid == region->cur_maxid) { if ((ret = __os_malloc(dbenv, sizeof(u_int32_t) * region->maxtxns, &ids)) != 0) goto err; nids = 0; for (td = SH_TAILQ_FIRST(®ion->active_txn, __txn_detail); td != NULL; td = SH_TAILQ_NEXT(td, links, __txn_detail)) ids[nids++] = td->txnid; region->last_txnid = TXN_MINIMUM - 1; region->cur_maxid = TXN_MAXIMUM; if (nids != 0) __db_idspace(ids, nids, ®ion->last_txnid, ®ion->cur_maxid); __os_free(dbenv, ids); if (DBENV_LOGGING(dbenv) && (ret = __txn_recycle_log(dbenv, NULL, &null_lsn, 0, region->last_txnid, region->cur_maxid)) != 0) goto err; } /* Allocate a new transaction detail structure. */ if ((ret = __db_shalloc(mgr->reginfo.addr, sizeof(TXN_DETAIL), 0, &td)) != 0) { __db_err(dbenv, "Unable to allocate memory for transaction detail"); goto err; } /* Place transaction on active transaction list. */ SH_TAILQ_INSERT_HEAD(®ion->active_txn, td, links, __txn_detail); id = ++region->last_txnid; ++region->stat.st_nbegins; if (++region->stat.st_nactive > region->stat.st_maxnactive) region->stat.st_maxnactive = region->stat.st_nactive; td->txnid = id; td->begin_lsn = begin_lsn; ZERO_LSN(td->last_lsn); td->status = TXN_RUNNING; if (txn->parent != NULL) td->parent = txn->parent->off; else td->parent = INVALID_ROFF; td->flags = 0; off = R_OFFSET(&mgr->reginfo, td); R_UNLOCK(dbenv, &mgr->reginfo); ZERO_LSN(txn->last_lsn); txn->txnid = id; txn->off = (u_int32_t)off; txn->abort = __txn_abort; txn->commit = __txn_commit; txn->discard = __txn_discard; txn->id = __txn_id; txn->prepare = __txn_prepare; txn->set_timeout = __txn_set_timeout; /* * If this is a transaction family, we must link the child to the * maximal grandparent in the lock table for deadlock detection. */ if (txn->parent != NULL && LOCKING_ON(dbenv)) if ((ret = __lock_addfamilylocker(dbenv, txn->parent->txnid, txn->txnid)) != 0) return (ret); if (F_ISSET(txn, TXN_MALLOC)) { MUTEX_THREAD_LOCK(dbenv, mgr->mutexp); TAILQ_INSERT_TAIL(&mgr->txn_chain, txn, links); MUTEX_THREAD_UNLOCK(dbenv, mgr->mutexp); } return (0);err: R_UNLOCK(dbenv, &mgr->reginfo); return (ret);}/* * __txn_commit -- * Commit a transaction. * * PUBLIC: int __txn_commit __P((DB_TXN *, u_int32_t)); */int__txn_commit(txnp, flags) DB_TXN *txnp; u_int32_t flags;{ DB_ENV *dbenv; DB_LOCKREQ request; DB_TXN *kid; TXN_DETAIL *td; u_int32_t lflags; int ret, t_ret; dbenv = txnp->mgrp->dbenv; PANIC_CHECK(dbenv); if ((ret = __txn_isvalid(txnp, &td, TXN_OP_COMMIT)) != 0) return (ret); /* * We clear flags that are incorrect, ignoring any flag errors, and * default to synchronous operations. By definition, transaction * handles are dead when we return, and this error should never * happen, but we don't want to fail in the field 'cause the app is * specifying the wrong flag for some reason. */ if (__db_fchk(dbenv, "DB_TXN->commit", flags, DB_TXN_NOSYNC | DB_TXN_SYNC) != 0) flags = DB_TXN_SYNC; if (__db_fcchk(dbenv, "DB_TXN->commit", flags, DB_TXN_NOSYNC, DB_TXN_SYNC) != 0) flags = DB_TXN_SYNC; if (LF_ISSET(DB_TXN_NOSYNC)) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -