
it is sufficient to track whether a backend holds a lock or not, and store information about transaction vs. session locks only in the inside-the-backend LocalLockTable. Since there can now be but one PROCLOCK per lock per backend, LockCountMyLocks() is no longer needed, thus eliminating some O(N^2) behavior when a backend holds many locks. Also simplify the LockAcquire/LockRelease API by passing just a 'sessionLock' boolean instead of a transaction ID. The previous API was designed with the idea that per-transaction lock holding would be important for subtransactions, but now that we have subtransactions we know that this is unwanted. While at it, add an 'isTempObject' parameter to LockAcquire to indicate whether the lock is being taken on a temp table. This is not used just yet, but will be needed shortly for two-phase commit.
409 lines
14 KiB
C
409 lines
14 KiB
C
/*-------------------------------------------------------------------------
|
|
*
|
|
* lock.h
|
|
* POSTGRES low-level lock mechanism
|
|
*
|
|
*
|
|
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
|
*
|
|
* $PostgreSQL: pgsql/src/include/storage/lock.h,v 1.88 2005/06/14 22:15:33 tgl Exp $
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
#ifndef LOCK_H_
|
|
#define LOCK_H_
|
|
|
|
#include "storage/itemptr.h"
|
|
#include "storage/lwlock.h"
|
|
#include "storage/shmem.h"
|
|
|
|
|
|
/* originally in procq.h */
|
|
typedef struct PROC_QUEUE
|
|
{
|
|
SHM_QUEUE links; /* head of list of PGPROC objects */
|
|
int size; /* number of entries in list */
|
|
} PROC_QUEUE;
|
|
|
|
/* struct PGPROC is declared in proc.h, but must forward-reference it */
|
|
typedef struct PGPROC PGPROC;
|
|
|
|
/* GUC variables */
|
|
extern int max_locks_per_xact;
|
|
|
|
#ifdef LOCK_DEBUG
|
|
extern int Trace_lock_oidmin;
|
|
extern bool Trace_locks;
|
|
extern bool Trace_userlocks;
|
|
extern int Trace_lock_table;
|
|
extern bool Debug_deadlocks;
|
|
#endif /* LOCK_DEBUG */
|
|
|
|
|
|
/*
|
|
* LOCKMODE is an integer (1..N) indicating a lock type. LOCKMASK is a bit
|
|
* mask indicating a set of held or requested lock types (the bit 1<<mode
|
|
* corresponds to a particular lock mode).
|
|
*/
|
|
typedef int LOCKMASK;
|
|
typedef int LOCKMODE;
|
|
|
|
/* MAX_LOCKMODES cannot be larger than the # of bits in LOCKMASK */
|
|
#define MAX_LOCKMODES 10
|
|
|
|
#define LOCKBIT_ON(lockmode) (1 << (lockmode))
|
|
#define LOCKBIT_OFF(lockmode) (~(1 << (lockmode)))
|
|
|
|
/*
|
|
* There is normally only one lock method, the default one.
|
|
* If user locks are enabled, an additional lock method is present.
|
|
* Lock methods are identified by LOCKMETHODID. (Despite the declaration as
|
|
* uint16, we are constrained to 256 lockmethods by the layout of LOCKTAG.)
|
|
*/
|
|
typedef uint16 LOCKMETHODID;
|
|
|
|
/* MAX_LOCK_METHODS is the number of distinct lock control tables allowed */
|
|
#define MAX_LOCK_METHODS 3
|
|
|
|
#define INVALID_LOCKMETHOD 0
|
|
#define DEFAULT_LOCKMETHOD 1
|
|
#define USER_LOCKMETHOD 2
|
|
|
|
#define LockMethodIsValid(lockmethodid) ((lockmethodid) != INVALID_LOCKMETHOD)
|
|
|
|
extern int NumLockMethods;
|
|
|
|
|
|
/*
|
|
* This is the control structure for a lock table. It lives in shared
|
|
* memory. Currently, none of these fields change after startup. In addition
|
|
* to the LockMethodData, a lock table has a shared "lockHash" table holding
|
|
* per-locked-object lock information, and a shared "proclockHash" table
|
|
* holding per-lock-holder/waiter lock information.
|
|
*
|
|
* masterLock -- LWLock used to synchronize access to the table
|
|
*
|
|
* numLockModes -- number of lock types (READ,WRITE,etc) that
|
|
* are defined on this lock table
|
|
*
|
|
* conflictTab -- this is an array of bitmasks showing lock
|
|
* type conflicts. conflictTab[i] is a mask with the j-th bit
|
|
* turned on if lock types i and j conflict.
|
|
*/
|
|
typedef struct LockMethodData
|
|
{
|
|
LWLockId masterLock;
|
|
int numLockModes;
|
|
LOCKMASK conflictTab[MAX_LOCKMODES];
|
|
} LockMethodData;
|
|
|
|
typedef LockMethodData *LockMethod;
|
|
|
|
|
|
/*
|
|
* LOCKTAG is the key information needed to look up a LOCK item in the
|
|
* lock hashtable. A LOCKTAG value uniquely identifies a lockable object.
|
|
*
|
|
* The LockTagType enum defines the different kinds of objects we can lock.
|
|
* We can handle up to 256 different LockTagTypes.
|
|
*/
|
|
typedef enum LockTagType
|
|
{
|
|
LOCKTAG_RELATION, /* whole relation */
|
|
/* ID info for a relation is DB OID + REL OID; DB OID = 0 if shared */
|
|
LOCKTAG_RELATION_EXTEND, /* the right to extend a relation */
|
|
/* same ID info as RELATION */
|
|
LOCKTAG_PAGE, /* one page of a relation */
|
|
/* ID info for a page is RELATION info + BlockNumber */
|
|
LOCKTAG_TUPLE, /* one physical tuple */
|
|
/* ID info for a tuple is PAGE info + OffsetNumber */
|
|
LOCKTAG_TRANSACTION, /* transaction (for waiting for xact done) */
|
|
/* ID info for a transaction is its TransactionId */
|
|
LOCKTAG_OBJECT, /* non-relation database object */
|
|
/* ID info for an object is DB OID + CLASS OID + OBJECT OID + SUBID */
|
|
/*
|
|
* Note: object ID has same representation as in pg_depend and
|
|
* pg_description, but notice that we are constraining SUBID to 16 bits.
|
|
* Also, we use DB OID = 0 for shared objects such as tablespaces.
|
|
*/
|
|
LOCKTAG_USERLOCK /* reserved for contrib/userlock */
|
|
/* ID info for a userlock is defined by user_locks.c */
|
|
} LockTagType;
|
|
|
|
/*
|
|
* The LOCKTAG struct is defined with malice aforethought to fit into 16
|
|
* bytes with no padding. Note that this would need adjustment if we were
|
|
* to widen Oid, BlockNumber, or TransactionId to more than 32 bits.
|
|
*
|
|
* We include lockmethodid in the locktag so that a single hash table in
|
|
* shared memory can store locks of different lockmethods. For largely
|
|
* historical reasons, it's passed to the lock.c routines as a separate
|
|
* argument and then stored into the locktag.
|
|
*/
|
|
typedef struct LOCKTAG
|
|
{
|
|
uint32 locktag_field1; /* a 32-bit ID field */
|
|
uint32 locktag_field2; /* a 32-bit ID field */
|
|
uint32 locktag_field3; /* a 32-bit ID field */
|
|
uint16 locktag_field4; /* a 16-bit ID field */
|
|
uint8 locktag_type; /* see enum LockTagType */
|
|
uint8 locktag_lockmethodid; /* lockmethod indicator */
|
|
} LOCKTAG;
|
|
|
|
/*
|
|
* These macros define how we map logical IDs of lockable objects into
|
|
* the physical fields of LOCKTAG. Use these to set up LOCKTAG values,
|
|
* rather than accessing the fields directly. Note multiple eval of target!
|
|
*/
|
|
#define SET_LOCKTAG_RELATION(locktag,dboid,reloid) \
|
|
((locktag).locktag_field1 = (dboid), \
|
|
(locktag).locktag_field2 = (reloid), \
|
|
(locktag).locktag_field3 = 0, \
|
|
(locktag).locktag_field4 = 0, \
|
|
(locktag).locktag_type = LOCKTAG_RELATION)
|
|
|
|
#define SET_LOCKTAG_RELATION_EXTEND(locktag,dboid,reloid) \
|
|
((locktag).locktag_field1 = (dboid), \
|
|
(locktag).locktag_field2 = (reloid), \
|
|
(locktag).locktag_field3 = 0, \
|
|
(locktag).locktag_field4 = 0, \
|
|
(locktag).locktag_type = LOCKTAG_RELATION_EXTEND)
|
|
|
|
#define SET_LOCKTAG_PAGE(locktag,dboid,reloid,blocknum) \
|
|
((locktag).locktag_field1 = (dboid), \
|
|
(locktag).locktag_field2 = (reloid), \
|
|
(locktag).locktag_field3 = (blocknum), \
|
|
(locktag).locktag_field4 = 0, \
|
|
(locktag).locktag_type = LOCKTAG_PAGE)
|
|
|
|
#define SET_LOCKTAG_TUPLE(locktag,dboid,reloid,blocknum,offnum) \
|
|
((locktag).locktag_field1 = (dboid), \
|
|
(locktag).locktag_field2 = (reloid), \
|
|
(locktag).locktag_field3 = (blocknum), \
|
|
(locktag).locktag_field4 = (offnum), \
|
|
(locktag).locktag_type = LOCKTAG_TUPLE)
|
|
|
|
#define SET_LOCKTAG_TRANSACTION(locktag,xid) \
|
|
((locktag).locktag_field1 = (xid), \
|
|
(locktag).locktag_field2 = 0, \
|
|
(locktag).locktag_field3 = 0, \
|
|
(locktag).locktag_field4 = 0, \
|
|
(locktag).locktag_type = LOCKTAG_TRANSACTION)
|
|
|
|
#define SET_LOCKTAG_OBJECT(locktag,dboid,classoid,objoid,objsubid) \
|
|
((locktag).locktag_field1 = (dboid), \
|
|
(locktag).locktag_field2 = (classoid), \
|
|
(locktag).locktag_field3 = (objoid), \
|
|
(locktag).locktag_field4 = (objsubid), \
|
|
(locktag).locktag_type = LOCKTAG_OBJECT)
|
|
|
|
|
|
/*
|
|
* Per-locked-object lock information:
|
|
*
|
|
* tag -- uniquely identifies the object being locked
|
|
* grantMask -- bitmask for all lock types currently granted on this object.
|
|
* waitMask -- bitmask for all lock types currently awaited on this object.
|
|
* procLocks -- list of PROCLOCK objects for this lock.
|
|
* waitProcs -- queue of processes waiting for this lock.
|
|
* requested -- count of each lock type currently requested on the lock
|
|
* (includes requests already granted!!).
|
|
* nRequested -- total requested locks of all types.
|
|
* granted -- count of each lock type currently granted on the lock.
|
|
* nGranted -- total granted locks of all types.
|
|
*
|
|
* Note: these counts count 1 for each backend. Internally to a backend,
|
|
* there may be multiple grabs on a particular lock, but this is not reflected
|
|
* into shared memory.
|
|
*/
|
|
typedef struct LOCK
|
|
{
|
|
/* hash key */
|
|
LOCKTAG tag; /* unique identifier of lockable object */
|
|
|
|
/* data */
|
|
LOCKMASK grantMask; /* bitmask for lock types already granted */
|
|
LOCKMASK waitMask; /* bitmask for lock types awaited */
|
|
SHM_QUEUE procLocks; /* list of PROCLOCK objects assoc. with
|
|
* lock */
|
|
PROC_QUEUE waitProcs; /* list of PGPROC objects waiting on lock */
|
|
int requested[MAX_LOCKMODES]; /* counts of requested
|
|
* locks */
|
|
int nRequested; /* total of requested[] array */
|
|
int granted[MAX_LOCKMODES]; /* counts of granted locks */
|
|
int nGranted; /* total of granted[] array */
|
|
} LOCK;
|
|
|
|
#define LOCK_LOCKMETHOD(lock) ((LOCKMETHODID) (lock).tag.locktag_lockmethodid)
|
|
|
|
|
|
/*
|
|
* We may have several different backends holding or awaiting locks
|
|
* on the same lockable object. We need to store some per-holder/waiter
|
|
* information for each such holder (or would-be holder). This is kept in
|
|
* a PROCLOCK struct.
|
|
*
|
|
* PROCLOCKTAG is the key information needed to look up a PROCLOCK item in the
|
|
* proclock hashtable. A PROCLOCKTAG value uniquely identifies the combination
|
|
* of a lockable object and a holder/waiter for that object.
|
|
*
|
|
* Internally to a backend, it is possible for the same lock to be held
|
|
* for different purposes: the backend tracks transaction locks separately
|
|
* from session locks. However, this is not reflected in the shared-memory
|
|
* state: we only track which backend(s) hold the lock. This is OK since a
|
|
* backend can never block itself.
|
|
*
|
|
* The holdMask field shows the already-granted locks represented by this
|
|
* proclock. Note that there will be a proclock object, possibly with
|
|
* zero holdMask, for any lock that the process is currently waiting on.
|
|
* Otherwise, proclock objects whose holdMasks are zero are recycled
|
|
* as soon as convenient.
|
|
*
|
|
* releaseMask is workspace for LockReleaseAll(): it shows the locks due
|
|
* to be released during the current call. This must only be examined or
|
|
* set by the backend owning the PROCLOCK.
|
|
*
|
|
* Each PROCLOCK object is linked into lists for both the associated LOCK
|
|
* object and the owning PGPROC object. Note that the PROCLOCK is entered
|
|
* into these lists as soon as it is created, even if no lock has yet been
|
|
* granted. A PGPROC that is waiting for a lock to be granted will also be
|
|
* linked into the lock's waitProcs queue.
|
|
*/
|
|
typedef struct PROCLOCKTAG
|
|
{
|
|
SHMEM_OFFSET lock; /* link to per-lockable-object information */
|
|
SHMEM_OFFSET proc; /* link to PGPROC of owning backend */
|
|
} PROCLOCKTAG;
|
|
|
|
typedef struct PROCLOCK
|
|
{
|
|
/* tag */
|
|
PROCLOCKTAG tag; /* unique identifier of proclock object */
|
|
|
|
/* data */
|
|
LOCKMASK holdMask; /* bitmask for lock types currently held */
|
|
LOCKMASK releaseMask; /* bitmask for lock types to be released */
|
|
SHM_QUEUE lockLink; /* list link in LOCK's list of proclocks */
|
|
SHM_QUEUE procLink; /* list link in PGPROC's list of proclocks */
|
|
} PROCLOCK;
|
|
|
|
#define PROCLOCK_LOCKMETHOD(proclock) \
|
|
LOCK_LOCKMETHOD(*((LOCK *) MAKE_PTR((proclock).tag.lock)))
|
|
|
|
/*
|
|
* Each backend also maintains a local hash table with information about each
|
|
* lock it is currently interested in. In particular the local table counts
|
|
* the number of times that lock has been acquired. This allows multiple
|
|
* requests for the same lock to be executed without additional accesses to
|
|
* shared memory. We also track the number of lock acquisitions per
|
|
* ResourceOwner, so that we can release just those locks belonging to a
|
|
* particular ResourceOwner.
|
|
*/
|
|
typedef struct LOCALLOCKTAG
|
|
{
|
|
LOCKTAG lock; /* identifies the lockable object */
|
|
LOCKMODE mode; /* lock mode for this table entry */
|
|
} LOCALLOCKTAG;
|
|
|
|
typedef struct LOCALLOCKOWNER
|
|
{
|
|
/*
|
|
* Note: if owner is NULL then the lock is held on behalf of the session;
|
|
* otherwise it is held on behalf of my current transaction.
|
|
*
|
|
* Must use a forward struct reference to avoid circularity.
|
|
*/
|
|
struct ResourceOwnerData *owner;
|
|
int nLocks; /* # of times held by this owner */
|
|
} LOCALLOCKOWNER;
|
|
|
|
typedef struct LOCALLOCK
|
|
{
|
|
/* tag */
|
|
LOCALLOCKTAG tag; /* unique identifier of locallock entry */
|
|
|
|
/* data */
|
|
LOCK *lock; /* associated LOCK object in shared mem */
|
|
PROCLOCK *proclock; /* associated PROCLOCK object in shmem */
|
|
bool isTempObject; /* true if lock is on a temporary object */
|
|
int nLocks; /* total number of times lock is held */
|
|
int numLockOwners; /* # of relevant ResourceOwners */
|
|
int maxLockOwners; /* allocated size of array */
|
|
LOCALLOCKOWNER *lockOwners; /* dynamically resizable array */
|
|
} LOCALLOCK;
|
|
|
|
#define LOCALLOCK_LOCKMETHOD(llock) ((llock).tag.lock.locktag_lockmethodid)
|
|
|
|
|
|
/*
|
|
* This struct holds information passed from lmgr internals to the lock
|
|
* listing user-level functions (lockfuncs.c). For each PROCLOCK in the
|
|
* system, the SHMEM_OFFSET, PROCLOCK itself, and associated PGPROC and
|
|
* LOCK objects are stored. (Note there will often be multiple copies
|
|
* of the same PGPROC or LOCK.) We do not store the SHMEM_OFFSET of the
|
|
* PGPROC or LOCK separately, since they're in the PROCLOCK's tag fields.
|
|
*/
|
|
typedef struct
|
|
{
|
|
int nelements; /* The length of each of the arrays */
|
|
SHMEM_OFFSET *proclockaddrs;
|
|
PROCLOCK *proclocks;
|
|
PGPROC *procs;
|
|
LOCK *locks;
|
|
} LockData;
|
|
|
|
|
|
/* Result codes for LockAcquire() */
|
|
typedef enum
|
|
{
|
|
LOCKACQUIRE_NOT_AVAIL, /* lock not available, and dontWait=true */
|
|
LOCKACQUIRE_OK, /* lock successfully acquired */
|
|
LOCKACQUIRE_ALREADY_HELD /* incremented count for lock already held */
|
|
} LockAcquireResult;
|
|
|
|
|
|
/*
|
|
* function prototypes
|
|
*/
|
|
extern void InitLocks(void);
|
|
extern LockMethod GetLocksMethodTable(LOCK *lock);
|
|
extern LOCKMETHODID LockMethodTableInit(const char *tabName,
|
|
const LOCKMASK *conflictsP,
|
|
int numModes, int maxBackends);
|
|
extern LOCKMETHODID LockMethodTableRename(LOCKMETHODID lockmethodid);
|
|
extern LockAcquireResult LockAcquire(LOCKMETHODID lockmethodid,
|
|
LOCKTAG *locktag,
|
|
bool isTempObject,
|
|
LOCKMODE lockmode,
|
|
bool sessionLock,
|
|
bool dontWait);
|
|
extern bool LockRelease(LOCKMETHODID lockmethodid, LOCKTAG *locktag,
|
|
LOCKMODE lockmode, bool sessionLock);
|
|
extern void LockReleaseAll(LOCKMETHODID lockmethodid, bool allLocks);
|
|
extern void LockReleaseCurrentOwner(void);
|
|
extern void LockReassignCurrentOwner(void);
|
|
extern int LockCheckConflicts(LockMethod lockMethodTable,
|
|
LOCKMODE lockmode,
|
|
LOCK *lock, PROCLOCK *proclock, PGPROC *proc);
|
|
extern void GrantLock(LOCK *lock, PROCLOCK *proclock, LOCKMODE lockmode);
|
|
extern void GrantAwaitedLock(void);
|
|
extern void RemoveFromWaitQueue(PGPROC *proc);
|
|
extern int LockShmemSize(int maxBackends);
|
|
extern bool DeadLockCheck(PGPROC *proc);
|
|
extern void DeadLockReport(void);
|
|
extern void RememberSimpleDeadLock(PGPROC *proc1,
|
|
LOCKMODE lockmode,
|
|
LOCK *lock,
|
|
PGPROC *proc2);
|
|
extern void InitDeadLockChecking(void);
|
|
extern LockData *GetLockStatusData(void);
|
|
extern const char *GetLockmodeName(LOCKMODE mode);
|
|
|
|
#ifdef LOCK_DEBUG
|
|
extern void DumpLocks(void);
|
|
extern void DumpAllLocks(void);
|
|
#endif
|
|
|
|
#endif /* LOCK_H */
|