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

📄 txn.c

📁 这是国外的resip协议栈
💻 C
📖 第 1 页 / 共 3 页
字号:
		if ((ret = __db_txnlist_lsninit(		    dbenv, txnlist, key_lsn)) == 0)			F_SET(txnp, TXN_CHILDCOMMIT);	}	return (ret);}/* * __txn_undo -- *	Undo the transaction with id txnid. */static int__txn_undo(txnp)	DB_TXN *txnp;{	DBT rdbt;	DB_ENV *dbenv;	DB_LOGC *logc;	DB_LSN key_lsn;	DB_TXN *ptxn;	DB_TXNLOGREC *lr;	DB_TXNMGR *mgr;	int ret, t_ret;	void *txnlist;	mgr = txnp->mgrp;	dbenv = mgr->dbenv;	logc = NULL;	txnlist = NULL;	ret = 0;	if (!DBENV_LOGGING(dbenv))		return (0);	/*	 * This is the simplest way to code this, but if the mallocs during	 * recovery turn out to be a performance issue, we can do the	 * allocation here and use DB_DBT_USERMEM.	 */	memset(&rdbt, 0, sizeof(rdbt));	/*	 * Allocate a txnlist for children and aborted page allocs.	 * We need to associate the list with the maximal parent	 * so that aborted pages are recovered when that transaction	 * is committed or aborted.	 */	for (ptxn = txnp->parent; ptxn != NULL && ptxn->parent != NULL;)		ptxn = ptxn->parent;	if (ptxn != NULL && ptxn->txn_list != NULL)		txnlist = ptxn->txn_list;	else if (txnp->txn_list != NULL)		txnlist = txnp->txn_list;	else if ((ret = __db_txnlist_init(dbenv, 0, 0, NULL, &txnlist)) != 0)		return (ret);	else if (ptxn != NULL)		ptxn->txn_list = txnlist;	if (F_ISSET(txnp, TXN_CHILDCOMMIT) &&	    (ret = __db_txnlist_lsninit(dbenv, txnlist, &txnp->last_lsn)) != 0)		return (ret);	/*	 * Take log records from the linked list stored in the transaction,	 * then from the log.	 */	for (lr = STAILQ_FIRST(&txnp->logs);	    lr != NULL; lr = STAILQ_NEXT(lr, links)) {		rdbt.data = lr->data;		rdbt.size = 0;		LSN_NOT_LOGGED(key_lsn);		ret =		    __txn_dispatch_undo(dbenv, txnp, &rdbt, &key_lsn, txnlist);		if (ret != 0) {			__db_err(dbenv,			    "DB_TXN->abort: In-memory log undo failed: %s",			    db_strerror(ret));			goto err;		}	}	key_lsn = txnp->last_lsn;	if (!IS_ZERO_LSN(key_lsn) &&	     (ret = __log_cursor(dbenv, &logc)) != 0)		goto err;	while (!IS_ZERO_LSN(key_lsn)) {		/*		 * The dispatch routine returns the lsn of the record		 * before the current one in the key_lsn argument.		 */		if ((ret = __log_c_get(logc, &key_lsn, &rdbt, DB_SET)) == 0) {			ret = __txn_dispatch_undo(dbenv,			    txnp, &rdbt, &key_lsn, txnlist);		}		if (ret != 0) {			__db_err(dbenv,		    "DB_TXN->abort: Log undo failed for LSN: %lu %lu: %s",			    (u_long)key_lsn.file, (u_long)key_lsn.offset,			    db_strerror(ret));			goto err;		}	}#ifndef HAVE_FTRUNCATE	ret = __db_do_the_limbo(dbenv, ptxn, txnp, txnlist, LIMBO_NORMAL);#endiferr:	if (logc != NULL && (t_ret = __log_c_close(logc)) != 0 && ret == 0)		ret = t_ret;	if (ptxn == NULL && txnlist != NULL)		__db_txnlist_end(dbenv, txnlist);	return (ret);}/* * __txn_checkpoint_pp -- *	DB_ENV->txn_checkpoint pre/post processing. * * PUBLIC: int __txn_checkpoint_pp * PUBLIC:     __P((DB_ENV *, u_int32_t, u_int32_t, u_int32_t)); */int__txn_checkpoint_pp(dbenv, kbytes, minutes, flags)	DB_ENV *dbenv;	u_int32_t kbytes, minutes, flags;{	int rep_check, ret;	PANIC_CHECK(dbenv);	ENV_REQUIRES_CONFIG(dbenv,	    dbenv->tx_handle, "txn_checkpoint", DB_INIT_TXN);	/*	 * On a replication client, all transactions are read-only; therefore,	 * a checkpoint is a null-op.	 *	 * We permit txn_checkpoint, instead of just rendering it illegal,	 * so that an application can just let a checkpoint thread continue	 * to operate as it gets promoted or demoted between being a	 * master and a client.	 */	if (IS_REP_CLIENT(dbenv))		return (0);	rep_check = IS_ENV_REPLICATED(dbenv) ? 1 : 0;	if (rep_check)		__env_rep_enter(dbenv);	ret = __txn_checkpoint(dbenv, kbytes, minutes, flags);	if (rep_check)		__env_db_rep_exit(dbenv);	return (ret);}/* * __txn_checkpoint -- *	DB_ENV->txn_checkpoint. * * PUBLIC: int __txn_checkpoint * PUBLIC:	__P((DB_ENV *, u_int32_t, u_int32_t, u_int32_t)); */int__txn_checkpoint(dbenv, kbytes, minutes, flags)	DB_ENV *dbenv;	u_int32_t kbytes, minutes, flags;{	DB_LSN ckp_lsn, last_ckp;	DB_TXNMGR *mgr;	DB_TXNREGION *region;	REGENV *renv;	REGINFO *infop;	time_t last_ckp_time, now;	u_int32_t bytes, gen, id, logflags, mbytes;	int ret;	ret = gen = 0;	/*	 * A client will only call through here during recovery,	 * so just sync the Mpool and go home.	 */	if (IS_REP_CLIENT(dbenv)) {		if (MPOOL_ON(dbenv) && (ret = __memp_sync(dbenv, NULL)) != 0) {			__db_err(dbenv,		    "txn_checkpoint: failed to flush the buffer cache %s",			    db_strerror(ret));			return (ret);		} else			return (0);	}	mgr = dbenv->tx_handle;	region = mgr->reginfo.primary;	infop = dbenv->reginfo;	renv = infop->primary;	/*	 * No mutex is needed as envid is read-only once it is set.	 */	id = renv->envid;	/*	 * The checkpoint LSN is an LSN such that all transactions begun before	 * it are complete.  Our first guess (corrected below based on the list	 * of active transactions) is the last-written LSN.	 */	__log_txn_lsn(dbenv, &ckp_lsn, &mbytes, &bytes);	if (!LF_ISSET(DB_FORCE)) {		/* Don't checkpoint a quiescent database. */		if (bytes == 0 && mbytes == 0)			return (0);		/*		 * If either kbytes or minutes is non-zero, then only take the		 * checkpoint if more than "minutes" minutes have passed or if		 * more than "kbytes" of log data have been written since the		 * last checkpoint.		 */		if (kbytes != 0 &&		    mbytes * 1024 + bytes / 1024 >= (u_int32_t)kbytes)			goto do_ckp;		if (minutes != 0) {			(void)time(&now);			R_LOCK(dbenv, &mgr->reginfo);			last_ckp_time = region->time_ckp;			R_UNLOCK(dbenv, &mgr->reginfo);			if (now - last_ckp_time >= (time_t)(minutes * 60))				goto do_ckp;		}		/*		 * If we checked time and data and didn't go to checkpoint,		 * we're done.		 */		if (minutes != 0 || kbytes != 0)			return (0);	}do_ckp:	__txn_getactive(dbenv, &ckp_lsn);	if (MPOOL_ON(dbenv) && (ret = __memp_sync(dbenv, NULL)) != 0) {		__db_err(dbenv,		    "txn_checkpoint: failed to flush the buffer cache %s",		    db_strerror(ret));		return (ret);	}	/*	 * Because we can't be a replication client here, and because	 * recovery (somewhat unusually) calls txn_checkpoint and expects	 * it to write a log message, LOGGING_ON is the correct macro here.	 */	if (LOGGING_ON(dbenv)) {		R_LOCK(dbenv, &mgr->reginfo);		last_ckp = region->last_ckp;		R_UNLOCK(dbenv, &mgr->reginfo);		if (REP_ON(dbenv))			__rep_get_gen(dbenv, &gen);		/*		 * Put out records for the open files before we log		 * the checkpoint.  The records are certain to be at		 * or after ckp_lsn, but before the checkpoint record		 * itself, so they're sure to be included if we start		 * recovery from the ckp_lsn contained in this		 * checkpoint.		 */		logflags = DB_LOG_PERM | DB_LOG_CHKPNT;		if (!IS_RECOVERING(dbenv))			logflags |= DB_FLUSH;		if ((ret = __dbreg_log_files(dbenv)) != 0 ||		    (ret = __txn_ckp_log(dbenv, NULL, &ckp_lsn, logflags,		    &ckp_lsn, &last_ckp, (int32_t)time(NULL), id, gen)) != 0) {			__db_err(dbenv,			    "txn_checkpoint: log failed at LSN [%ld %ld] %s",			    (long)ckp_lsn.file, (long)ckp_lsn.offset,			    db_strerror(ret));			return (ret);		}		__txn_updateckp(dbenv, &ckp_lsn);	}	return (ret);}/* * __txn_getactive -- *	 Find the oldest active transaction and figure out its "begin" LSN. *	 This is the lowest LSN we can checkpoint, since any record written *	 after it may be involved in a transaction and may therefore need *	 to be undone in the case of an abort. * *	 We check both the file and offset for 0 since the lsn may be in *	 transition.  If it is then we don't care about this txn becuase it *	 must be starting after we set the initial value of lsnp in the caller. *	 All txns must initalize their begin_lsn before writing to the log. * * PUBLIC: void __txn_getactive __P((DB_ENV *, DB_LSN *)); */void__txn_getactive(dbenv, lsnp)	DB_ENV *dbenv;	DB_LSN *lsnp;{	DB_TXNMGR *mgr;	DB_TXNREGION *region;	TXN_DETAIL *txnp;	mgr = dbenv->tx_handle;	region = mgr->reginfo.primary;	R_LOCK(dbenv, &mgr->reginfo);	for (txnp = SH_TAILQ_FIRST(&region->active_txn, __txn_detail);	    txnp != NULL;	    txnp = SH_TAILQ_NEXT(txnp, links, __txn_detail))		if (txnp->begin_lsn.file != 0 &&		    txnp->begin_lsn.offset != 0 &&		    log_compare(&txnp->begin_lsn, lsnp) < 0)			*lsnp = txnp->begin_lsn;	R_UNLOCK(dbenv, &mgr->reginfo);}/* * __txn_getckp -- *	Get the LSN of the last transaction checkpoint. * * PUBLIC: int __txn_getckp __P((DB_ENV *, DB_LSN *)); */int__txn_getckp(dbenv, lsnp)	DB_ENV *dbenv;	DB_LSN *lsnp;{	DB_LSN lsn;	DB_TXNMGR *mgr;	DB_TXNREGION *region;	mgr = dbenv->tx_handle;	region = mgr->reginfo.primary;	R_LOCK(dbenv, &mgr->reginfo);	lsn = region->last_ckp;	R_UNLOCK(dbenv, &mgr->reginfo);	if (IS_ZERO_LSN(lsn))		return (DB_NOTFOUND);	*lsnp = lsn;	return (0);}/* * __txn_activekids -- *	Return if this transaction has any active children. * * PUBLIC: int __txn_activekids __P((DB_ENV *, u_int32_t, DB_TXN *)); */int__txn_activekids(dbenv, rectype, txnp)	DB_ENV *dbenv;	u_int32_t rectype;	DB_TXN *txnp;{	/*	 * On a child commit, we know that there are children (i.e., the	 * committing child at the least.  In that case, skip this check.	 */	if (F_ISSET(txnp, TXN_COMPENSATE) || rectype == DB___txn_child)		return (0);	if (TAILQ_FIRST(&txnp->kids) != NULL) {		__db_err(dbenv, "Child transaction is active");		return (EPERM);	}	return (0);}/* * __txn_force_abort -- *	Force an abort record into the log if the commit record *	failed to get to disk. * * PUBLIC: int __txn_force_abort __P((DB_ENV *, u_int8_t *)); */int__txn_force_abort(dbenv, buffer)	DB_ENV *dbenv;	u_int8_t *buffer;{	DB_CIPHER *db_cipher;	HDR *hdr;	u_int32_t hdrlen, offset, opcode, sum_len;	u_int8_t *bp, *key, chksum[DB_MAC_KEY];	size_t hdrsize, rec_len;	int ret;	db_cipher = dbenv->crypto_handle;	/*	 * This routine depends on the layout of HDR and the __txn_regop	 * __txn_xa_regop records in txn.src.  We are passed the beginning	 * of the commit record in the log buffer and overwrite the	 * commit with an abort and recalculate the checksum.	 */	hdrsize = CRYPTO_ON(dbenv) ? HDR_CRYPTO_SZ : HDR_NORMAL_SZ;	hdr = (HDR *)buffer;	memcpy(&hdrlen, buffer + SSZ(HDR, len), sizeof(hdr->len));	rec_len = hdrlen - hdrsize;	offset = sizeof(u_int32_t) + sizeof(u_int32_t) + sizeof(DB_LSN);	if (CRYPTO_ON(dbenv)) {		key = db_cipher->mac_key;		sum_len = DB_MAC_KEY;		if ((ret = db_cipher->decrypt(dbenv, db_cipher->data,		    &hdr->iv[0], buffer + hdrsize, rec_len)) != 0)			return (__db_panic(dbenv, ret));	} else {		key = NULL;		sum_len = sizeof(u_int32_t);	}	bp = buffer + hdrsize + offset;	opcode = TXN_ABORT;	memcpy(bp, &opcode, sizeof(opcode));	if (CRYPTO_ON(dbenv) &&	    (ret = db_cipher->encrypt(dbenv,	    db_cipher->data, &hdr->iv[0], buffer + hdrsize, rec_len)) != 0)		return (__db_panic(dbenv, ret));	__db_chksum(buffer + hdrsize, rec_len, key, chksum);	memcpy(buffer + SSZA(HDR, chksum), chksum, sum_len);	return (0);}/* * __txn_preclose *	Before we can close an environment, we need to check if we * were in the midst of taking care of restored transactions.  If * so, then we need to close the files that we opened. * * PUBLIC: int __txn_preclose __P((DB_ENV *)); */int__txn_preclose(dbenv)	DB_ENV *dbenv;{	DB_TXNMGR *mgr;	DB_TXNREGION *region;	int do_closefiles, ret;	mgr = (DB_TXNMGR *)dbenv->tx_handle;	region = mgr->reginfo.primary;	do_closefiles = 0;	R_LOCK(dbenv, &mgr->reginfo);	if (region != NULL &&	    region->stat.st_nrestores <= mgr->n_discards &&	    mgr->n_discards != 0)		do_closefiles = 1;	R_UNLOCK(dbenv, &mgr->reginfo);	if (do_closefiles) {		/*		 * Set the DBLOG_RECOVER flag while closing these		 * files so they do not create additional log records		 * that will confuse future recoveries.		 */		F_SET((DB_LOG *)dbenv->lg_handle, DBLOG_RECOVER);		ret = __dbreg_close_files(dbenv);		F_CLR((DB_LOG *)dbenv->lg_handle, DBLOG_RECOVER);	} else		ret = 0;	return (ret);}/* * __txn_reset -- *	Reset the last txnid to its minimum value, and log the reset. * * PUBLIC: int __txn_reset __P((DB_ENV *)); */int__txn_reset(dbenv)	DB_ENV *dbenv;{	DB_LSN scrap;	DB_TXNREGION *region;	region = ((DB_TXNMGR *)dbenv->tx_handle)->reginfo.primary;	region->last_txnid = TXN_MINIMUM;	DB_ASSERT(LOGGING_ON(dbenv));	return (__txn_recycle_log(dbenv,	    NULL, &scrap, 0, TXN_MINIMUM, TXN_MAXIMUM));}/* * __txn_updateckp -- *	Update the last_ckp field in the transaction region.  This happens * at the end of a normal checkpoint and also when a replication client * receives a checkpoint record. * * PUBLIC: void __txn_updateckp __P((DB_ENV *, DB_LSN *)); */void__txn_updateckp(dbenv, lsnp)	DB_ENV *dbenv;	DB_LSN *lsnp;{	DB_TXNMGR *mgr;	DB_TXNREGION *region;	mgr = dbenv->tx_handle;	region = mgr->reginfo.primary;	/*	 * We want to make sure last_ckp only moves forward;  since we drop	 * locks above and in log_put, it's possible for two calls to	 * __txn_ckp_log to finish in a different order from how they were	 * called.	 */	R_LOCK(dbenv, &mgr->reginfo);	if (log_compare(&region->last_ckp, lsnp) < 0) {		region->last_ckp = *lsnp;		(void)time(&region->time_ckp);	}	R_UNLOCK(dbenv, &mgr->reginfo);}/* * txn_set_begin_lsnp -- *	Set the pointer to the begin_lsn field if that field is zero. */static void__txn_set_begin_lsnp(txn, rlsnp)	DB_TXN *txn;	DB_LSN **rlsnp;{	DB_LSN *lsnp;	TXN_DETAIL *td;	td = R_ADDR(&txn->mgrp->reginfo, txn->off);	while (td->parent != INVALID_ROFF)		td = R_ADDR(&txn->mgrp->reginfo, td->parent);	lsnp = &td->begin_lsn;	if (IS_ZERO_LSN(*lsnp))		*rlsnp = lsnp;}

⌨️ 快捷键说明

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