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

📄 lock_deadlock.c

📁 这是国外的resip协议栈
💻 C
📖 第 1 页 / 共 2 页
字号:
/*- * See the file LICENSE for redistribution information. * * Copyright (c) 1996-2004 *	Sleepycat Software.  All rights reserved. * * $Id: lock_deadlock.c,v 11.86 2004/10/15 16:59:42 bostic Exp $ */#include "db_config.h"#ifndef NO_SYSTEM_INCLUDES#include <sys/types.h>#include <string.h>#endif#include "db_int.h"#include "dbinc/db_shash.h"#include "dbinc/lock.h"#include "dbinc/log.h"#include "dbinc/txn.h"#define	ISSET_MAP(M, N)	((M)[(N) / 32] & (1 << ((N) % 32)))#define	CLEAR_MAP(M, N) {						\	u_int32_t __i;							\	for (__i = 0; __i < (N); __i++)					\		(M)[__i] = 0;						\}#define	SET_MAP(M, B)	((M)[(B) / 32] |= (1 << ((B) % 32)))#define	CLR_MAP(M, B)	((M)[(B) / 32] &= ~(1 << ((B) % 32)))#define	OR_MAP(D, S, N)	{						\	u_int32_t __i;							\	for (__i = 0; __i < (N); __i++)					\		D[__i] |= S[__i];					\}#define	BAD_KILLID	0xfffffffftypedef struct {	int		valid;	int		self_wait;	int		in_abort;	u_int32_t	count;	u_int32_t	id;	roff_t		last_lock;	roff_t		last_obj;	u_int32_t	last_locker_id;	db_pgno_t	pgno;} locker_info;static int __dd_abort __P((DB_ENV *, locker_info *));static int __dd_build __P((DB_ENV *,	    u_int32_t, u_int32_t **, u_int32_t *, u_int32_t *, locker_info **));static int __dd_find __P((DB_ENV *,	    u_int32_t *, locker_info *, u_int32_t, u_int32_t, u_int32_t ***));static int __dd_isolder __P((u_int32_t, u_int32_t, u_int32_t, u_int32_t));static int __dd_verify __P((locker_info *, u_int32_t *, u_int32_t *,	    u_int32_t *, u_int32_t, u_int32_t, u_int32_t));#ifdef DIAGNOSTICstatic void __dd_debug	    __P((DB_ENV *, locker_info *, u_int32_t *, u_int32_t, u_int32_t));#endif/* * __lock_detect_pp -- *	DB_ENV->lock_detect pre/post processing. * * PUBLIC: int __lock_detect_pp __P((DB_ENV *, u_int32_t, u_int32_t, int *)); */int__lock_detect_pp(dbenv, flags, atype, abortp)	DB_ENV *dbenv;	u_int32_t flags, atype;	int *abortp;{	int ret, rep_check;	PANIC_CHECK(dbenv);	ENV_REQUIRES_CONFIG(dbenv,	    dbenv->lk_handle, "DB_ENV->lock_detect", DB_INIT_LOCK);	/* Validate arguments. */	if ((ret = __db_fchk(dbenv, "DB_ENV->lock_detect", flags, 0)) != 0)		return (ret);	switch (atype) {	case DB_LOCK_DEFAULT:	case DB_LOCK_EXPIRE:	case DB_LOCK_MAXLOCKS:	case DB_LOCK_MAXWRITE:	case DB_LOCK_MINLOCKS:	case DB_LOCK_MINWRITE:	case DB_LOCK_OLDEST:	case DB_LOCK_RANDOM:	case DB_LOCK_YOUNGEST:		break;	default:		__db_err(dbenv,	    "DB_ENV->lock_detect: unknown deadlock detection mode specified");		return (EINVAL);	}	rep_check = IS_ENV_REPLICATED(dbenv) ? 1 : 0;	if (rep_check)		__env_rep_enter(dbenv);	ret = __lock_detect(dbenv, atype, abortp);	if (rep_check)		__env_db_rep_exit(dbenv);	return (ret);}/* * __lock_detect -- *	DB_ENV->lock_detect. * * PUBLIC: int __lock_detect __P((DB_ENV *, u_int32_t, int *)); */int__lock_detect(dbenv, atype, abortp)	DB_ENV *dbenv;	u_int32_t atype;	int *abortp;{	DB_LOCKREGION *region;	DB_LOCKTAB *lt;	DB_TXNMGR *tmgr;	db_timeval_t now;	locker_info *idmap;	u_int32_t *bitmap, *copymap, **deadp, **free_me, *tmpmap;	u_int32_t i, cid, keeper, killid, limit, nalloc, nlockers;	u_int32_t lock_max, txn_max;	int ret;	/*	 * If this environment is a replication client, then we must use the	 * MINWRITE detection discipline.	 */	if (__rep_is_client(dbenv))		atype = DB_LOCK_MINWRITE;	free_me = NULL;	lt = dbenv->lk_handle;	if (abortp != NULL)		*abortp = 0;	/* Check if a detector run is necessary. */	LOCKREGION(dbenv, lt);	/* Make a pass only if auto-detect would run. */	region = lt->reginfo.primary;	LOCK_SET_TIME_INVALID(&now);	if (region->need_dd == 0 &&	     (!LOCK_TIME_ISVALID(&region->next_timeout) ||	     !__lock_expired(dbenv, &now, &region->next_timeout))) {		UNLOCKREGION(dbenv, lt);		return (0);	}	if (region->need_dd == 0)		atype = DB_LOCK_EXPIRE;	/* Reset need_dd, so we know we've run the detector. */	region->need_dd = 0;	/* Build the waits-for bitmap. */	ret = __dd_build(dbenv, atype, &bitmap, &nlockers, &nalloc, &idmap);	lock_max = region->stat.st_cur_maxid;	UNLOCKREGION(dbenv, lt);	/*	 * We need the cur_maxid from the txn region as well.  In order	 * to avoid tricky synchronization between the lock and txn	 * regions, we simply unlock the lock region and then lock the	 * txn region.  This introduces a small window during which the	 * transaction system could then wrap.  We're willing to return	 * the wrong answer for "oldest" or "youngest" in those rare	 * circumstances.	 */	tmgr = dbenv->tx_handle;	if (tmgr != NULL) {		R_LOCK(dbenv, &tmgr->reginfo);		txn_max = ((DB_TXNREGION *)tmgr->reginfo.primary)->cur_maxid;		R_UNLOCK(dbenv, &tmgr->reginfo);	} else		txn_max = TXN_MAXIMUM;	if (ret != 0 || atype == DB_LOCK_EXPIRE)		return (ret);	if (nlockers == 0)		return (0);#ifdef DIAGNOSTIC	if (FLD_ISSET(dbenv->verbose, DB_VERB_WAITSFOR))		__dd_debug(dbenv, idmap, bitmap, nlockers, nalloc);#endif	/* Now duplicate the bitmaps so we can verify deadlock participants. */	if ((ret = __os_calloc(dbenv, (size_t)nlockers,	    sizeof(u_int32_t) * nalloc, &copymap)) != 0)		goto err;	memcpy(copymap, bitmap, nlockers * sizeof(u_int32_t) * nalloc);	if ((ret = __os_calloc(dbenv, sizeof(u_int32_t), nalloc, &tmpmap)) != 0)		goto err1;	/* Find a deadlock. */	if ((ret =	    __dd_find(dbenv, bitmap, idmap, nlockers, nalloc, &deadp)) != 0)		return (ret);	killid = BAD_KILLID;	free_me = deadp;	for (; *deadp != NULL; deadp++) {		if (abortp != NULL)			++*abortp;		killid = (u_int32_t)(*deadp - bitmap) / nalloc;		limit = killid;		/*		 * There are cases in which our general algorithm will		 * fail.  Returning 1 from verify indicates that the		 * particular locker is not only involved in a deadlock,		 * but that killing him will allow others to make forward		 * progress.  Unfortunately, there are cases where we need		 * to abort someone, but killing them will not necessarily		 * ensure forward progress (imagine N readers all trying to		 * acquire a write lock).		 * killid is only set to lockers that pass the db_verify test.		 * keeper will hold the best candidate even if it does		 * not pass db_verify.  Once we fill in killid then we do		 * not need a keeper, but we keep updating it anyway.		 */		keeper = idmap[killid].in_abort == 0 ? killid : BAD_KILLID;		if (keeper == BAD_KILLID ||		    __dd_verify(idmap, *deadp,		    tmpmap, copymap, nlockers, nalloc, keeper) == 0)			killid = BAD_KILLID;		if (killid != BAD_KILLID &&		    (atype == DB_LOCK_DEFAULT || atype == DB_LOCK_RANDOM))			goto dokill;		/*		 * Start with the id that we know is deadlocked, then examine		 * all other set bits and see if any are a better candidate		 * for abortion and they are genuinely part of the deadlock.		 * The definition of "best":		 *	MAXLOCKS: maximum count		 *	MAXWRITE: maximum write count		 *	MINLOCKS: minimum count		 *	MINWRITE: minimum write count		 *	OLDEST: smallest id		 *	YOUNGEST: largest id		 */		for (i = (limit + 1) % nlockers;		    i != limit;		    i = (i + 1) % nlockers) {			if (!ISSET_MAP(*deadp, i) || idmap[i].in_abort)				continue;			/*			 * Determine if we have a verified candidate			 * in killid, if not then compare with the			 * non-verified candidate in keeper.			 */			if (killid == BAD_KILLID) {				if (keeper == BAD_KILLID)					goto use_next;				else					cid = keeper;			} else				cid = killid;			switch (atype) {			case DB_LOCK_OLDEST:				if (__dd_isolder(idmap[cid].id,				    idmap[i].id, lock_max, txn_max))					continue;				break;			case DB_LOCK_YOUNGEST:				if (__dd_isolder(idmap[i].id,				    idmap[cid].id, lock_max, txn_max))					continue;				break;			case DB_LOCK_MAXLOCKS:				if (idmap[i].count < idmap[cid].count)					continue;				break;			case DB_LOCK_MAXWRITE:				if (idmap[i].count < idmap[cid].count)					continue;				break;			case DB_LOCK_MINLOCKS:			case DB_LOCK_MINWRITE:				if (idmap[i].count > idmap[cid].count)					continue;				break;			case DB_LOCK_DEFAULT:			case DB_LOCK_RANDOM:				goto dokill;			default:				killid = BAD_KILLID;				ret = EINVAL;				goto dokill;			}use_next:		keeper = i;			if (__dd_verify(idmap, *deadp,			    tmpmap, copymap, nlockers, nalloc, i))				killid = i;		}dokill:		if (killid == BAD_KILLID) {			if (keeper == BAD_KILLID)				/*				 * It's conceivable that under XA, the				 * locker could have gone away.				 */				continue;			else {				/*				 * Removing a single locker will not				 * break the deadlock, signal to run				 * detection again.				 */				LOCKREGION(dbenv, lt);				region->need_dd = 1;				UNLOCKREGION(dbenv, lt);				killid = keeper;			}		}		/* Kill the locker with lockid idmap[killid]. */		if ((ret = __dd_abort(dbenv, &idmap[killid])) != 0) {			/*			 * It's possible that the lock was already aborted;			 * this isn't necessarily a problem, so do not treat			 * it as an error.			 */			if (ret == DB_ALREADY_ABORTED)				ret = 0;			else				__db_err(dbenv,				    "warning: unable to abort locker %lx",				    (u_long)idmap[killid].id);		} else if (FLD_ISSET(dbenv->verbose, DB_VERB_DEADLOCK))			__db_msg(dbenv,			    "Aborting locker %lx", (u_long)idmap[killid].id);	}	__os_free(dbenv, tmpmap);err1:	__os_free(dbenv, copymap);err:	if (free_me != NULL)		__os_free(dbenv, free_me);	__os_free(dbenv, bitmap);	__os_free(dbenv, idmap);	return (ret);}/* * ======================================================================== * Utilities */# define DD_INVALID_ID	((u_int32_t) -1)static int__dd_build(dbenv, atype, bmp, nlockers, allocp, idmap)	DB_ENV *dbenv;	u_int32_t atype, **bmp, *nlockers, *allocp;	locker_info **idmap;{	struct __db_lock *lp;	DB_LOCKER *lip, *lockerp, *child;	DB_LOCKOBJ *op, *lo;	DB_LOCKREGION *region;	DB_LOCKTAB *lt;	locker_info *id_array;	db_timeval_t now, min_timeout;	u_int32_t *bitmap, count, dd, *entryp, id, ndx, nentries, *tmpmap;	u_int8_t *pptr;	int expire_only, is_first, ret;	lt = dbenv->lk_handle;	region = lt->reginfo.primary;	LOCK_SET_TIME_INVALID(&now);	LOCK_SET_TIME_MAX(&min_timeout);	expire_only = atype == DB_LOCK_EXPIRE;	/*	 * While we always check for expired timeouts, if we are called	 * with DB_LOCK_EXPIRE, then we are only checking for timeouts	 * (i.e., not doing deadlock detection at all).  If we aren't	 * doing real deadlock detection, then we can skip a significant,	 * amount of the processing.  In particular we do not build	 * the conflict array and our caller needs to expect this.	 */	if (expire_only) {		count = 0;		nentries = 0;		goto obj_loop;	}	/*	 * We'll check how many lockers there are, add a few more in for	 * good measure and then allocate all the structures.  Then we'll	 * verify that we have enough room when we go back in and get the	 * mutex the second time.	 */retry:	count = region->stat.st_nlockers;	if (count == 0) {		*nlockers = 0;		return (0);	}	if (FLD_ISSET(dbenv->verbose, DB_VERB_DEADLOCK))		__db_msg(dbenv, "%lu lockers", (u_long)count);	count += 20;	nentries = (u_int32_t)DB_ALIGN(count, 32) / 32;	/*	 * Allocate enough space for a count by count bitmap matrix.	 *	 * XXX	 * We can probably save the malloc's between iterations just	 * reallocing if necessary because count grew by too much.	 */	if ((ret = __os_calloc(dbenv, (size_t)count,	    sizeof(u_int32_t) * nentries, &bitmap)) != 0)		return (ret);	if ((ret = __os_calloc(dbenv,	    sizeof(u_int32_t), nentries, &tmpmap)) != 0) {		__os_free(dbenv, bitmap);		return (ret);	}	if ((ret = __os_calloc(dbenv,	    (size_t)count, sizeof(locker_info), &id_array)) != 0) {		__os_free(dbenv, bitmap);		__os_free(dbenv, tmpmap);		return (ret);	}	/*	 * Now go back in and actually fill in the matrix.	 */	if (region->stat.st_nlockers > count) {		__os_free(dbenv, bitmap);		__os_free(dbenv, tmpmap);		__os_free(dbenv, id_array);		goto retry;	}	/*	 * First we go through and assign each locker a deadlock detector id.	 */	for (id = 0, lip = SH_TAILQ_FIRST(&region->lockers, __db_locker);	    lip != NULL;	    lip = SH_TAILQ_NEXT(lip, ulinks, __db_locker)) {		if (lip->master_locker == INVALID_ROFF) {			lip->dd_id = id++;			id_array[lip->dd_id].id = lip->id;			switch (atype) {			case DB_LOCK_MINLOCKS:			case DB_LOCK_MAXLOCKS:				id_array[lip->dd_id].count = lip->nlocks;				break;			case DB_LOCK_MINWRITE:			case DB_LOCK_MAXWRITE:				id_array[lip->dd_id].count = lip->nwrites;				break;			}			if (F_ISSET(lip, DB_LOCKER_INABORT))				id_array[lip->dd_id].in_abort = 1;		} else

⌨️ 快捷键说明

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