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

📄 lock.c

📁 PostgreSQL7.4.6 for Linux
💻 C
📖 第 1 页 / 共 3 页
字号:
/*------------------------------------------------------------------------- * * lock.c *	  POSTGRES low-level lock mechanism * * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION *	  $Header: /cvsroot/pgsql/src/backend/storage/lmgr/lock.c,v 1.128 2003/10/16 20:59:35 tgl Exp $ * * NOTES *	  Outside modules can create a lock table and acquire/release *	  locks.  A lock table is a shared memory hash table.  When *	  a process tries to acquire a lock of a type that conflicts *	  with existing locks, it is put to sleep using the routines *	  in storage/lmgr/proc.c. * *	  For the most part, this code should be invoked via lmgr.c *	  or another lock-management module, not directly. * *	Interface: * *	LockAcquire(), LockRelease(), LockMethodTableInit(), *	LockMethodTableRename(), LockReleaseAll, *	LockCheckConflicts(), GrantLock() * *------------------------------------------------------------------------- */#include "postgres.h"#include <unistd.h>#include <signal.h>#include "access/xact.h"#include "miscadmin.h"#include "storage/proc.h"#include "utils/memutils.h"#include "utils/ps_status.h"/* This configuration variable is used to set the lock table size */int			max_locks_per_xact; /* set by guc.c */#define NLOCKENTS(maxBackends)	(max_locks_per_xact * (maxBackends))static int WaitOnLock(LOCKMETHOD lockmethod, LOCKMODE lockmode,		   LOCK *lock, PROCLOCK *proclock);static void LockCountMyLocks(SHMEM_OFFSET lockOffset, PGPROC *proc,				 int *myHolding);static char *lock_mode_names[] ={	"INVALID",	"AccessShareLock",	"RowShareLock",	"RowExclusiveLock",	"ShareUpdateExclusiveLock",	"ShareLock",	"ShareRowExclusiveLock",	"ExclusiveLock",	"AccessExclusiveLock"};#ifdef LOCK_DEBUG/*------ * The following configuration options are available for lock debugging: * *	   TRACE_LOCKS		-- give a bunch of output what's going on in this file *	   TRACE_USERLOCKS	-- same but for user locks *	   TRACE_LOCK_OIDMIN-- do not trace locks for tables below this oid *						   (use to avoid output on system tables) *	   TRACE_LOCK_TABLE -- trace locks on this table (oid) unconditionally *	   DEBUG_DEADLOCKS	-- currently dumps locks at untimely occasions ;) * * Furthermore, but in storage/lmgr/lwlock.c: *	   TRACE_LWLOCKS	-- trace lightweight locks (pretty useless) * * Define LOCK_DEBUG at compile time to get all these enabled. * -------- */int			Trace_lock_oidmin = BootstrapObjectIdData;bool		Trace_locks = false;bool		Trace_userlocks = false;int			Trace_lock_table = 0;bool		Debug_deadlocks = false;inline static boolLOCK_DEBUG_ENABLED(const LOCK *lock){	return		(((LOCK_LOCKMETHOD(*lock) == DEFAULT_LOCKMETHOD && Trace_locks)	   || (LOCK_LOCKMETHOD(*lock) == USER_LOCKMETHOD && Trace_userlocks))		 && (lock->tag.relId >= (Oid) Trace_lock_oidmin))		|| (Trace_lock_table && (lock->tag.relId == Trace_lock_table));}inline static voidLOCK_PRINT(const char *where, const LOCK *lock, LOCKMODE type){	if (LOCK_DEBUG_ENABLED(lock))		elog(LOG,			 "%s: lock(%lx) tbl(%d) rel(%u) db(%u) obj(%u) grantMask(%x) "			 "req(%d,%d,%d,%d,%d,%d,%d)=%d "			 "grant(%d,%d,%d,%d,%d,%d,%d)=%d wait(%d) type(%s)",			 where, MAKE_OFFSET(lock),			 lock->tag.lockmethod, lock->tag.relId, lock->tag.dbId,			 lock->tag.objId.blkno, lock->grantMask,			 lock->requested[1], lock->requested[2], lock->requested[3],			 lock->requested[4], lock->requested[5], lock->requested[6],			 lock->requested[7], lock->nRequested,			 lock->granted[1], lock->granted[2], lock->granted[3],			 lock->granted[4], lock->granted[5], lock->granted[6],			 lock->granted[7], lock->nGranted,			 lock->waitProcs.size, lock_mode_names[type]);}inline static voidPROCLOCK_PRINT(const char *where, const PROCLOCK *proclockP){	if (		(((PROCLOCK_LOCKMETHOD(*proclockP) == DEFAULT_LOCKMETHOD && Trace_locks)		  || (PROCLOCK_LOCKMETHOD(*proclockP) == USER_LOCKMETHOD && Trace_userlocks))		 && (((LOCK *) MAKE_PTR(proclockP->tag.lock))->tag.relId >= (Oid) Trace_lock_oidmin))		|| (Trace_lock_table && (((LOCK *) MAKE_PTR(proclockP->tag.lock))->tag.relId == Trace_lock_table))		)		elog(LOG,			 "%s: proclock(%lx) lock(%lx) tbl(%d) proc(%lx) xid(%u) hold(%d,%d,%d,%d,%d,%d,%d)=%d",			 where, MAKE_OFFSET(proclockP), proclockP->tag.lock,			 PROCLOCK_LOCKMETHOD(*(proclockP)),			 proclockP->tag.proc, proclockP->tag.xid,			 proclockP->holding[1], proclockP->holding[2], proclockP->holding[3],			 proclockP->holding[4], proclockP->holding[5], proclockP->holding[6],			 proclockP->holding[7], proclockP->nHolding);}#else							/* not LOCK_DEBUG */#define LOCK_PRINT(where, lock, type)#define PROCLOCK_PRINT(where, proclockP)#endif   /* not LOCK_DEBUG *//* * These are to simplify/speed up some bit arithmetic. * * XXX is a fetch from a static array really faster than a shift? * Wouldn't bet on it... */static LOCKMASK BITS_OFF[MAX_LOCKMODES];static LOCKMASK BITS_ON[MAX_LOCKMODES];/* * map from lockmethod to the lock table structure */static LOCKMETHODTABLE *LockMethodTable[MAX_LOCK_METHODS];static int	NumLockMethods;/* * InitLocks -- Init the lock module.  Create a private data *		structure for constructing conflict masks. */voidInitLocks(void){	int			i;	int			bit;	bit = 1;	for (i = 0; i < MAX_LOCKMODES; i++, bit <<= 1)	{		BITS_ON[i] = bit;		BITS_OFF[i] = ~bit;	}}/* * Fetch the lock method table associated with a given lock */LOCKMETHODTABLE *GetLocksMethodTable(LOCK *lock){	LOCKMETHOD	lockmethod = LOCK_LOCKMETHOD(*lock);	Assert(lockmethod > 0 && lockmethod < NumLockMethods);	return LockMethodTable[lockmethod];}/* * LockMethodInit -- initialize the lock table's lock type *		structures * * Notes: just copying.  Should only be called once. */static voidLockMethodInit(LOCKMETHODTABLE *lockMethodTable,			   LOCKMASK *conflictsP,			   int numModes){	int			i;	lockMethodTable->numLockModes = numModes;	/* copies useless zero element as well as the N lockmodes */	for (i = 0; i <= numModes; i++, conflictsP++)		lockMethodTable->conflictTab[i] = *conflictsP;}/* * LockMethodTableInit -- initialize a lock table structure * * NOTE: data structures allocated here are allocated permanently, using * TopMemoryContext and shared memory.	We don't ever release them anyway, * and in normal multi-backend operation the lock table structures set up * by the postmaster are inherited by each backend, so they must be in * TopMemoryContext. */LOCKMETHODLockMethodTableInit(char *tabName,					LOCKMASK *conflictsP,					int numModes,					int maxBackends){	LOCKMETHODTABLE *lockMethodTable;	char	   *shmemName;	HASHCTL		info;	int			hash_flags;	bool		found;	long		init_table_size,				max_table_size;	if (numModes >= MAX_LOCKMODES)		elog(ERROR, "too many lock types %d (limit is %d)",			 numModes, MAX_LOCKMODES-1);	/* Compute init/max size to request for lock hashtables */	max_table_size = NLOCKENTS(maxBackends);	init_table_size = max_table_size / 10;	/* Allocate a string for the shmem index table lookups. */	/* This is just temp space in this routine, so palloc is OK. */	shmemName = (char *) palloc(strlen(tabName) + 32);	/* each lock table has a header in shared memory */	sprintf(shmemName, "%s (lock method table)", tabName);	lockMethodTable = (LOCKMETHODTABLE *)		ShmemInitStruct(shmemName, sizeof(LOCKMETHODTABLE), &found);	if (!lockMethodTable)		elog(FATAL, "could not initialize lock table \"%s\"", tabName);	/*	 * Lock the LWLock for the table (probably not necessary here)	 */	LWLockAcquire(LockMgrLock, LW_EXCLUSIVE);	/*	 * no zero-th table	 */	NumLockMethods = 1;	/*	 * we're first - initialize	 */	if (!found)	{		MemSet(lockMethodTable, 0, sizeof(LOCKMETHODTABLE));		lockMethodTable->masterLock = LockMgrLock;		lockMethodTable->lockmethod = NumLockMethods;	}	/*	 * other modules refer to the lock table by a lockmethod ID	 */	LockMethodTable[NumLockMethods] = lockMethodTable;	NumLockMethods++;	Assert(NumLockMethods <= MAX_LOCK_METHODS);	/*	 * allocate a hash table for LOCK structs.	This is used to store	 * per-locked-object information.	 */	info.keysize = sizeof(LOCKTAG);	info.entrysize = sizeof(LOCK);	info.hash = tag_hash;	hash_flags = (HASH_ELEM | HASH_FUNCTION);	sprintf(shmemName, "%s (lock hash)", tabName);	lockMethodTable->lockHash = ShmemInitHash(shmemName,											  init_table_size,											  max_table_size,											  &info,											  hash_flags);	if (!lockMethodTable->lockHash)		elog(FATAL, "could not initialize lock table \"%s\"", tabName);	Assert(lockMethodTable->lockHash->hash == tag_hash);	/*	 * allocate a hash table for PROCLOCK structs.	This is used to store	 * per-lock-proclock information.	 */	info.keysize = sizeof(PROCLOCKTAG);	info.entrysize = sizeof(PROCLOCK);	info.hash = tag_hash;	hash_flags = (HASH_ELEM | HASH_FUNCTION);	sprintf(shmemName, "%s (proclock hash)", tabName);	lockMethodTable->proclockHash = ShmemInitHash(shmemName,												  init_table_size,												  max_table_size,												  &info,												  hash_flags);	if (!lockMethodTable->proclockHash)		elog(FATAL, "could not initialize lock table \"%s\"", tabName);	/* init data structures */	LockMethodInit(lockMethodTable, conflictsP, numModes);	LWLockRelease(LockMgrLock);	pfree(shmemName);	return lockMethodTable->lockmethod;}/* * LockMethodTableRename -- allocate another lockmethod ID to the same *		lock table. * * NOTES: Both the lock module and the lock chain (lchain.c) *		module use table id's to distinguish between different *		kinds of locks.  Short term and long term locks look *		the same to the lock table, but are handled differently *		by the lock chain manager.	This function allows the *		client to use different lockmethods when acquiring/releasing *		short term and long term locks, yet store them all in one hashtable. */LOCKMETHODLockMethodTableRename(LOCKMETHOD lockmethod){	LOCKMETHOD	newLockMethod;	if (NumLockMethods >= MAX_LOCK_METHODS)		return INVALID_LOCKMETHOD;	if (LockMethodTable[lockmethod] == INVALID_LOCKMETHOD)		return INVALID_LOCKMETHOD;	/* other modules refer to the lock table by a lockmethod ID */	newLockMethod = NumLockMethods;	NumLockMethods++;	LockMethodTable[newLockMethod] = LockMethodTable[lockmethod];	return newLockMethod;}/* * LockAcquire -- Check for lock conflicts, sleep if conflict found, *		set lock if/when no conflicts. * * Returns: TRUE if lock was acquired, FALSE otherwise.  Note that *		a FALSE return is to be expected if dontWait is TRUE; *		but if dontWait is FALSE, only a parameter error can cause *		a FALSE return.  (XXX probably we should just ereport on parameter *		errors, instead of conflating this with failure to acquire lock?) * * Side Effects: The lock is acquired and recorded in lock tables. * * NOTE: if we wait for the lock, there is no way to abort the wait * short of aborting the transaction. * * * Note on User Locks: * *		User locks are handled totally on the application side as *		long term cooperative locks which extend beyond the normal *		transaction boundaries.  Their purpose is to indicate to an *		application that someone is `working' on an item.  So it is *		possible to put an user lock on a tuple's oid, retrieve the *		tuple, work on it for an hour and then update it and remove *		the lock.  While the lock is active other clients can still *		read and write the tuple but they can be aware that it has *		been locked at the application level by someone. *		User locks use lock tags made of an uint16 and an uint32, for *		example 0 and a tuple oid, or any other arbitrary pair of *		numbers following a convention established by the application. *		In this sense tags don't refer to tuples or database entities. *		User locks and normal locks are completely orthogonal and *		they don't interfere with each other, so it is possible *		to acquire a normal lock on an user-locked tuple or user-lock *		a tuple for which a normal write lock already exists. *		User locks are always non blocking, therefore they are never *		acquired if already held by another process.  They must be *		released explicitly by the application but they are released *		automatically when a backend terminates. *		They are indicated by a lockmethod 2 which is an alias for the *		normal lock table, and are distinguished from normal locks *		by the following differences: * *										normal lock		user lock * *		lockmethod						1				2 *		tag.dbId						database oid	database oid *		tag.relId						rel oid or 0	0 *		tag.objId						block id		lock id2 *										or xact id *		tag.offnum						0				lock id1 *		proclock.xid					xid or 0		0 *		persistence						transaction		user or backend *										or backend * *		The lockmode parameter can have the same values for normal locks *		although probably only WRITE_LOCK can have some practical use. * *														DZ - 22 Nov 1997 */boolLockAcquire(LOCKMETHOD lockmethod, LOCKTAG *locktag,			TransactionId xid, LOCKMODE lockmode, bool dontWait){	PROCLOCK   *proclock;	PROCLOCKTAG proclocktag;	HTAB	   *proclockTable;	bool		found;	LOCK	   *lock;	LWLockId	masterLock;	LOCKMETHODTABLE *lockMethodTable;	int			status;	int			myHolding[MAX_LOCKMODES];	int			i;#ifdef LOCK_DEBUG	if (lockmethod == USER_LOCKMETHOD && Trace_userlocks)		elog(LOG, "LockAcquire: user lock [%u] %s",			 locktag->objId.blkno, lock_mode_names[lockmode]);#endif	/* ???????? This must be changed when short term locks will be used */	locktag->lockmethod = lockmethod;	Assert(lockmethod < NumLockMethods);	lockMethodTable = LockMethodTable[lockmethod];	if (!lockMethodTable)	{		elog(WARNING, "bad lock table id: %d", lockmethod);		return FALSE;	}	masterLock = lockMethodTable->masterLock;	LWLockAcquire(masterLock, LW_EXCLUSIVE);	/*	 * Find or create a lock with this tag	 */	Assert(lockMethodTable->lockHash->hash == tag_hash);	lock = (LOCK *) hash_search(lockMethodTable->lockHash,								(void *) locktag,								HASH_ENTER, &found);	if (!lock)	{		LWLockRelease(masterLock);		ereport(ERROR,				(errcode(ERRCODE_OUT_OF_MEMORY),				 errmsg("out of shared memory"),				 errhint("You may need to increase max_locks_per_transaction.")));	}	/*	 * if it's a new lock object, initialize it	 */	if (!found)	{		lock->grantMask = 0;		lock->waitMask = 0;		SHMQueueInit(&(lock->lockHolders));		ProcQueueInit(&(lock->waitProcs));		lock->nRequested = 0;		lock->nGranted = 0;		MemSet((char *) lock->requested, 0, sizeof(int) * MAX_LOCKMODES);		MemSet((char *) lock->granted, 0, sizeof(int) * MAX_LOCKMODES);		LOCK_PRINT("LockAcquire: new", lock, lockmode);	}	else	{		LOCK_PRINT("LockAcquire: found", lock, lockmode);		Assert((lock->nRequested >= 0) && (lock->requested[lockmode] >= 0));		Assert((lock->nGranted >= 0) && (lock->granted[lockmode] >= 0));		Assert(lock->nGranted <= lock->nRequested);	}	/*	 * Create the hash key for the proclock table.	 */	MemSet(&proclocktag, 0, sizeof(PROCLOCKTAG));		/* must clear padding,

⌨️ 快捷键说明

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