📄 lock.c
字号:
/*------------------------------------------------------------------------- * * lock.c * POSTGRES low-level lock mechanism * * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION * $PostgreSQL: pgsql/src/backend/storage/lmgr/lock.c,v 1.159.2.1 2005/11/22 18:23:18 momjian 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 <signal.h>#include <unistd.h>#include "access/twophase.h"#include "access/twophase_rmgr.h"#include "access/xact.h"#include "miscadmin.h"#include "storage/proc.h"#include "utils/memutils.h"#include "utils/ps_status.h"#include "utils/resowner.h"/* This configuration variable is used to set the lock table size */int max_locks_per_xact; /* set by guc.c */#define NLOCKENTS() \ mul_size(max_locks_per_xact, add_size(MaxBackends, max_prepared_xacts))/* Record that's written to 2PC state file when a lock is persisted */typedef struct TwoPhaseLockRecord{ LOCKTAG locktag; LOCKMODE lockmode;} TwoPhaseLockRecord;/* * map from lock method id to the lock table data structures */static LockMethod LockMethods[MAX_LOCK_METHODS];static HTAB *LockMethodLockHash[MAX_LOCK_METHODS];static HTAB *LockMethodProcLockHash[MAX_LOCK_METHODS];static HTAB *LockMethodLocalHash[MAX_LOCK_METHODS];/* exported so lmgr.c can initialize it */int NumLockMethods;/* private state for GrantAwaitedLock */static LOCALLOCK *awaitedLock;static ResourceOwner awaitedOwner;static const char *const 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 = FirstNormalObjectId;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 (((Trace_locks && LOCK_LOCKMETHOD(*lock) == DEFAULT_LOCKMETHOD) || (Trace_userlocks && LOCK_LOCKMETHOD(*lock) == USER_LOCKMETHOD)) && ((Oid) lock->tag.locktag_field2 >= (Oid) Trace_lock_oidmin)) || (Trace_lock_table && (lock->tag.locktag_field2 == 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) id(%u,%u,%u,%u,%u,%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.locktag_field1, lock->tag.locktag_field2, lock->tag.locktag_field3, lock->tag.locktag_field4, lock->tag.locktag_type, lock->tag.locktag_lockmethodid, 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 (LOCK_DEBUG_ENABLED((LOCK *) MAKE_PTR(proclockP->tag.lock))) elog(LOG, "%s: proclock(%lx) lock(%lx) method(%u) proc(%lx) hold(%x)", where, MAKE_OFFSET(proclockP), proclockP->tag.lock, PROCLOCK_LOCKMETHOD(*(proclockP)), proclockP->tag.proc, (int) proclockP->holdMask);}#else /* not LOCK_DEBUG */#define LOCK_PRINT(where, lock, type)#define PROCLOCK_PRINT(where, proclockP)#endif /* not LOCK_DEBUG */static void RemoveLocalLock(LOCALLOCK *locallock);static void GrantLockLocal(LOCALLOCK *locallock, ResourceOwner owner);static void WaitOnLock(LOCKMETHODID lockmethodid, LOCALLOCK *locallock, ResourceOwner owner);static bool UnGrantLock(LOCK *lock, LOCKMODE lockmode, PROCLOCK *proclock, LockMethod lockMethodTable);static void CleanUpLock(LOCKMETHODID lockmethodid, LOCK *lock, PROCLOCK *proclock, bool wakeupNeeded);/* * InitLocks -- Init the lock module. Nothing to do here at present. */voidInitLocks(void){ /* NOP */}/* * Fetch the lock method table associated with a given lock */LockMethodGetLocksMethodTable(LOCK *lock){ LOCKMETHODID lockmethodid = LOCK_LOCKMETHOD(*lock); Assert(0 < lockmethodid && lockmethodid < NumLockMethods); return LockMethods[lockmethodid];}/* * LockMethodInit -- initialize the lock table's lock type * structures * * Notes: just copying. Should only be called once. */static voidLockMethodInit(LockMethod lockMethodTable, const 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++) lockMethodTable->conflictTab[i] = conflictsP[i];}/* * 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. */LOCKMETHODIDLockMethodTableInit(const char *tabName, const LOCKMASK *conflictsP, int numModes){ LockMethod newLockMethod; LOCKMETHODID lockmethodid; 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(); init_table_size = max_table_size / 2; /* 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); newLockMethod = (LockMethod) ShmemInitStruct(shmemName, sizeof(LockMethodData), &found); if (!newLockMethod) elog(FATAL, "could not initialize lock table \"%s\"", tabName); /* * we're first - initialize */ if (!found) { MemSet(newLockMethod, 0, sizeof(LockMethodData)); newLockMethod->masterLock = LockMgrLock; LockMethodInit(newLockMethod, conflictsP, numModes); } /* * other modules refer to the lock table by a lockmethod ID */ Assert(NumLockMethods < MAX_LOCK_METHODS); lockmethodid = NumLockMethods++; LockMethods[lockmethodid] = newLockMethod; /* * allocate a hash table for LOCK structs. This is used to store * per-locked-object information. */ MemSet(&info, 0, sizeof(info)); info.keysize = sizeof(LOCKTAG); info.entrysize = sizeof(LOCK); info.hash = tag_hash; hash_flags = (HASH_ELEM | HASH_FUNCTION); sprintf(shmemName, "%s (lock hash)", tabName); LockMethodLockHash[lockmethodid] = ShmemInitHash(shmemName, init_table_size, max_table_size, &info, hash_flags); if (!LockMethodLockHash[lockmethodid]) elog(FATAL, "could not initialize lock table \"%s\"", tabName); /* * allocate a hash table for PROCLOCK structs. This is used to store * per-lock-holder 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); LockMethodProcLockHash[lockmethodid] = ShmemInitHash(shmemName, init_table_size, max_table_size, &info, hash_flags); if (!LockMethodProcLockHash[lockmethodid]) elog(FATAL, "could not initialize lock table \"%s\"", tabName); /* * allocate a non-shared hash table for LOCALLOCK structs. This is used * to store lock counts and resource owner information. * * The non-shared table could already exist in this process (this occurs * when the postmaster is recreating shared memory after a backend crash). * If so, delete and recreate it. (We could simply leave it, since it * ought to be empty in the postmaster, but for safety let's zap it.) */ if (LockMethodLocalHash[lockmethodid]) hash_destroy(LockMethodLocalHash[lockmethodid]); info.keysize = sizeof(LOCALLOCKTAG); info.entrysize = sizeof(LOCALLOCK); info.hash = tag_hash; hash_flags = (HASH_ELEM | HASH_FUNCTION); sprintf(shmemName, "%s (locallock hash)", tabName); LockMethodLocalHash[lockmethodid] = hash_create(shmemName, 128, &info, hash_flags); pfree(shmemName); return lockmethodid;}/* * LockMethodTableRename -- allocate another lockmethod ID to the same * lock table. * * NOTES: This function makes it possible to have different lockmethodids, * and hence different locking semantics, while still storing all * the data in one shared-memory hashtable. */LOCKMETHODIDLockMethodTableRename(LOCKMETHODID lockmethodid){ LOCKMETHODID newLockMethodId; if (NumLockMethods >= MAX_LOCK_METHODS) return INVALID_LOCKMETHOD; if (LockMethods[lockmethodid] == INVALID_LOCKMETHOD) return INVALID_LOCKMETHOD; /* other modules refer to the lock table by a lockmethod ID */ newLockMethodId = NumLockMethods; NumLockMethods++; LockMethods[newLockMethodId] = LockMethods[lockmethodid]; LockMethodLockHash[newLockMethodId] = LockMethodLockHash[lockmethodid]; LockMethodProcLockHash[newLockMethodId] = LockMethodProcLockHash[lockmethodid]; LockMethodLocalHash[newLockMethodId] = LockMethodLocalHash[lockmethodid]; return newLockMethodId;}/* * LockAcquire -- Check for lock conflicts, sleep if conflict found, * set lock if/when no conflicts. * * Inputs: * lockmethodid: identifies which lock table to use * locktag: unique identifier for the lockable object * isTempObject: is the lockable object a temporary object? (Under 2PC, * such locks cannot be persisted) * lockmode: lock mode to acquire * sessionLock: if true, acquire lock for session not current transaction * dontWait: if true, don't wait to acquire lock * * Returns one of: * LOCKACQUIRE_NOT_AVAIL lock not available, and dontWait=true * LOCKACQUIRE_OK lock successfully acquired * LOCKACQUIRE_ALREADY_HELD incremented count for lock already held * * In the normal case where dontWait=false and the caller doesn't need to * distinguish a freshly acquired lock from one already taken earlier in * this same transaction, there is no need to examine the return value. * * 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 and normal locks are completely orthogonal and * they don't interfere with each other. * * 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. * * 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 */LockAcquireResultLockAcquire(LOCKMETHODID lockmethodid, LOCKTAG *locktag, bool isTempObject, LOCKMODE lockmode, bool sessionLock, bool dontWait){ LOCALLOCKTAG localtag; LOCALLOCK *locallock; LOCK *lock; PROCLOCK *proclock; PROCLOCKTAG proclocktag; bool found; ResourceOwner owner; LWLockId masterLock; LockMethod lockMethodTable; int status;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -