⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 txn.c

📁 这是国外的resip协议栈
💻 C
📖 第 1 页 / 共 3 页
字号:
				 * All records are undone in reverse order.				 */				STAILQ_CONCAT(&txnp->logs, &txnp->parent->logs);				txnp->parent->logs = txnp->logs;				STAILQ_INIT(&txnp->logs);			}			F_SET(txnp->parent, TXN_CHILDCOMMIT);		}	}	/*	 * Process any aborted pages from our children.  We delay putting pages	 * on the free list that are newly allocated and then aborted so we can	 * undo other allocations, if necessary, without worrying about these	 * pages which were not on the free list before.	 */	if (txnp->txn_list != NULL) {#ifndef HAVE_FTRUNCATE		t_ret = __db_do_the_limbo(dbenv,		      NULL, txnp, txnp->txn_list, LIMBO_NORMAL);		if (t_ret != 0 && ret == 0)			ret = t_ret;#endif		__db_txnlist_end(dbenv, txnp->txn_list);		txnp->txn_list = NULL;	}	if (ret != 0)		goto err;	/* This is OK because __txn_end can only fail with a panic. */	return (__txn_end(txnp, 1));err:	/*	 * If we are prepared, then we "must" be able to commit.  We panic here	 * because even though the coordinator might be able to retry it is not	 * clear it would know to do that.  Otherwise  we'll try to abort.  If	 * that is successful, then we return whatever was in ret (that is, the	 * reason we failed).  If the abort was unsuccessful, abort probably	 * returned DB_RUNRECOVERY and we need to propagate that up.	 */	if (td->status == TXN_PREPARED)		return (__db_panic(dbenv, ret));	if ((t_ret = __txn_abort(txnp)) != 0)		ret = t_ret;	return (ret);}/* * __txn_abort_pp -- *	Interface routine to TXN->abort. */static int__txn_abort_pp(txnp)	DB_TXN *txnp;{	DB_ENV *dbenv;	int not_child, ret;	dbenv = txnp->mgrp->dbenv;	not_child = txnp->parent == NULL;	ret = __txn_abort(txnp);	if (not_child && IS_ENV_REPLICATED(dbenv))		__op_rep_exit(dbenv);	return (ret);}/* * __txn_abort -- *	Abort a transaction. * * PUBLIC: int __txn_abort __P((DB_TXN *)); */int__txn_abort(txnp)	DB_TXN *txnp;{	DB_ENV *dbenv;	DB_LOCKREQ request;	DB_TXN *kid;	TXN_DETAIL *td;	u_int32_t lflags;	int ret;	dbenv = txnp->mgrp->dbenv;	PANIC_CHECK(dbenv);	/* Ensure that abort always fails fatally. */	if ((ret = __txn_isvalid(txnp, &td, TXN_OP_ABORT)) != 0)		return (__db_panic(dbenv, ret));	/*	 * Try to abort any unresolved children.	 *	 * Abort either succeeds or panics the region.  As soon as we	 * see any failure, we just get out of here and return the panic	 * up.	 */	while ((kid = TAILQ_FIRST(&txnp->kids)) != NULL)		if ((ret = __txn_abort(kid)) != 0)			return (ret);	if (LOCKING_ON(dbenv)) {		/*		 * We are about to free all the read locks for this transaction		 * below.  Some of those locks might be handle locks which		 * should not be freed, because they will be freed when the		 * handle is closed.  Check the events and preprocess any		 * trades now so that we don't release the locks below.		 */		if ((ret = __txn_doevents(dbenv, txnp, TXN_ABORT, 1)) != 0)			return (__db_panic(dbenv, ret));		/* Turn off timeouts. */		if ((ret = __lock_set_timeout(dbenv,		    txnp->txnid, 0, DB_SET_TXN_TIMEOUT)) != 0)			return (__db_panic(dbenv, ret));		if ((ret = __lock_set_timeout(dbenv,		    txnp->txnid, 0, DB_SET_LOCK_TIMEOUT)) != 0)			return (__db_panic(dbenv, ret));		request.op = DB_LOCK_UPGRADE_WRITE;		request.obj = NULL;		if ((ret = __lock_vec(		    dbenv, txnp->txnid, DB_LOCK_ABORT, &request, 1, NULL)) != 0)			return (__db_panic(dbenv, ret));	}	if ((ret = __txn_undo(txnp)) != 0)		return (__db_panic(dbenv, ret));	/*	 * Normally, we do not need to log aborts.  However, if we	 * are a distributed transaction (i.e., we have a prepare),	 * then we log the abort so we know that this transaction	 * was actually completed.	 */	SET_LOG_FLAGS(dbenv, txnp, lflags);	if (DBENV_LOGGING(dbenv) && td->status == TXN_PREPARED &&	    (ret = __txn_regop_log(dbenv, txnp, &txnp->last_lsn,	    lflags, TXN_ABORT, (int32_t)time(NULL), NULL)) != 0)		return (__db_panic(dbenv, ret));	/* __txn_end always panics if it errors, so pass the return along. */	return (__txn_end(txnp, 0));}/* * __txn_discard_pp -- *	Interface routine to TXN->discard. */static int__txn_discard_pp(txnp, flags)	DB_TXN *txnp;	u_int32_t flags;{	DB_ENV *dbenv;	int not_child, ret;	dbenv = txnp->mgrp->dbenv;	not_child = txnp->parent == NULL;	ret = __txn_discard(txnp, flags);	if (not_child && IS_ENV_REPLICATED(dbenv))		__op_rep_exit(dbenv);	return (ret);}/* * __txn_discard -- *	Free the per-process resources associated with this txn handle. * * PUBLIC: int __txn_discard __P((DB_TXN *, u_int32_t flags)); */int__txn_discard(txnp, flags)	DB_TXN *txnp;	u_int32_t flags;{	DB_ENV *dbenv;	DB_TXN *freep;	TXN_DETAIL *td;	int ret;	COMPQUIET(flags, 0);	dbenv = txnp->mgrp->dbenv;	freep = NULL;	PANIC_CHECK(dbenv);	if ((ret = __txn_isvalid(txnp, &td, TXN_OP_DISCARD)) != 0)		return (ret);	/* Should be no children. */	DB_ASSERT(TAILQ_FIRST(&txnp->kids) == NULL);	/* Free the space. */	MUTEX_THREAD_LOCK(dbenv, txnp->mgrp->mutexp);	txnp->mgrp->n_discards++;	if (F_ISSET(txnp, TXN_MALLOC)) {		TAILQ_REMOVE(&txnp->mgrp->txn_chain, txnp, links);		freep = txnp;	}	MUTEX_THREAD_UNLOCK(dbenv, txnp->mgrp->mutexp);	if (freep != NULL)		__os_free(dbenv, freep);	return (0);}/* * __txn_prepare -- *	Flush the log so a future commit is guaranteed to succeed. * * PUBLIC: int __txn_prepare __P((DB_TXN *, u_int8_t *)); */int__txn_prepare(txnp, gid)	DB_TXN *txnp;	u_int8_t *gid;{	DBT list_dbt, xid;	DB_ENV *dbenv;	DB_LOCKREQ request;	DB_TXN *kid;	TXN_DETAIL *td;	u_int32_t lflags;	int ret;	dbenv = txnp->mgrp->dbenv;	PANIC_CHECK(dbenv);	if ((ret = __txn_isvalid(txnp, &td, TXN_OP_PREPARE)) != 0)		return (ret);	/* Commit any unresolved children. */	while ((kid = TAILQ_FIRST(&txnp->kids)) != NULL)		if ((ret = __txn_commit(kid, DB_TXN_NOSYNC)) != 0)			return (ret);#ifndef HAVE_FTRUNCATE	if (txnp->txn_list != NULL  &&	    (ret = __db_do_the_limbo(dbenv,	    NULL, txnp, txnp->txn_list, LIMBO_PREPARE)) != 0)		return (ret);#endif	/*	 * In XA, the global transaction ID in the txn_detail structure is	 * already set; in a non-XA environment, we must set it here.  XA	 * requires that the transaction be either ENDED or SUSPENDED when	 * prepare is called, so we know that if the xa_status isn't in one	 * of those states, then we are calling prepare directly and we need	 * to fill in the td->xid.	 */	if ((ret = __txn_doevents(dbenv, txnp, TXN_PREPARE, 1)) != 0)		return (ret);	memset(&request, 0, sizeof(request));	if (LOCKING_ON(dbenv)) {		request.op = DB_LOCK_PUT_READ;		if (IS_REP_MASTER(dbenv) &&		    IS_ZERO_LSN(txnp->last_lsn)) {			memset(&list_dbt, 0, sizeof(list_dbt));			request.obj = &list_dbt;		}		if ((ret = __lock_vec(dbenv,		    txnp->txnid, 0, &request, 1, NULL)) != 0)			return (ret);	}	if (DBENV_LOGGING(dbenv)) {		memset(&xid, 0, sizeof(xid));		if (td->xa_status != TXN_XA_ENDED &&		    td->xa_status != TXN_XA_SUSPENDED)			/* Regular prepare; fill in the gid. */			memcpy(td->xid, gid, sizeof(td->xid));		xid.size = sizeof(td->xid);		xid.data = td->xid;		lflags = DB_LOG_COMMIT | DB_LOG_PERM | DB_FLUSH;		if ((ret = __txn_xa_regop_log(dbenv, txnp, &txnp->last_lsn,		    lflags, TXN_PREPARE, &xid, td->format, td->gtrid, td->bqual,		    &td->begin_lsn, request.obj)) != 0) {			__db_err(dbenv, "DB_TXN->prepare: log_write failed %s",			    db_strerror(ret));		}		if (request.obj != NULL && request.obj->data != NULL)			__os_free(dbenv, request.obj->data);		if (ret != 0)			return (ret);	}	MUTEX_THREAD_LOCK(dbenv, txnp->mgrp->mutexp);	td->status = TXN_PREPARED;	MUTEX_THREAD_UNLOCK(dbenv, txnp->mgrp->mutexp);	return (0);}/* * __txn_id -- *	Return the transaction ID. * * PUBLIC: u_int32_t __txn_id __P((DB_TXN *)); */u_int32_t__txn_id(txnp)	DB_TXN *txnp;{	return (txnp->txnid);}/* * __txn_set_timeout -- *	DB_ENV->set_txn_timeout. * * PUBLIC: int  __txn_set_timeout __P((DB_TXN *, db_timeout_t, u_int32_t)); */int__txn_set_timeout(txnp, timeout, op)	DB_TXN *txnp;	db_timeout_t timeout;	u_int32_t op;{	if (op != DB_SET_TXN_TIMEOUT &&  op != DB_SET_LOCK_TIMEOUT)		return (__db_ferr(txnp->mgrp->dbenv, "DB_TXN->set_timeout", 0));	return (__lock_set_timeout(	    txnp->mgrp->dbenv, txnp->txnid, timeout, op));}/* * __txn_isvalid -- *	Return 0 if the txnp is reasonable, otherwise panic. */static int__txn_isvalid(txnp, tdp, op)	const DB_TXN *txnp;	TXN_DETAIL **tdp;	txnop_t op;{	DB_ENV *dbenv;	DB_TXNMGR *mgrp;	DB_TXNREGION *region;	TXN_DETAIL *tp;	mgrp = txnp->mgrp;	dbenv = mgrp->dbenv;	region = mgrp->reginfo.primary;	/* Check for recovery. */	if (!F_ISSET(txnp, TXN_COMPENSATE) &&	    F_ISSET(region, TXN_IN_RECOVERY)) {		__db_err(dbenv, "operation not permitted during recovery");		goto err;	}	/* Check for live cursors. */	if (txnp->cursors != 0) {		__db_err(dbenv, "transaction has active cursors");		goto err;	}	/* Check transaction's state. */	tp = R_ADDR(&mgrp->reginfo, txnp->off);	if (tdp != NULL)		*tdp = tp;	/* Handle any operation specific checks. */	switch (op) {	case TXN_OP_DISCARD:		/*		 * Since we're just tossing the per-process space; there are		 * a lot of problems with the transaction that we can tolerate.		 */		/* Transaction is already been reused. */		if (txnp->txnid != tp->txnid)			return (0);		/*		 * What we've got had better be either a prepared or		 * restored transaction.		 */		if (tp->status != TXN_PREPARED &&		    !F_ISSET(tp, TXN_DTL_RESTORED)) {			__db_err(dbenv, "not a restored transaction");			return (__db_panic(dbenv, EINVAL));		}		return (0);	case TXN_OP_PREPARE:		if (txnp->parent != NULL) {			/*			 * This is not fatal, because you could imagine an			 * application that simply prepares everybody because			 * it doesn't distinguish between children and parents.			 * I'm not arguing this is good, but I could imagine			 * someone doing it.			 */			__db_err(dbenv,			    "Prepare disallowed on child transactions");			return (EINVAL);		}		break;	case TXN_OP_ABORT:	case TXN_OP_COMMIT:	default:		break;	}	switch (tp->status) {	case TXN_PREPARED:		if (op == TXN_OP_PREPARE) {			__db_err(dbenv, "transaction already prepared");			/*			 * Txn_prepare doesn't blow away the user handle, so			 * in this case, give the user the opportunity to			 * abort or commit.			 */			return (EINVAL);		}		break;	case TXN_RUNNING:		break;	case TXN_ABORTED:	case TXN_COMMITTED:	default:		__db_err(dbenv, "transaction already %s",		    tp->status == TXN_COMMITTED ? "committed" : "aborted");		goto err;	}	return (0);err:	/*	 * If there's a serious problem with the transaction, panic.  TXN	 * handles are dead by definition when we return, and if you use	 * a cursor you forgot to close, we have no idea what will happen.	 */	return (__db_panic(dbenv, EINVAL));}/* * __txn_end -- *	Internal transaction end routine. */static int__txn_end(txnp, is_commit)	DB_TXN *txnp;	int is_commit;{	DB_ENV *dbenv;	DB_LOCKREQ request;	DB_TXNLOGREC *lr;	DB_TXNMGR *mgr;	DB_TXNREGION *region;	TXN_DETAIL *tp;	int do_closefiles, ret;	mgr = txnp->mgrp;	dbenv = mgr->dbenv;	region = mgr->reginfo.primary;	do_closefiles = 0;	/* Process commit events. */	if ((ret = __txn_doevents(dbenv,	    txnp, is_commit ? TXN_COMMIT : TXN_ABORT, 0)) != 0)		return (__db_panic(dbenv, ret));	/*	 * Release the locks.	 *	 * __txn_end cannot return an simple error, we MUST return	 * success/failure from commit or abort, ignoring any internal	 * errors.  So, we panic if something goes wrong.  We can't	 * deadlock here because we're not acquiring any new locks,	 * so DB_LOCK_DEADLOCK is just as fatal as any other error.	 */	if (LOCKING_ON(dbenv)) {		request.op = txnp->parent == NULL ||		    is_commit == 0 ? DB_LOCK_PUT_ALL : DB_LOCK_INHERIT;		request.obj = NULL;		if ((ret = __lock_vec(dbenv,		    txnp->txnid, 0, &request, 1, NULL)) != 0)			return (__db_panic(dbenv, ret));	}	/* End the transaction. */	R_LOCK(dbenv, &mgr->reginfo);	tp = R_ADDR(&mgr->reginfo, txnp->off);	SH_TAILQ_REMOVE(&region->active_txn, tp, links, __txn_detail);	if (F_ISSET(tp, TXN_DTL_RESTORED)) {		region->stat.st_nrestores--;		do_closefiles = region->stat.st_nrestores == 0;	}	__db_shalloc_free(&mgr->reginfo, tp);	if (is_commit)		region->stat.st_ncommits++;	else		region->stat.st_naborts++;	--region->stat.st_nactive;	R_UNLOCK(dbenv, &mgr->reginfo);	/*	 * The transaction cannot get more locks, remove its locker info,	 * if any.	 */	if (LOCKING_ON(dbenv) && (ret =	    __lock_freefamilylocker(dbenv->lk_handle, txnp->txnid)) != 0)		return (__db_panic(dbenv, ret));	if (txnp->parent != NULL)		TAILQ_REMOVE(&txnp->parent->kids, txnp, klinks);	/* Free the space. */	while ((lr = STAILQ_FIRST(&txnp->logs)) != NULL) {		STAILQ_REMOVE(&txnp->logs, lr, __txn_logrec, links);		__os_free(dbenv, lr);	}	if (F_ISSET(txnp, TXN_MALLOC)) {		MUTEX_THREAD_LOCK(dbenv, mgr->mutexp);		TAILQ_REMOVE(&mgr->txn_chain, txnp, links);		MUTEX_THREAD_UNLOCK(dbenv, mgr->mutexp);		__os_free(dbenv, txnp);	}	if (do_closefiles) {		F_SET((DB_LOG *)dbenv->lg_handle, DBLOG_RECOVER);		(void)__dbreg_close_files(dbenv);		F_CLR((DB_LOG *)dbenv->lg_handle, DBLOG_RECOVER);		mgr->n_discards = 0;		(void)__txn_checkpoint(dbenv, 0, 0, DB_FORCE);	}	return (0);}static int__txn_dispatch_undo(dbenv, txnp, rdbt, key_lsn, txnlist)	DB_ENV *dbenv;	DB_TXN *txnp;	DBT *rdbt;	DB_LSN *key_lsn;	void *txnlist;{	int ret;	ret = __db_dispatch(dbenv, dbenv->recover_dtab,	    dbenv->recover_dtab_size, rdbt, key_lsn, DB_TXN_ABORT, txnlist);	if (F_ISSET(txnp, TXN_CHILDCOMMIT))		(void)__db_txnlist_lsnadd(dbenv,		    txnlist, key_lsn, 0);	if (ret == DB_SURPRISE_KID) {

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -