📄 lock.c
字号:
/*------------------------------------------------------------------------- * * 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 + -