📄 lock.c
字号:
/*------------------------------------------------------------------------- * * lock.c * simple lock acquisition * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION * $Header: /usr/local/cvsroot/pgsql/src/backend/storage/lmgr/lock.c,v 1.55 1999/05/29 06:14:42 vadim 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 conflictRs * with existing locks, it is put to sleep using the routines * in storage/lmgr/proc.c. * * Interface: * * LockAcquire(), LockRelease(), LockMethodTableInit(), * LockMethodTableRename(), LockReleaseAll, LockOwners() * LockResolveConflicts(), GrantLock() * * NOTE: This module is used to define new lock tables. The * multi-level lock table (multi.c) used by the heap * access methods calls these routines. See multi.c for * examples showing how to use this interface. * *------------------------------------------------------------------------- */#include <stdio.h> /* for sprintf() */#include <string.h>#include <sys/types.h>#include <unistd.h>#include <signal.h>#include "postgres.h"#include "miscadmin.h"#include "storage/shmem.h"#include "storage/sinvaladt.h"#include "storage/spin.h"#include "storage/proc.h"#include "storage/lock.h"#include "utils/hsearch.h"#include "utils/memutils.h"#include "utils/palloc.h"#include "access/xact.h"#include "access/transam.h"#include "utils/trace.h"#include "utils/ps_status.h"static int WaitOnLock(LOCKMETHOD lockmethod, LOCK *lock, LOCKMODE lockmode);/* * lockDebugRelation can be used to trace unconditionally a single relation, * for example pg_listener, if you suspect there are locking problems. * * lockDebugOidMin is is used to avoid tracing postgres relations, which * would produce a lot of output. Unfortunately most system relations are * created after bootstrap and have oid greater than BootstrapObjectIdData. * If you are using tprintf you should specify a value greater than the max * oid of system relations, which can be found with the following query: * * select max(int4in(int4out(oid))) from pg_class where relname ~ '^pg_'; * * To get a useful lock trace you can use the following pg_options: * * -T "verbose,query,locks,userlocks,lock_debug_oidmin=17500" */#define LOCKDEBUG(lockmethod) (pg_options[TRACE_SHORTLOCKS+lockmethod])#define lockDebugRelation (pg_options[TRACE_LOCKRELATION])#define lockDebugOidMin (pg_options[TRACE_LOCKOIDMIN])#define lockReadPriority (pg_options[OPT_LOCKREADPRIORITY])#ifdef LOCK_MGR_DEBUG#define LOCK_PRINT(where,lock,type) \ if (((LOCKDEBUG(LOCK_LOCKMETHOD(*(lock))) >= 1) \ && (lock->tag.relId >= lockDebugOidMin)) \ || \ (lockDebugRelation && (lock->tag.relId == lockDebugRelation))) \ LOCK_PRINT_AUX(where,lock,type)#define LOCK_PRINT_AUX(where,lock,type) \ TPRINTF(TRACE_ALL, \ "%s: lock(%x) tbl(%d) rel(%u) db(%u) obj(%u) mask(%x) " \ "hold(%d,%d,%d,%d,%d,%d,%d)=%d " \ "act(%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->mask, \ lock->holders[1], \ lock->holders[2], \ lock->holders[3], \ lock->holders[4], \ lock->holders[5], \ lock->holders[6], \ lock->holders[7], \ lock->nHolding, \ lock->activeHolders[1], \ lock->activeHolders[2], \ lock->activeHolders[3], \ lock->activeHolders[4], \ lock->activeHolders[5], \ lock->activeHolders[6], \ lock->activeHolders[7], \ lock->nActive, \ lock->waitProcs.size, \ lock_types[type])#define XID_PRINT(where,xidentP) \ if (((LOCKDEBUG(XIDENT_LOCKMETHOD(*(xidentP))) >= 1) \ && (((LOCK *)MAKE_PTR(xidentP->tag.lock))->tag.relId \ >= lockDebugOidMin)) \ || (lockDebugRelation && \ (((LOCK *)MAKE_PTR(xidentP->tag.lock))->tag.relId \ == lockDebugRelation))) \ XID_PRINT_AUX(where,xidentP)#define XID_PRINT_AUX(where,xidentP) \ TPRINTF(TRACE_ALL, \ "%s: xid(%x) lock(%x) tbl(%d) pid(%d) xid(%u) " \ "hold(%d,%d,%d,%d,%d,%d,%d)=%d", \ where, \ MAKE_OFFSET(xidentP), \ xidentP->tag.lock, \ XIDENT_LOCKMETHOD(*(xidentP)), \ xidentP->tag.pid, \ xidentP->tag.xid, \ xidentP->holders[1], \ xidentP->holders[2], \ xidentP->holders[3], \ xidentP->holders[4], \ xidentP->holders[5], \ xidentP->holders[6], \ xidentP->holders[7], \ xidentP->nHolding)#else /* !LOCK_MGR_DEBUG */#define LOCK_PRINT(where,lock,type)#define LOCK_PRINT_AUX(where,lock,type)#define XID_PRINT(where,xidentP)#define XID_PRINT_AUX(where,xidentP)#endif /* !LOCK_MGR_DEBUG */static char *lock_types[] = { "INVALID", "AccessShareLock", "RowShareLock", "RowExclusiveLock", "ShareLock", "ShareRowExclusiveLock", "ExclusiveLock", "AccessExclusiveLock"};SPINLOCK LockMgrLock; /* in Shmem or created in * CreateSpinlocks() *//* This is to simplify/speed up some bit arithmetic */static MASK BITS_OFF[MAX_LOCKMODES];static MASK BITS_ON[MAX_LOCKMODES];/* ----------------- * XXX Want to move this to this file * ----------------- */static bool LockingIsDisabled;/* ------------------- * 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(){ int i; int bit; bit = 1; /* ------------------- * remember 0th lockmode is invalid * ------------------- */ for (i = 0; i < MAX_LOCKMODES; i++, bit <<= 1) { BITS_ON[i] = bit; BITS_OFF[i] = ~bit; }#ifdef LOCK_MGR_DEBUG /* * If lockDebugOidMin value has not been specified in pg_options set a * default value. */ if (!lockDebugOidMin) lockDebugOidMin = BootstrapObjectIdData;#endif}/* ------------------- * LockDisable -- sets LockingIsDisabled flag to TRUE or FALSE. * ------------------ */voidLockDisable(int status){ LockingIsDisabled = status;}/* * LockMethodInit -- initialize the lock table's lock type * structures * * Notes: just copying. Should only be called once. */static voidLockMethodInit(LOCKMETHODTABLE *lockMethodTable, MASK *conflictsP, int *prioP, int numModes){ int i; lockMethodTable->ctl->numLockModes = numModes; numModes++; for (i = 0; i < numModes; i++, prioP++, conflictsP++) { lockMethodTable->ctl->conflictTab[i] = *conflictsP; lockMethodTable->ctl->prio[i] = *prioP; }}/* * LockMethodTableInit -- initialize a lock table structure * * Notes: * (a) a lock table has four separate entries in the shmem index * table. This is because every shared hash table and spinlock * has its name stored in the shmem index at its creation. It * is wasteful, in this case, but not much space is involved. * */LOCKMETHODLockMethodTableInit(char *tabName, MASK *conflictsP, int *prioP, int numModes){ LOCKMETHODTABLE *lockMethodTable; char *shmemName; HASHCTL info; int hash_flags; bool found; int status = TRUE; if (numModes > MAX_LOCKMODES) { elog(NOTICE, "LockMethodTableInit: too many lock types %d greater than %d", numModes, MAX_LOCKMODES); return INVALID_LOCKMETHOD; } /* allocate a string for the shmem index table lookup */ shmemName = (char *) palloc((unsigned) (strlen(tabName) + 32)); if (!shmemName) { elog(NOTICE, "LockMethodTableInit: couldn't malloc string %s \n", tabName); return INVALID_LOCKMETHOD; } /* each lock table has a non-shared header */ lockMethodTable = (LOCKMETHODTABLE *) palloc((unsigned) sizeof(LOCKMETHODTABLE)); if (!lockMethodTable) { elog(NOTICE, "LockMethodTableInit: couldn't malloc lock table %s\n", tabName); pfree(shmemName); return INVALID_LOCKMETHOD; } /* ------------------------ * find/acquire the spinlock for the table * ------------------------ */ SpinAcquire(LockMgrLock); /* ----------------------- * allocate a control structure from shared memory or attach to it * if it already exists. * ----------------------- */ sprintf(shmemName, "%s (ctl)", tabName); lockMethodTable->ctl = (LOCKMETHODCTL *) ShmemInitStruct(shmemName, (unsigned) sizeof(LOCKMETHODCTL), &found); if (!lockMethodTable->ctl) { elog(FATAL, "LockMethodTableInit: couldn't initialize %s", tabName); status = FALSE; } /* ------------------- * no zero-th table * ------------------- */ NumLockMethods = 1; /* ---------------- * we're first - initialize * ---------------- */ if (!found) { MemSet(lockMethodTable->ctl, 0, sizeof(LOCKMETHODCTL)); lockMethodTable->ctl->masterLock = LockMgrLock; lockMethodTable->ctl->lockmethod = NumLockMethods; } /* -------------------- * other modules refer to the lock table by a lockmethod * -------------------- */ LockMethodTable[NumLockMethods] = lockMethodTable; NumLockMethods++; Assert(NumLockMethods <= MAX_LOCK_METHODS); /* ---------------------- * allocate a hash table for the lock tags. This is used * to find the different locks. * ---------------------- */ info.keysize = SHMEM_LOCKTAB_KEYSIZE; info.datasize = SHMEM_LOCKTAB_DATASIZE; info.hash = tag_hash; hash_flags = (HASH_ELEM | HASH_FUNCTION); sprintf(shmemName, "%s (lock hash)", tabName); lockMethodTable->lockHash = (HTAB *) ShmemInitHash(shmemName, INIT_TABLE_SIZE, MAX_TABLE_SIZE, &info, hash_flags); Assert(lockMethodTable->lockHash->hash == tag_hash); if (!lockMethodTable->lockHash) { elog(FATAL, "LockMethodTableInit: couldn't initialize %s", tabName); status = FALSE; } /* ------------------------- * allocate an xid table. When different transactions hold * the same lock, additional information must be saved (locks per tx). * ------------------------- */ info.keysize = SHMEM_XIDTAB_KEYSIZE; info.datasize = SHMEM_XIDTAB_DATASIZE; info.hash = tag_hash; hash_flags = (HASH_ELEM | HASH_FUNCTION); sprintf(shmemName, "%s (xid hash)", tabName); lockMethodTable->xidHash = (HTAB *) ShmemInitHash(shmemName, INIT_TABLE_SIZE, MAX_TABLE_SIZE, &info, hash_flags); if (!lockMethodTable->xidHash) { elog(FATAL, "LockMethodTableInit: couldn't initialize %s", tabName); status = FALSE; } /* init ctl data structures */ LockMethodInit(lockMethodTable, conflictsP, prioP, numModes); SpinRelease(LockMgrLock); pfree(shmemName); if (status) return lockMethodTable->ctl->lockmethod; else return INVALID_LOCKMETHOD;}/* * LockMethodTableRename -- allocate another lockmethod 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. */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 */ 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 parameters are correct, FALSE otherwise. * * Side Effects: The lock is always acquired. No way to abort * a lock acquisition other than aborting the transaction. * Lock is recorded in the lkchain. *#ifdef USER_LOCKS * * 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 * for the following differences: * * normal lock user lock * * lockmethod 1 2 * tag.relId rel oid 0 * tag.ItemPointerData.ip_blkid block id lock id2 * tag.ItemPointerData.ip_posid tuple offset lock id1 * xid.pid 0 backend pid * xid.xid xid or 0 0 * persistence transaction user 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#endif */boolLockAcquire(LOCKMETHOD lockmethod, LOCKTAG *locktag, LOCKMODE lockmode){ XIDLookupEnt *result, item; HTAB *xidTable; bool found; LOCK *lock = NULL; SPINLOCK masterLock; LOCKMETHODTABLE *lockMethodTable; int status; TransactionId xid;#ifdef USER_LOCKS int is_user_lock; is_user_lock = (lockmethod == USER_LOCKMETHOD); if (is_user_lock) {#ifdef USER_LOCKS_DEBUG TPRINTF(TRACE_USERLOCKS, "LockAcquire: user lock [%u] %s", locktag->objId.blkno, lock_types[lockmode]);#endif }#endif /* ???????? This must be changed when short term locks will be used */ locktag->lockmethod = lockmethod; Assert(lockmethod < NumLockMethods); lockMethodTable = LockMethodTable[lockmethod]; if (!lockMethodTable) { elog(NOTICE, "LockAcquire: bad lock table %d", lockmethod); return FALSE; } if (LockingIsDisabled) return TRUE; masterLock = lockMethodTable->ctl->masterLock; SpinAcquire(masterLock); /* * Find or create a lock with this tag
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -