1996-07-09 08:22:35 +02:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
1999-02-14 00:22:53 +01:00
|
|
|
* lock.c
|
2000-12-22 01:51:54 +01:00
|
|
|
* POSTGRES low-level lock mechanism
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
2000-01-26 06:58:53 +01:00
|
|
|
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
|
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
*
|
|
|
|
* IDENTIFICATION
|
2001-01-10 02:24:19 +01:00
|
|
|
* $Header: /cvsroot/pgsql/src/backend/storage/lmgr/lock.c,v 1.76 2001/01/10 01:24:19 inoue Exp $
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
* NOTES
|
1997-09-07 07:04:48 +02:00
|
|
|
* Outside modules can create a lock table and acquire/release
|
|
|
|
* locks. A lock table is a shared memory hash table. When
|
2000-12-22 01:51:54 +01:00
|
|
|
* a process tries to acquire a lock of a type that conflicts
|
1997-09-07 07:04:48 +02:00
|
|
|
* with existing locks, it is put to sleep using the routines
|
|
|
|
* in storage/lmgr/proc.c.
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
2000-12-22 01:51:54 +01:00
|
|
|
* For the most part, this code should be invoked via lmgr.c
|
|
|
|
* or another lock-management module, not directly.
|
|
|
|
*
|
1997-09-07 07:04:48 +02:00
|
|
|
* Interface:
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
1998-08-25 23:20:32 +02:00
|
|
|
* LockAcquire(), LockRelease(), LockMethodTableInit(),
|
2000-07-17 05:05:41 +02:00
|
|
|
* LockMethodTableRename(), LockReleaseAll,
|
1998-08-25 23:20:32 +02:00
|
|
|
* LockResolveConflicts(), GrantLock()
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
*/
|
1996-12-26 18:50:26 +01:00
|
|
|
#include <sys/types.h>
|
|
|
|
#include <unistd.h>
|
1998-08-25 23:20:32 +02:00
|
|
|
#include <signal.h>
|
1996-11-08 07:02:30 +01:00
|
|
|
|
1996-11-03 06:08:01 +01:00
|
|
|
#include "postgres.h"
|
2000-07-17 05:05:41 +02:00
|
|
|
|
1999-07-16 07:00:38 +02:00
|
|
|
#include "access/xact.h"
|
1998-01-25 06:15:15 +01:00
|
|
|
#include "miscadmin.h"
|
1996-07-09 08:22:35 +02:00
|
|
|
#include "storage/proc.h"
|
2000-07-17 05:05:41 +02:00
|
|
|
#include "utils/memutils.h"
|
1998-08-25 23:20:32 +02:00
|
|
|
#include "utils/ps_status.h"
|
1996-07-09 08:22:35 +02:00
|
|
|
|
2000-12-22 01:51:54 +01:00
|
|
|
static int WaitOnLock(LOCKMETHOD lockmethod, LOCKMODE lockmode,
|
|
|
|
LOCK *lock, HOLDER *holder);
|
|
|
|
static void LockCountMyLocks(SHMEM_OFFSET lockOffset, PROC *proc,
|
|
|
|
int *myHolders);
|
|
|
|
static int LockGetMyHoldLocks(SHMEM_OFFSET lockOffset, PROC *proc);
|
1996-07-09 08:22:35 +02:00
|
|
|
|
2000-05-31 02:28:42 +02:00
|
|
|
static char *lock_types[] =
|
|
|
|
{
|
1999-05-07 03:23:11 +02:00
|
|
|
"INVALID",
|
|
|
|
"AccessShareLock",
|
|
|
|
"RowShareLock",
|
|
|
|
"RowExclusiveLock",
|
|
|
|
"ShareLock",
|
|
|
|
"ShareRowExclusiveLock",
|
|
|
|
"ExclusiveLock",
|
|
|
|
"AccessExclusiveLock"
|
1997-03-15 02:23:58 +01:00
|
|
|
};
|
1997-02-12 06:25:13 +01:00
|
|
|
|
2000-11-29 00:27:57 +01:00
|
|
|
static char *DeadLockMessage = "Deadlock detected.\n\tSee the lock(l) manual page for a possible cause.";
|
2000-05-31 02:28:42 +02:00
|
|
|
|
|
|
|
|
|
|
|
#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/ipc/spin.c:
|
|
|
|
* trace_spinlocks -- trace spinlocks (pretty useless)
|
|
|
|
*
|
|
|
|
* Define LOCK_DEBUG at compile time to get all this 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 bool
|
|
|
|
LOCK_DEBUG_ENABLED(const LOCK * lock)
|
|
|
|
{
|
|
|
|
return
|
|
|
|
(((LOCK_LOCKMETHOD(*lock) == DEFAULT_LOCKMETHOD && Trace_locks)
|
|
|
|
|| (LOCK_LOCKMETHOD(*lock) == USER_LOCKMETHOD && Trace_userlocks))
|
|
|
|
&& (lock->tag.relId >= Trace_lock_oidmin))
|
|
|
|
|| (Trace_lock_table && (lock->tag.relId == Trace_lock_table));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
inline static void
|
|
|
|
LOCK_PRINT(const char * where, const LOCK * lock, LOCKMODE type)
|
|
|
|
{
|
|
|
|
if (LOCK_DEBUG_ENABLED(lock))
|
|
|
|
elog(DEBUG,
|
|
|
|
"%s: lock(%lx) 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]);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
inline static void
|
2000-12-22 01:51:54 +01:00
|
|
|
HOLDER_PRINT(const char * where, const HOLDER * holderP)
|
2000-05-31 02:28:42 +02:00
|
|
|
{
|
|
|
|
if (
|
2000-12-22 01:51:54 +01:00
|
|
|
(((HOLDER_LOCKMETHOD(*holderP) == DEFAULT_LOCKMETHOD && Trace_locks)
|
|
|
|
|| (HOLDER_LOCKMETHOD(*holderP) == USER_LOCKMETHOD && Trace_userlocks))
|
|
|
|
&& (((LOCK *)MAKE_PTR(holderP->tag.lock))->tag.relId >= Trace_lock_oidmin))
|
|
|
|
|| (Trace_lock_table && (((LOCK *)MAKE_PTR(holderP->tag.lock))->tag.relId == Trace_lock_table))
|
2000-05-31 02:28:42 +02:00
|
|
|
)
|
|
|
|
elog(DEBUG,
|
2000-12-22 01:51:54 +01:00
|
|
|
"%s: holder(%lx) lock(%lx) tbl(%d) pid(%d) xid(%u) hold(%d,%d,%d,%d,%d,%d,%d)=%d",
|
|
|
|
where, MAKE_OFFSET(holderP), holderP->tag.lock,
|
|
|
|
HOLDER_LOCKMETHOD(*(holderP)),
|
|
|
|
holderP->tag.pid, holderP->tag.xid,
|
|
|
|
holderP->holders[1], holderP->holders[2], holderP->holders[3], holderP->holders[4],
|
|
|
|
holderP->holders[5], holderP->holders[6], holderP->holders[7], holderP->nHolding);
|
2000-05-31 02:28:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#else /* not LOCK_DEBUG */
|
|
|
|
|
|
|
|
#define LOCK_PRINT(where, lock, type)
|
2000-12-22 01:51:54 +01:00
|
|
|
#define HOLDER_PRINT(where, holderP)
|
2000-05-31 02:28:42 +02:00
|
|
|
|
|
|
|
#endif /* not LOCK_DEBUG */
|
|
|
|
|
|
|
|
|
|
|
|
|
1997-09-08 04:41:22 +02:00
|
|
|
SPINLOCK LockMgrLock; /* in Shmem or created in
|
1997-09-07 07:04:48 +02:00
|
|
|
* CreateSpinlocks() */
|
1996-07-09 08:22:35 +02:00
|
|
|
|
|
|
|
/* This is to simplify/speed up some bit arithmetic */
|
|
|
|
|
1999-07-15 22:32:30 +02:00
|
|
|
static LOCKMASK BITS_OFF[MAX_LOCKMODES];
|
|
|
|
static LOCKMASK BITS_ON[MAX_LOCKMODES];
|
1996-07-09 08:22:35 +02:00
|
|
|
|
|
|
|
/* -----------------
|
2000-12-22 01:51:54 +01:00
|
|
|
* Disable flag
|
1996-07-09 08:22:35 +02:00
|
|
|
* -----------------
|
|
|
|
*/
|
1997-09-08 04:41:22 +02:00
|
|
|
static bool LockingIsDisabled;
|
1996-07-09 08:22:35 +02:00
|
|
|
|
|
|
|
/* -------------------
|
1998-06-30 04:33:34 +02:00
|
|
|
* map from lockmethod to the lock table structure
|
1996-07-09 08:22:35 +02:00
|
|
|
* -------------------
|
|
|
|
*/
|
1998-06-30 04:33:34 +02:00
|
|
|
static LOCKMETHODTABLE *LockMethodTable[MAX_LOCK_METHODS];
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1998-06-30 04:33:34 +02:00
|
|
|
static int NumLockMethods;
|
1996-07-09 08:22:35 +02:00
|
|
|
|
|
|
|
/* -------------------
|
|
|
|
* InitLocks -- Init the lock module. Create a private data
|
1997-09-07 07:04:48 +02:00
|
|
|
* structure for constructing conflict masks.
|
1996-07-09 08:22:35 +02:00
|
|
|
* -------------------
|
|
|
|
*/
|
|
|
|
void
|
2000-12-22 01:51:54 +01:00
|
|
|
InitLocks(void)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
int i;
|
|
|
|
int bit;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
bit = 1;
|
1998-06-30 04:33:34 +02:00
|
|
|
for (i = 0; i < MAX_LOCKMODES; i++, bit <<= 1)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-07 07:04:48 +02:00
|
|
|
BITS_ON[i] = bit;
|
|
|
|
BITS_OFF[i] = ~bit;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* -------------------
|
|
|
|
* LockDisable -- sets LockingIsDisabled flag to TRUE or FALSE.
|
|
|
|
* ------------------
|
|
|
|
*/
|
|
|
|
void
|
2000-12-22 01:51:54 +01:00
|
|
|
LockDisable(bool status)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-07 07:04:48 +02:00
|
|
|
LockingIsDisabled = status;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
2000-12-22 01:51:54 +01:00
|
|
|
/* -----------------
|
|
|
|
* Boolean function to determine current locking status
|
|
|
|
* -----------------
|
|
|
|
*/
|
|
|
|
bool
|
|
|
|
LockingDisabled(void)
|
|
|
|
{
|
|
|
|
return LockingIsDisabled;
|
|
|
|
}
|
|
|
|
|
1996-07-09 08:22:35 +02:00
|
|
|
|
|
|
|
/*
|
1998-06-30 04:33:34 +02:00
|
|
|
* LockMethodInit -- initialize the lock table's lock type
|
1997-09-07 07:04:48 +02:00
|
|
|
* structures
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
* Notes: just copying. Should only be called once.
|
|
|
|
*/
|
|
|
|
static void
|
1999-05-26 00:43:53 +02:00
|
|
|
LockMethodInit(LOCKMETHODTABLE *lockMethodTable,
|
1999-07-15 22:32:30 +02:00
|
|
|
LOCKMASK *conflictsP,
|
1998-09-01 06:40:42 +02:00
|
|
|
int *prioP,
|
|
|
|
int numModes)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
int i;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1998-06-30 04:33:34 +02:00
|
|
|
lockMethodTable->ctl->numLockModes = numModes;
|
|
|
|
numModes++;
|
|
|
|
for (i = 0; i < numModes; i++, prioP++, conflictsP++)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1998-06-30 04:33:34 +02:00
|
|
|
lockMethodTable->ctl->conflictTab[i] = *conflictsP;
|
|
|
|
lockMethodTable->ctl->prio[i] = *prioP;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
1998-06-30 04:33:34 +02:00
|
|
|
* LockMethodTableInit -- initialize a lock table structure
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
* Notes:
|
1998-06-27 17:47:48 +02:00
|
|
|
* (a) a lock table has four separate entries in the shmem index
|
1997-09-07 07:04:48 +02:00
|
|
|
* table. This is because every shared hash table and spinlock
|
1998-06-27 17:47:48 +02:00
|
|
|
* has its name stored in the shmem index at its creation. It
|
1997-09-07 07:04:48 +02:00
|
|
|
* is wasteful, in this case, but not much space is involved.
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
2000-06-28 05:33:33 +02:00
|
|
|
* 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.
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
1998-06-30 04:33:34 +02:00
|
|
|
LOCKMETHOD
|
|
|
|
LockMethodTableInit(char *tabName,
|
1999-07-15 22:32:30 +02:00
|
|
|
LOCKMASK *conflictsP,
|
1998-09-01 06:40:42 +02:00
|
|
|
int *prioP,
|
2000-12-22 01:51:54 +01:00
|
|
|
int numModes,
|
|
|
|
int maxBackends)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1998-09-01 06:40:42 +02:00
|
|
|
LOCKMETHODTABLE *lockMethodTable;
|
1997-09-08 04:41:22 +02:00
|
|
|
char *shmemName;
|
|
|
|
HASHCTL info;
|
|
|
|
int hash_flags;
|
|
|
|
bool found;
|
2000-12-22 01:51:54 +01:00
|
|
|
long init_table_size,
|
|
|
|
max_table_size;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1998-06-30 04:33:34 +02:00
|
|
|
if (numModes > MAX_LOCKMODES)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1998-06-30 04:33:34 +02:00
|
|
|
elog(NOTICE, "LockMethodTableInit: too many lock types %d greater than %d",
|
|
|
|
numModes, MAX_LOCKMODES);
|
1998-09-01 05:29:17 +02:00
|
|
|
return INVALID_LOCKMETHOD;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2000-12-22 01:51:54 +01:00
|
|
|
/* Compute init/max size to request for lock hashtables */
|
|
|
|
max_table_size = NLOCKENTS(maxBackends);
|
|
|
|
init_table_size = max_table_size / 10;
|
|
|
|
|
2000-06-28 05:33:33 +02:00
|
|
|
/* 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);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2000-06-28 05:33:33 +02:00
|
|
|
/* each lock table has a non-shared, permanent header */
|
|
|
|
lockMethodTable = (LOCKMETHODTABLE *)
|
|
|
|
MemoryContextAlloc(TopMemoryContext, sizeof(LOCKMETHODTABLE));
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/* ------------------------
|
|
|
|
* 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);
|
1998-06-30 04:33:34 +02:00
|
|
|
lockMethodTable->ctl = (LOCKMETHODCTL *)
|
2000-06-28 05:33:33 +02:00
|
|
|
ShmemInitStruct(shmemName, sizeof(LOCKMETHODCTL), &found);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1998-06-30 04:33:34 +02:00
|
|
|
if (!lockMethodTable->ctl)
|
|
|
|
elog(FATAL, "LockMethodTableInit: couldn't initialize %s", tabName);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1998-06-30 04:33:34 +02:00
|
|
|
/* -------------------
|
|
|
|
* no zero-th table
|
|
|
|
* -------------------
|
|
|
|
*/
|
|
|
|
NumLockMethods = 1;
|
1998-06-27 06:53:49 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
/* ----------------
|
|
|
|
* we're first - initialize
|
|
|
|
* ----------------
|
|
|
|
*/
|
|
|
|
if (!found)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1998-06-30 04:33:34 +02:00
|
|
|
MemSet(lockMethodTable->ctl, 0, sizeof(LOCKMETHODCTL));
|
|
|
|
lockMethodTable->ctl->masterLock = LockMgrLock;
|
|
|
|
lockMethodTable->ctl->lockmethod = NumLockMethods;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/* --------------------
|
2000-12-22 01:51:54 +01:00
|
|
|
* other modules refer to the lock table by a lockmethod ID
|
1997-09-07 07:04:48 +02:00
|
|
|
* --------------------
|
|
|
|
*/
|
1998-06-30 04:33:34 +02:00
|
|
|
LockMethodTable[NumLockMethods] = lockMethodTable;
|
|
|
|
NumLockMethods++;
|
|
|
|
Assert(NumLockMethods <= MAX_LOCK_METHODS);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/* ----------------------
|
2000-12-22 01:51:54 +01:00
|
|
|
* allocate a hash table for LOCK structs. This is used
|
|
|
|
* to store per-locked-object information.
|
1997-09-07 07:04:48 +02:00
|
|
|
* ----------------------
|
|
|
|
*/
|
1999-03-06 22:17:56 +01:00
|
|
|
info.keysize = SHMEM_LOCKTAB_KEYSIZE;
|
|
|
|
info.datasize = SHMEM_LOCKTAB_DATASIZE;
|
1997-09-07 07:04:48 +02:00
|
|
|
info.hash = tag_hash;
|
|
|
|
hash_flags = (HASH_ELEM | HASH_FUNCTION);
|
|
|
|
|
|
|
|
sprintf(shmemName, "%s (lock hash)", tabName);
|
2000-12-22 01:51:54 +01:00
|
|
|
lockMethodTable->lockHash = ShmemInitHash(shmemName,
|
|
|
|
init_table_size,
|
|
|
|
max_table_size,
|
|
|
|
&info,
|
|
|
|
hash_flags);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1998-06-30 04:33:34 +02:00
|
|
|
if (!lockMethodTable->lockHash)
|
|
|
|
elog(FATAL, "LockMethodTableInit: couldn't initialize %s", tabName);
|
2000-12-22 01:51:54 +01:00
|
|
|
Assert(lockMethodTable->lockHash->hash == tag_hash);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/* -------------------------
|
2000-12-22 01:51:54 +01:00
|
|
|
* allocate a hash table for HOLDER structs. This is used
|
|
|
|
* to store per-lock-holder information.
|
1997-09-07 07:04:48 +02:00
|
|
|
* -------------------------
|
|
|
|
*/
|
2000-12-22 01:51:54 +01:00
|
|
|
info.keysize = SHMEM_HOLDERTAB_KEYSIZE;
|
|
|
|
info.datasize = SHMEM_HOLDERTAB_DATASIZE;
|
1997-09-07 07:04:48 +02:00
|
|
|
info.hash = tag_hash;
|
|
|
|
hash_flags = (HASH_ELEM | HASH_FUNCTION);
|
|
|
|
|
2000-12-22 01:51:54 +01:00
|
|
|
sprintf(shmemName, "%s (holder hash)", tabName);
|
|
|
|
lockMethodTable->holderHash = ShmemInitHash(shmemName,
|
|
|
|
init_table_size,
|
|
|
|
max_table_size,
|
|
|
|
&info,
|
|
|
|
hash_flags);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2000-12-22 01:51:54 +01:00
|
|
|
if (!lockMethodTable->holderHash)
|
1998-06-30 04:33:34 +02:00
|
|
|
elog(FATAL, "LockMethodTableInit: couldn't initialize %s", tabName);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/* init ctl data structures */
|
1998-06-30 04:33:34 +02:00
|
|
|
LockMethodInit(lockMethodTable, conflictsP, prioP, numModes);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
SpinRelease(LockMgrLock);
|
|
|
|
|
|
|
|
pfree(shmemName);
|
|
|
|
|
2000-12-22 01:51:54 +01:00
|
|
|
return lockMethodTable->ctl->lockmethod;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2000-12-22 01:51:54 +01:00
|
|
|
* LockMethodTableRename -- allocate another lockmethod ID to the same
|
1997-09-07 07:04:48 +02:00
|
|
|
* lock table.
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
* NOTES: Both the lock module and the lock chain (lchain.c)
|
1997-09-07 07:04:48 +02:00
|
|
|
* 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
|
1998-06-30 04:33:34 +02:00
|
|
|
* client to use different lockmethods when acquiring/releasing
|
2000-12-22 01:51:54 +01:00
|
|
|
* short term and long term locks, yet store them all in one hashtable.
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
1998-08-25 23:20:32 +02:00
|
|
|
|
1998-06-30 04:33:34 +02:00
|
|
|
LOCKMETHOD
|
|
|
|
LockMethodTableRename(LOCKMETHOD lockmethod)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1998-09-01 06:40:42 +02:00
|
|
|
LOCKMETHOD newLockMethod;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1998-06-30 04:33:34 +02:00
|
|
|
if (NumLockMethods >= MAX_LOCK_METHODS)
|
1998-09-01 05:29:17 +02:00
|
|
|
return INVALID_LOCKMETHOD;
|
1998-08-25 23:20:32 +02:00
|
|
|
if (LockMethodTable[lockmethod] == INVALID_LOCKMETHOD)
|
1998-09-01 05:29:17 +02:00
|
|
|
return INVALID_LOCKMETHOD;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2000-12-22 01:51:54 +01:00
|
|
|
/* other modules refer to the lock table by a lockmethod ID */
|
1998-06-30 04:33:34 +02:00
|
|
|
newLockMethod = NumLockMethods;
|
|
|
|
NumLockMethods++;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1998-06-30 04:33:34 +02:00
|
|
|
LockMethodTable[newLockMethod] = LockMethodTable[lockmethod];
|
1998-09-01 05:29:17 +02:00
|
|
|
return newLockMethod;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* LockAcquire -- Check for lock conflicts, sleep if conflict found,
|
1997-09-07 07:04:48 +02:00
|
|
|
* set lock if/when no conflicts.
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
* Returns: TRUE if parameters are correct, FALSE otherwise.
|
|
|
|
*
|
|
|
|
* Side Effects: The lock is always acquired. No way to abort
|
1997-09-07 07:04:48 +02:00
|
|
|
* a lock acquisition other than aborting the transaction.
|
|
|
|
* Lock is recorded in the lkchain.
|
1998-08-25 23:20:32 +02:00
|
|
|
*
|
|
|
|
*
|
1997-09-07 07:04:48 +02:00
|
|
|
* Note on User Locks:
|
1998-08-25 23:20:32 +02:00
|
|
|
*
|
1997-09-07 07:04:48 +02:00
|
|
|
* 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.
|
1998-08-25 23:20:32 +02:00
|
|
|
* They are indicated by a lockmethod 2 which is an alias for the
|
|
|
|
* normal lock table, and are distinguished from normal locks
|
2000-12-22 01:51:54 +01:00
|
|
|
* by the following differences:
|
1996-10-11 05:22:59 +02:00
|
|
|
*
|
1997-09-07 07:04:48 +02:00
|
|
|
* normal lock user lock
|
1996-10-11 05:22:59 +02:00
|
|
|
*
|
1998-08-25 23:20:32 +02:00
|
|
|
* lockmethod 1 2
|
2000-12-22 01:51:54 +01:00
|
|
|
* 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
|
|
|
|
* xid.pid backend pid backend pid
|
1998-08-25 23:20:32 +02:00
|
|
|
* xid.xid xid or 0 0
|
1997-09-07 07:04:48 +02:00
|
|
|
* persistence transaction user or backend
|
2000-12-22 01:51:54 +01:00
|
|
|
* or backend
|
1996-10-11 05:22:59 +02:00
|
|
|
*
|
1998-06-30 04:33:34 +02:00
|
|
|
* The lockmode parameter can have the same values for normal locks
|
1997-09-07 07:04:48 +02:00
|
|
|
* although probably only WRITE_LOCK can have some practical use.
|
1996-10-11 05:22:59 +02:00
|
|
|
*
|
1998-08-25 23:20:32 +02:00
|
|
|
* DZ - 22 Nov 1997
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
1997-09-18 16:21:02 +02:00
|
|
|
|
1996-07-09 08:22:35 +02:00
|
|
|
bool
|
2000-12-22 01:51:54 +01:00
|
|
|
LockAcquire(LOCKMETHOD lockmethod, LOCKTAG *locktag,
|
|
|
|
TransactionId xid, LOCKMODE lockmode)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
2000-12-22 01:51:54 +01:00
|
|
|
HOLDER *holder;
|
|
|
|
HOLDERTAG holdertag;
|
|
|
|
HTAB *holderTable;
|
1997-09-08 04:41:22 +02:00
|
|
|
bool found;
|
2000-12-22 01:51:54 +01:00
|
|
|
LOCK *lock;
|
1997-09-08 04:41:22 +02:00
|
|
|
SPINLOCK masterLock;
|
1998-09-01 06:40:42 +02:00
|
|
|
LOCKMETHODTABLE *lockMethodTable;
|
1997-09-08 04:41:22 +02:00
|
|
|
int status;
|
2000-12-22 01:51:54 +01:00
|
|
|
int myHolders[MAX_LOCKMODES];
|
|
|
|
int i;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2000-05-31 02:28:42 +02:00
|
|
|
#ifdef LOCK_DEBUG
|
|
|
|
if (lockmethod == USER_LOCKMETHOD && Trace_userlocks)
|
2000-12-22 01:51:54 +01:00
|
|
|
elog(DEBUG, "LockAcquire: user lock [%u] %s",
|
|
|
|
locktag->objId.blkno, lock_types[lockmode]);
|
1996-10-11 05:22:59 +02:00
|
|
|
#endif
|
|
|
|
|
1998-08-25 23:20:32 +02:00
|
|
|
/* ???????? This must be changed when short term locks will be used */
|
|
|
|
locktag->lockmethod = lockmethod;
|
|
|
|
|
1998-06-30 04:33:34 +02:00
|
|
|
Assert(lockmethod < NumLockMethods);
|
|
|
|
lockMethodTable = LockMethodTable[lockmethod];
|
|
|
|
if (!lockMethodTable)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1998-06-30 04:33:34 +02:00
|
|
|
elog(NOTICE, "LockAcquire: bad lock table %d", lockmethod);
|
1998-09-01 05:29:17 +02:00
|
|
|
return FALSE;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
if (LockingIsDisabled)
|
1998-09-01 05:29:17 +02:00
|
|
|
return TRUE;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1998-06-30 04:33:34 +02:00
|
|
|
masterLock = lockMethodTable->ctl->masterLock;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
SpinAcquire(masterLock);
|
|
|
|
|
1998-08-25 23:20:32 +02:00
|
|
|
/*
|
|
|
|
* Find or create a lock with this tag
|
|
|
|
*/
|
1998-06-30 04:33:34 +02:00
|
|
|
Assert(lockMethodTable->lockHash->hash == tag_hash);
|
1998-08-25 23:20:32 +02:00
|
|
|
lock = (LOCK *) hash_search(lockMethodTable->lockHash, (Pointer) locktag,
|
|
|
|
HASH_ENTER, &found);
|
1997-09-07 07:04:48 +02:00
|
|
|
if (!lock)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-07 07:04:48 +02:00
|
|
|
SpinRelease(masterLock);
|
1998-06-30 04:33:34 +02:00
|
|
|
elog(FATAL, "LockAcquire: lock table %d is corrupted", lockmethod);
|
1998-09-01 05:29:17 +02:00
|
|
|
return FALSE;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/* --------------------
|
2000-12-22 01:51:54 +01:00
|
|
|
* if it's a new lock object, initialize it
|
1997-09-07 07:04:48 +02:00
|
|
|
* --------------------
|
|
|
|
*/
|
|
|
|
if (!found)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-07 07:04:48 +02:00
|
|
|
lock->mask = 0;
|
|
|
|
lock->nHolding = 0;
|
|
|
|
lock->nActive = 0;
|
1998-08-25 23:20:32 +02:00
|
|
|
MemSet((char *) lock->holders, 0, sizeof(int) * MAX_LOCKMODES);
|
|
|
|
MemSet((char *) lock->activeHolders, 0, sizeof(int) * MAX_LOCKMODES);
|
|
|
|
ProcQueueInit(&(lock->waitProcs));
|
|
|
|
LOCK_PRINT("LockAcquire: new", lock, lockmode);
|
1998-09-01 06:40:42 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
1998-08-25 23:20:32 +02:00
|
|
|
LOCK_PRINT("LockAcquire: found", lock, lockmode);
|
|
|
|
Assert((lock->nHolding > 0) && (lock->holders[lockmode] >= 0));
|
|
|
|
Assert((lock->nActive > 0) && (lock->activeHolders[lockmode] >= 0));
|
|
|
|
Assert(lock->nActive <= lock->nHolding);
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/* ------------------
|
2000-12-22 01:51:54 +01:00
|
|
|
* Create the hash key for the holder table.
|
1997-09-07 07:04:48 +02:00
|
|
|
* ------------------
|
|
|
|
*/
|
2000-12-22 01:51:54 +01:00
|
|
|
MemSet(&holdertag, 0, sizeof(HOLDERTAG)); /* must clear padding, needed */
|
|
|
|
holdertag.lock = MAKE_OFFSET(lock);
|
|
|
|
holdertag.pid = MyProcPid;
|
|
|
|
TransactionIdStore(xid, &holdertag.xid);
|
1996-10-11 05:22:59 +02:00
|
|
|
|
1998-08-25 23:20:32 +02:00
|
|
|
/*
|
2000-12-22 01:51:54 +01:00
|
|
|
* Find or create a holder entry with this tag
|
1998-08-25 23:20:32 +02:00
|
|
|
*/
|
2000-12-22 01:51:54 +01:00
|
|
|
holderTable = lockMethodTable->holderHash;
|
|
|
|
holder = (HOLDER *) hash_search(holderTable, (Pointer) &holdertag,
|
|
|
|
HASH_ENTER, &found);
|
|
|
|
if (!holder)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1999-11-28 02:56:48 +01:00
|
|
|
SpinRelease(masterLock);
|
2000-12-22 01:51:54 +01:00
|
|
|
elog(NOTICE, "LockAcquire: holder table corrupted");
|
1999-09-18 21:08:25 +02:00
|
|
|
return FALSE;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
1998-08-25 23:20:32 +02:00
|
|
|
|
|
|
|
/*
|
2000-12-22 01:51:54 +01:00
|
|
|
* If new, initialize the new entry
|
1998-08-25 23:20:32 +02:00
|
|
|
*/
|
1997-09-07 07:04:48 +02:00
|
|
|
if (!found)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
2000-12-22 01:51:54 +01:00
|
|
|
holder->nHolding = 0;
|
|
|
|
MemSet((char *) holder->holders, 0, sizeof(int) * MAX_LOCKMODES);
|
|
|
|
ProcAddLock(&holder->queue);
|
|
|
|
HOLDER_PRINT("LockAcquire: new", holder);
|
1998-09-01 06:40:42 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2000-12-22 01:51:54 +01:00
|
|
|
HOLDER_PRINT("LockAcquire: found", holder);
|
|
|
|
Assert((holder->nHolding > 0) && (holder->holders[lockmode] >= 0));
|
|
|
|
Assert(holder->nHolding <= lock->nActive);
|
2000-11-08 23:10:03 +01:00
|
|
|
|
2000-12-22 01:51:54 +01:00
|
|
|
#ifdef CHECK_DEADLOCK_RISK
|
2000-11-08 23:10:03 +01:00
|
|
|
/*
|
|
|
|
* Issue warning if we already hold a lower-level lock on this
|
|
|
|
* object and do not hold a lock of the requested level or higher.
|
|
|
|
* This indicates a deadlock-prone coding practice (eg, we'd have
|
|
|
|
* a deadlock if another backend were following the same code path
|
|
|
|
* at about the same time).
|
|
|
|
*
|
2000-12-22 01:51:54 +01:00
|
|
|
* This is not enabled by default, because it may generate log entries
|
|
|
|
* about user-level coding practices that are in fact safe in context.
|
|
|
|
* It can be enabled to help find system-level problems.
|
|
|
|
*
|
2000-11-08 23:10:03 +01:00
|
|
|
* XXX Doing numeric comparison on the lockmodes is a hack;
|
|
|
|
* it'd be better to use a table. For now, though, this works.
|
|
|
|
*/
|
|
|
|
for (i = lockMethodTable->ctl->numLockModes; i > 0; i--)
|
|
|
|
{
|
2000-12-22 01:51:54 +01:00
|
|
|
if (holder->holders[i] > 0)
|
2000-11-08 23:10:03 +01:00
|
|
|
{
|
|
|
|
if (i >= (int) lockmode)
|
|
|
|
break; /* safe: we have a lock >= req level */
|
|
|
|
elog(DEBUG, "Deadlock risk: raising lock level"
|
|
|
|
" from %s to %s on object %u/%u/%u",
|
|
|
|
lock_types[i], lock_types[lockmode],
|
|
|
|
lock->tag.relId, lock->tag.dbId, lock->tag.objId.blkno);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2000-12-22 01:51:54 +01:00
|
|
|
#endif /* CHECK_DEADLOCK_RISK */
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/* ----------------
|
2000-12-22 01:51:54 +01:00
|
|
|
* lock->nHolding and lock->holders count the total number of holders
|
|
|
|
* either holding or waiting for the lock, so increment those immediately.
|
|
|
|
* The other counts don't increment till we get the lock.
|
1997-09-07 07:04:48 +02:00
|
|
|
* ----------------
|
|
|
|
*/
|
|
|
|
lock->nHolding++;
|
1998-06-30 04:33:34 +02:00
|
|
|
lock->holders[lockmode]++;
|
1998-08-25 23:20:32 +02:00
|
|
|
Assert((lock->nHolding > 0) && (lock->holders[lockmode] > 0));
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/* --------------------
|
2000-12-22 01:51:54 +01:00
|
|
|
* If I'm the only one holding any lock on this object, then there
|
|
|
|
* cannot be a conflict. The same is true if I already hold this lock.
|
|
|
|
* --------------------
|
|
|
|
*/
|
|
|
|
if (holder->nHolding == lock->nActive || holder->holders[lockmode] != 0)
|
|
|
|
{
|
|
|
|
GrantLock(lock, holder, lockmode);
|
|
|
|
HOLDER_PRINT("LockAcquire: owning", holder);
|
|
|
|
SpinRelease(masterLock);
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* --------------------
|
|
|
|
* If this process (under any XID) is a holder of the lock,
|
|
|
|
* then there is no conflict, either.
|
1997-09-07 07:04:48 +02:00
|
|
|
* --------------------
|
|
|
|
*/
|
2000-12-22 01:51:54 +01:00
|
|
|
LockCountMyLocks(holder->tag.lock, MyProc, myHolders);
|
|
|
|
if (myHolders[lockmode] != 0)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
2000-12-22 01:51:54 +01:00
|
|
|
GrantLock(lock, holder, lockmode);
|
|
|
|
HOLDER_PRINT("LockAcquire: my other XID owning", holder);
|
1997-09-07 07:04:48 +02:00
|
|
|
SpinRelease(masterLock);
|
1998-09-01 05:29:17 +02:00
|
|
|
return TRUE;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1999-05-07 03:23:11 +02:00
|
|
|
/*
|
|
|
|
* If lock requested conflicts with locks requested by waiters...
|
|
|
|
*/
|
|
|
|
if (lockMethodTable->ctl->conflictTab[lockmode] & lock->waitMask)
|
|
|
|
{
|
|
|
|
/*
|
2000-12-22 01:51:54 +01:00
|
|
|
* If my process doesn't hold any locks that conflict with waiters
|
|
|
|
* then force to sleep, so that prior waiters get first chance.
|
1999-05-07 03:23:11 +02:00
|
|
|
*/
|
2000-12-22 01:51:54 +01:00
|
|
|
for (i = 1; i <= lockMethodTable->ctl->numLockModes; i++)
|
1999-05-07 03:23:11 +02:00
|
|
|
{
|
2000-12-22 01:51:54 +01:00
|
|
|
if (myHolders[i] > 0 &&
|
|
|
|
lockMethodTable->ctl->conflictTab[i] & lock->waitMask)
|
|
|
|
break; /* yes, there is a conflict */
|
1999-05-07 03:23:11 +02:00
|
|
|
}
|
1999-05-25 18:15:34 +02:00
|
|
|
|
2000-12-22 01:51:54 +01:00
|
|
|
if (i > lockMethodTable->ctl->numLockModes)
|
1999-05-07 03:23:11 +02:00
|
|
|
{
|
2000-12-22 01:51:54 +01:00
|
|
|
HOLDER_PRINT("LockAcquire: another proc already waiting",
|
|
|
|
holder);
|
1999-05-07 03:23:11 +02:00
|
|
|
status = STATUS_FOUND;
|
|
|
|
}
|
|
|
|
else
|
2000-12-22 01:51:54 +01:00
|
|
|
status = LockResolveConflicts(lockmethod, lockmode,
|
|
|
|
lock, holder,
|
|
|
|
MyProc, myHolders);
|
1999-05-07 03:23:11 +02:00
|
|
|
}
|
|
|
|
else
|
2000-12-22 01:51:54 +01:00
|
|
|
status = LockResolveConflicts(lockmethod, lockmode,
|
|
|
|
lock, holder,
|
|
|
|
MyProc, myHolders);
|
1999-05-07 03:23:11 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
if (status == STATUS_OK)
|
2000-12-22 01:51:54 +01:00
|
|
|
GrantLock(lock, holder, lockmode);
|
1997-09-07 07:04:48 +02:00
|
|
|
else if (status == STATUS_FOUND)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1996-10-11 05:22:59 +02:00
|
|
|
#ifdef USER_LOCKS
|
1998-09-01 06:40:42 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
/*
|
1998-08-25 23:20:32 +02:00
|
|
|
* User locks are non blocking. If we can't acquire a lock we must
|
2000-12-22 01:51:54 +01:00
|
|
|
* remove the holder entry and return FALSE without waiting.
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
2000-05-31 02:28:42 +02:00
|
|
|
if (lockmethod == USER_LOCKMETHOD)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2000-12-22 01:51:54 +01:00
|
|
|
if (holder->nHolding == 0)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2000-12-22 01:51:54 +01:00
|
|
|
SHMQueueDelete(&holder->queue);
|
|
|
|
holder = (HOLDER *) hash_search(holderTable,
|
|
|
|
(Pointer) holder,
|
|
|
|
HASH_REMOVE, &found);
|
|
|
|
if (!holder || !found)
|
|
|
|
elog(NOTICE, "LockAcquire: remove holder, table corrupted");
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
1998-09-01 06:40:42 +02:00
|
|
|
else
|
2000-12-22 01:51:54 +01:00
|
|
|
HOLDER_PRINT("LockAcquire: NHOLDING", holder);
|
1997-09-07 07:04:48 +02:00
|
|
|
lock->nHolding--;
|
1998-06-30 04:33:34 +02:00
|
|
|
lock->holders[lockmode]--;
|
1998-08-25 23:20:32 +02:00
|
|
|
LOCK_PRINT("LockAcquire: user lock failed", lock, lockmode);
|
|
|
|
Assert((lock->nHolding > 0) && (lock->holders[lockmode] >= 0));
|
|
|
|
Assert(lock->nActive <= lock->nHolding);
|
1997-09-07 07:04:48 +02:00
|
|
|
SpinRelease(masterLock);
|
1998-09-01 05:29:17 +02:00
|
|
|
return FALSE;
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
2000-05-31 02:28:42 +02:00
|
|
|
#endif /* USER_LOCKS */
|
1999-05-25 18:15:34 +02:00
|
|
|
|
1999-05-07 03:23:11 +02:00
|
|
|
/*
|
2000-12-22 01:51:54 +01:00
|
|
|
* Construct bitmask of locks this process holds on this object.
|
1999-05-07 03:23:11 +02:00
|
|
|
*/
|
|
|
|
{
|
2000-12-22 01:51:54 +01:00
|
|
|
int holdLock = 0;
|
|
|
|
int tmpMask;
|
1999-05-07 03:23:11 +02:00
|
|
|
|
2000-12-22 01:51:54 +01:00
|
|
|
for (i = 1, tmpMask = 2;
|
|
|
|
i <= lockMethodTable->ctl->numLockModes;
|
1999-05-25 18:15:34 +02:00
|
|
|
i++, tmpMask <<= 1)
|
1999-05-07 03:23:11 +02:00
|
|
|
{
|
2000-12-22 01:51:54 +01:00
|
|
|
if (myHolders[i] > 0)
|
|
|
|
holdLock |= tmpMask;
|
1999-05-07 03:23:11 +02:00
|
|
|
}
|
2000-12-22 01:51:54 +01:00
|
|
|
MyProc->holdLock = holdLock;
|
1999-05-07 03:23:11 +02:00
|
|
|
}
|
|
|
|
|
2000-12-22 01:51:54 +01:00
|
|
|
/*
|
|
|
|
* Sleep till someone wakes me up.
|
|
|
|
*/
|
|
|
|
status = WaitOnLock(lockmethod, lockmode, lock, holder);
|
1998-09-01 06:40:42 +02:00
|
|
|
|
1998-08-25 23:20:32 +02:00
|
|
|
/*
|
2000-12-22 01:51:54 +01:00
|
|
|
* Check the holder entry status, in case something in the ipc
|
1998-09-01 06:40:42 +02:00
|
|
|
* communication doesn't work correctly.
|
1998-08-25 23:20:32 +02:00
|
|
|
*/
|
2000-12-22 01:51:54 +01:00
|
|
|
if (!((holder->nHolding > 0) && (holder->holders[lockmode] > 0)))
|
1998-09-01 06:40:42 +02:00
|
|
|
{
|
2000-12-22 01:51:54 +01:00
|
|
|
HOLDER_PRINT("LockAcquire: INCONSISTENT", holder);
|
2000-05-31 02:28:42 +02:00
|
|
|
LOCK_PRINT("LockAcquire: INCONSISTENT", lock, lockmode);
|
1998-08-25 23:20:32 +02:00
|
|
|
/* Should we retry ? */
|
1999-11-28 02:56:48 +01:00
|
|
|
SpinRelease(masterLock);
|
1998-09-01 05:29:17 +02:00
|
|
|
return FALSE;
|
1998-08-25 23:20:32 +02:00
|
|
|
}
|
2000-12-22 01:51:54 +01:00
|
|
|
HOLDER_PRINT("LockAcquire: granted", holder);
|
1998-08-25 23:20:32 +02:00
|
|
|
LOCK_PRINT("LockAcquire: granted", lock, lockmode);
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
SpinRelease(masterLock);
|
|
|
|
|
1998-09-01 05:29:17 +02:00
|
|
|
return status == STATUS_OK;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* ----------------------------
|
|
|
|
* LockResolveConflicts -- test for lock conflicts
|
|
|
|
*
|
|
|
|
* NOTES:
|
1997-09-07 07:04:48 +02:00
|
|
|
* Here's what makes this complicated: one transaction's
|
1996-07-09 08:22:35 +02:00
|
|
|
* locks don't conflict with one another. When many processes
|
|
|
|
* hold locks, each has to subtract off the other's locks when
|
|
|
|
* determining whether or not any new lock acquired conflicts with
|
|
|
|
* the old ones.
|
|
|
|
*
|
2000-12-22 01:51:54 +01:00
|
|
|
* The caller can optionally pass the process's total holders counts, if
|
|
|
|
* known. If NULL is passed then these values will be computed internally.
|
1996-07-09 08:22:35 +02:00
|
|
|
* ----------------------------
|
|
|
|
*/
|
|
|
|
int
|
1998-06-30 04:33:34 +02:00
|
|
|
LockResolveConflicts(LOCKMETHOD lockmethod,
|
|
|
|
LOCKMODE lockmode,
|
2000-12-22 01:51:54 +01:00
|
|
|
LOCK *lock,
|
|
|
|
HOLDER *holder,
|
|
|
|
PROC *proc,
|
|
|
|
int *myHolders) /* myHolders[] array or NULL */
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
2000-12-22 01:51:54 +01:00
|
|
|
LOCKMETHODCTL *lockctl = LockMethodTable[lockmethod]->ctl;
|
|
|
|
int numLockModes = lockctl->numLockModes;
|
1997-09-08 04:41:22 +02:00
|
|
|
int bitmask;
|
|
|
|
int i,
|
|
|
|
tmpMask;
|
2000-12-22 01:51:54 +01:00
|
|
|
int localHolders[MAX_LOCKMODES];
|
1998-09-01 06:40:42 +02:00
|
|
|
|
2000-12-22 01:51:54 +01:00
|
|
|
Assert((holder->nHolding >= 0) && (holder->holders[lockmode] >= 0));
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/* ----------------------------
|
|
|
|
* first check for global conflicts: If no locks conflict
|
|
|
|
* with mine, then I get the lock.
|
|
|
|
*
|
|
|
|
* Checking for conflict: lock->mask represents the types of
|
1998-06-30 04:33:34 +02:00
|
|
|
* currently held locks. conflictTable[lockmode] has a bit
|
1997-09-07 07:04:48 +02:00
|
|
|
* set for each type of lock that conflicts with mine. Bitwise
|
|
|
|
* compare tells if there is a conflict.
|
|
|
|
* ----------------------------
|
|
|
|
*/
|
2000-12-22 01:51:54 +01:00
|
|
|
if (!(lockctl->conflictTab[lockmode] & lock->mask))
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
2000-12-22 01:51:54 +01:00
|
|
|
HOLDER_PRINT("LockResolveConflicts: no conflict", holder);
|
1998-09-01 05:29:17 +02:00
|
|
|
return STATUS_OK;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/* ------------------------
|
|
|
|
* Rats. Something conflicts. But it could still be my own
|
|
|
|
* lock. We have to construct a conflict mask
|
2000-12-22 01:51:54 +01:00
|
|
|
* that does not reflect our own locks. Locks held by the current
|
|
|
|
* process under another XID also count as "our own locks".
|
1997-09-07 07:04:48 +02:00
|
|
|
* ------------------------
|
|
|
|
*/
|
2000-12-22 01:51:54 +01:00
|
|
|
if (myHolders == NULL)
|
|
|
|
{
|
|
|
|
/* Caller didn't do calculation of total holding for me */
|
|
|
|
LockCountMyLocks(holder->tag.lock, proc, localHolders);
|
|
|
|
myHolders = localHolders;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Compute mask of lock types held by other processes */
|
1997-09-07 07:04:48 +02:00
|
|
|
bitmask = 0;
|
|
|
|
tmpMask = 2;
|
1998-06-30 04:33:34 +02:00
|
|
|
for (i = 1; i <= numLockModes; i++, tmpMask <<= 1)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1998-01-28 03:29:40 +01:00
|
|
|
if (lock->activeHolders[i] != myHolders[i])
|
1997-09-07 07:04:48 +02:00
|
|
|
bitmask |= tmpMask;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/* ------------------------
|
|
|
|
* now check again for conflicts. 'bitmask' describes the types
|
|
|
|
* of locks held by other processes. If one of these
|
|
|
|
* conflicts with the kind of lock that I want, there is a
|
|
|
|
* conflict and I have to sleep.
|
|
|
|
* ------------------------
|
|
|
|
*/
|
2000-12-22 01:51:54 +01:00
|
|
|
if (!(lockctl->conflictTab[lockmode] & bitmask))
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
2000-12-22 01:51:54 +01:00
|
|
|
/* no conflict. OK to get the lock */
|
|
|
|
HOLDER_PRINT("LockResolveConflicts: resolved", holder);
|
1998-09-01 05:29:17 +02:00
|
|
|
return STATUS_OK;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2000-12-22 01:51:54 +01:00
|
|
|
HOLDER_PRINT("LockResolveConflicts: conflicting", holder);
|
1998-09-01 05:29:17 +02:00
|
|
|
return STATUS_FOUND;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
1998-08-25 23:20:32 +02:00
|
|
|
/*
|
2000-12-22 01:51:54 +01:00
|
|
|
* LockCountMyLocks --- Count total number of locks held on a given lockable
|
|
|
|
* object by a given process (under any transaction ID).
|
|
|
|
*
|
|
|
|
* XXX This could be rather slow if the process holds a large number of locks.
|
|
|
|
* Perhaps it could be sped up if we kept yet a third hashtable of per-
|
|
|
|
* process lock information. However, for the normal case where a transaction
|
|
|
|
* doesn't hold a large number of locks, keeping such a table would probably
|
|
|
|
* be a net slowdown.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
LockCountMyLocks(SHMEM_OFFSET lockOffset, PROC *proc, int *myHolders)
|
|
|
|
{
|
|
|
|
HOLDER *holder = NULL;
|
|
|
|
HOLDER *nextHolder = NULL;
|
|
|
|
SHM_QUEUE *lockQueue = &(proc->lockQueue);
|
|
|
|
SHMEM_OFFSET end = MAKE_OFFSET(lockQueue);
|
|
|
|
int i;
|
|
|
|
|
|
|
|
MemSet(myHolders, 0, MAX_LOCKMODES * sizeof(int));
|
|
|
|
|
|
|
|
if (SHMQueueEmpty(lockQueue))
|
|
|
|
return;
|
|
|
|
|
|
|
|
SHMQueueFirst(lockQueue, (Pointer *) &holder, &holder->queue);
|
|
|
|
|
|
|
|
do
|
|
|
|
{
|
|
|
|
/* ---------------------------
|
|
|
|
* XXX Here we assume the shared memory queue is circular and
|
|
|
|
* that we know its internal structure. Should have some sort of
|
|
|
|
* macros to allow one to walk it. mer 20 July 1991
|
|
|
|
* ---------------------------
|
|
|
|
*/
|
|
|
|
if (holder->queue.next == end)
|
|
|
|
nextHolder = NULL;
|
|
|
|
else
|
|
|
|
SHMQueueFirst(&holder->queue,
|
|
|
|
(Pointer *) &nextHolder, &nextHolder->queue);
|
|
|
|
|
|
|
|
if (lockOffset == holder->tag.lock)
|
|
|
|
{
|
|
|
|
for (i = 1; i < MAX_LOCKMODES; i++)
|
|
|
|
{
|
|
|
|
myHolders[i] += holder->holders[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
holder = nextHolder;
|
|
|
|
} while (holder);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* LockGetMyHoldLocks -- compute bitmask of lock types held by a process
|
|
|
|
* for a given lockable object.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
LockGetMyHoldLocks(SHMEM_OFFSET lockOffset, PROC *proc)
|
|
|
|
{
|
|
|
|
int myHolders[MAX_LOCKMODES];
|
|
|
|
int holdLock = 0;
|
|
|
|
int i,
|
|
|
|
tmpMask;
|
|
|
|
|
|
|
|
LockCountMyLocks(lockOffset, proc, myHolders);
|
|
|
|
|
|
|
|
for (i = 1, tmpMask = 2;
|
|
|
|
i < MAX_LOCKMODES;
|
|
|
|
i++, tmpMask <<= 1)
|
|
|
|
{
|
|
|
|
if (myHolders[i] > 0)
|
|
|
|
holdLock |= tmpMask;
|
|
|
|
}
|
|
|
|
return holdLock;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* GrantLock -- update the lock and holder data structures to show
|
|
|
|
* the new lock has been granted.
|
1998-08-25 23:20:32 +02:00
|
|
|
*/
|
|
|
|
void
|
2000-12-22 01:51:54 +01:00
|
|
|
GrantLock(LOCK *lock, HOLDER *holder, LOCKMODE lockmode)
|
1998-08-25 23:20:32 +02:00
|
|
|
{
|
|
|
|
lock->nActive++;
|
|
|
|
lock->activeHolders[lockmode]++;
|
|
|
|
lock->mask |= BITS_ON[lockmode];
|
|
|
|
LOCK_PRINT("GrantLock", lock, lockmode);
|
|
|
|
Assert((lock->nActive > 0) && (lock->activeHolders[lockmode] > 0));
|
|
|
|
Assert(lock->nActive <= lock->nHolding);
|
2000-12-22 01:51:54 +01:00
|
|
|
holder->holders[lockmode]++;
|
|
|
|
holder->nHolding++;
|
|
|
|
Assert((holder->nHolding > 0) && (holder->holders[lockmode] > 0));
|
1998-08-25 23:20:32 +02:00
|
|
|
}
|
|
|
|
|
2000-12-22 01:51:54 +01:00
|
|
|
/*
|
|
|
|
* WaitOnLock -- wait to acquire a lock
|
|
|
|
*
|
|
|
|
* The locktable spinlock must be held at entry.
|
|
|
|
*/
|
1997-08-19 23:40:56 +02:00
|
|
|
static int
|
2000-12-22 01:51:54 +01:00
|
|
|
WaitOnLock(LOCKMETHOD lockmethod, LOCKMODE lockmode,
|
|
|
|
LOCK *lock, HOLDER *holder)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1998-09-01 06:40:42 +02:00
|
|
|
LOCKMETHODTABLE *lockMethodTable = LockMethodTable[lockmethod];
|
2000-12-22 01:51:54 +01:00
|
|
|
char *new_status,
|
|
|
|
*old_status;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1998-06-30 04:33:34 +02:00
|
|
|
Assert(lockmethod < NumLockMethods);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* the waitqueue is ordered by priority. I insert myself according to
|
|
|
|
* the priority of the lock I am acquiring.
|
|
|
|
*
|
|
|
|
* SYNC NOTE: I am assuming that the lock table spinlock is sufficient
|
|
|
|
* synchronization for this queue. That will not be true if/when
|
|
|
|
* people can be deleted from the queue by a SIGINT or something.
|
|
|
|
*/
|
2000-05-31 02:28:42 +02:00
|
|
|
LOCK_PRINT("WaitOnLock: sleeping on lock", lock, lockmode);
|
2000-06-04 03:44:38 +02:00
|
|
|
|
|
|
|
old_status = pstrdup(get_ps_display());
|
2000-12-22 01:51:54 +01:00
|
|
|
new_status = (char *) palloc(strlen(old_status) + 10);
|
|
|
|
strcpy(new_status, old_status);
|
1998-08-25 23:20:32 +02:00
|
|
|
strcat(new_status, " waiting");
|
2000-06-04 03:44:38 +02:00
|
|
|
set_ps_display(new_status);
|
|
|
|
|
2000-12-22 01:51:54 +01:00
|
|
|
if (ProcSleep(lockMethodTable->ctl,
|
1998-06-30 04:33:34 +02:00
|
|
|
lockmode,
|
2000-12-22 01:51:54 +01:00
|
|
|
lock,
|
|
|
|
holder) != NO_ERROR)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-07 07:04:48 +02:00
|
|
|
/* -------------------
|
2000-11-29 00:27:57 +01:00
|
|
|
* We failed as a result of a deadlock, see HandleDeadLock().
|
1998-08-25 23:20:32 +02:00
|
|
|
* Decrement the lock nHolding and holders fields as
|
2000-12-22 01:51:54 +01:00
|
|
|
* we are no longer waiting on this lock. Removal of the holder and
|
|
|
|
* lock objects, if no longer needed, will happen in xact cleanup.
|
1997-09-07 07:04:48 +02:00
|
|
|
* -------------------
|
|
|
|
*/
|
|
|
|
lock->nHolding--;
|
1998-06-30 04:33:34 +02:00
|
|
|
lock->holders[lockmode]--;
|
2000-05-31 02:28:42 +02:00
|
|
|
LOCK_PRINT("WaitOnLock: aborting on lock", lock, lockmode);
|
1998-08-25 23:20:32 +02:00
|
|
|
Assert((lock->nHolding >= 0) && (lock->holders[lockmode] >= 0));
|
|
|
|
Assert(lock->nActive <= lock->nHolding);
|
1999-05-07 03:23:11 +02:00
|
|
|
if (lock->activeHolders[lockmode] == lock->holders[lockmode])
|
|
|
|
lock->waitMask &= BITS_OFF[lockmode];
|
1998-06-30 04:33:34 +02:00
|
|
|
SpinRelease(lockMethodTable->ctl->masterLock);
|
2000-11-29 00:27:57 +01:00
|
|
|
elog(ERROR, DeadLockMessage);
|
1998-08-25 23:20:32 +02:00
|
|
|
/* not reached */
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1999-05-07 03:23:11 +02:00
|
|
|
if (lock->activeHolders[lockmode] == lock->holders[lockmode])
|
|
|
|
lock->waitMask &= BITS_OFF[lockmode];
|
2000-06-04 03:44:38 +02:00
|
|
|
|
|
|
|
set_ps_display(old_status);
|
|
|
|
pfree(old_status);
|
|
|
|
pfree(new_status);
|
|
|
|
|
2000-05-31 02:28:42 +02:00
|
|
|
LOCK_PRINT("WaitOnLock: wakeup on lock", lock, lockmode);
|
1998-09-01 05:29:17 +02:00
|
|
|
return STATUS_OK;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
1998-06-30 04:33:34 +02:00
|
|
|
* LockRelease -- look up 'locktag' in lock table 'lockmethod' and
|
1997-09-07 07:04:48 +02:00
|
|
|
* release it.
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
* Side Effects: if the lock no longer conflicts with the highest
|
1997-09-07 07:04:48 +02:00
|
|
|
* priority waiting process, that process is granted the lock
|
|
|
|
* and awoken. (We have to grant the lock here to avoid a
|
|
|
|
* race between the waking process and any new process to
|
2000-12-22 01:51:54 +01:00
|
|
|
* come along and request the lock.)
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
|
|
|
bool
|
2000-12-22 01:51:54 +01:00
|
|
|
LockRelease(LOCKMETHOD lockmethod, LOCKTAG *locktag,
|
|
|
|
TransactionId xid, LOCKMODE lockmode)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
2000-12-22 01:51:54 +01:00
|
|
|
LOCK *lock;
|
1997-09-08 04:41:22 +02:00
|
|
|
SPINLOCK masterLock;
|
|
|
|
bool found;
|
1998-09-01 06:40:42 +02:00
|
|
|
LOCKMETHODTABLE *lockMethodTable;
|
2000-12-22 01:51:54 +01:00
|
|
|
HOLDER *holder;
|
|
|
|
HOLDERTAG holdertag;
|
|
|
|
HTAB *holderTable;
|
1997-09-08 04:41:22 +02:00
|
|
|
bool wakeupNeeded = true;
|
1996-10-11 05:22:59 +02:00
|
|
|
|
2000-05-31 02:28:42 +02:00
|
|
|
#ifdef LOCK_DEBUG
|
|
|
|
if (lockmethod == USER_LOCKMETHOD && Trace_userlocks)
|
|
|
|
elog(DEBUG, "LockRelease: user lock tag [%u] %d", locktag->objId.blkno, lockmode);
|
1996-10-11 05:22:59 +02:00
|
|
|
#endif
|
|
|
|
|
1998-08-25 23:20:32 +02:00
|
|
|
/* ???????? This must be changed when short term locks will be used */
|
|
|
|
locktag->lockmethod = lockmethod;
|
|
|
|
|
1998-06-30 04:33:34 +02:00
|
|
|
Assert(lockmethod < NumLockMethods);
|
|
|
|
lockMethodTable = LockMethodTable[lockmethod];
|
|
|
|
if (!lockMethodTable)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
1998-06-30 04:33:34 +02:00
|
|
|
elog(NOTICE, "lockMethodTable is null in LockRelease");
|
1998-09-01 05:29:17 +02:00
|
|
|
return FALSE;
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (LockingIsDisabled)
|
1998-09-01 05:29:17 +02:00
|
|
|
return TRUE;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1998-06-30 04:33:34 +02:00
|
|
|
masterLock = lockMethodTable->ctl->masterLock;
|
1997-09-07 07:04:48 +02:00
|
|
|
SpinAcquire(masterLock);
|
|
|
|
|
|
|
|
/*
|
1998-08-25 23:20:32 +02:00
|
|
|
* Find a lock with this tag
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
1998-08-25 23:20:32 +02:00
|
|
|
Assert(lockMethodTable->lockHash->hash == tag_hash);
|
|
|
|
lock = (LOCK *) hash_search(lockMethodTable->lockHash, (Pointer) locktag,
|
|
|
|
HASH_FIND, &found);
|
1996-10-11 05:22:59 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
/*
|
1998-09-01 06:40:42 +02:00
|
|
|
* let the caller print its own error message, too. Do not
|
|
|
|
* elog(ERROR).
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
|
|
|
if (!lock)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-07 07:04:48 +02:00
|
|
|
SpinRelease(masterLock);
|
|
|
|
elog(NOTICE, "LockRelease: locktable corrupted");
|
1998-09-01 05:29:17 +02:00
|
|
|
return FALSE;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
if (!found)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-07 07:04:48 +02:00
|
|
|
SpinRelease(masterLock);
|
2000-05-31 02:28:42 +02:00
|
|
|
elog(NOTICE, "LockRelease: no such lock");
|
1998-09-01 05:29:17 +02:00
|
|
|
return FALSE;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
1998-08-25 23:20:32 +02:00
|
|
|
LOCK_PRINT("LockRelease: found", lock, lockmode);
|
|
|
|
Assert((lock->nHolding > 0) && (lock->holders[lockmode] >= 0));
|
|
|
|
Assert((lock->nActive > 0) && (lock->activeHolders[lockmode] >= 0));
|
|
|
|
Assert(lock->nActive <= lock->nHolding);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1998-08-25 23:20:32 +02:00
|
|
|
/*
|
2000-12-22 01:51:54 +01:00
|
|
|
* Find the holder entry for this holder.
|
1998-08-25 23:20:32 +02:00
|
|
|
*/
|
2000-12-22 01:51:54 +01:00
|
|
|
MemSet(&holdertag, 0, sizeof(HOLDERTAG)); /* must clear padding, needed */
|
|
|
|
holdertag.lock = MAKE_OFFSET(lock);
|
|
|
|
holdertag.pid = MyProcPid;
|
|
|
|
TransactionIdStore(xid, &holdertag.xid);
|
|
|
|
|
|
|
|
holderTable = lockMethodTable->holderHash;
|
|
|
|
holder = (HOLDER *) hash_search(holderTable, (Pointer) &holdertag,
|
|
|
|
HASH_FIND_SAVE, &found);
|
|
|
|
if (!holder || !found)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-07 07:04:48 +02:00
|
|
|
SpinRelease(masterLock);
|
1996-10-11 05:22:59 +02:00
|
|
|
#ifdef USER_LOCKS
|
2000-05-31 02:28:42 +02:00
|
|
|
if (!found && lockmethod == USER_LOCKMETHOD)
|
|
|
|
elog(NOTICE, "LockRelease: no lock with this tag");
|
1998-09-01 06:40:42 +02:00
|
|
|
else
|
1996-10-11 05:22:59 +02:00
|
|
|
#endif
|
2000-12-22 01:51:54 +01:00
|
|
|
elog(NOTICE, "LockRelease: holder table corrupted");
|
1998-09-01 05:29:17 +02:00
|
|
|
return FALSE;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
2000-12-22 01:51:54 +01:00
|
|
|
HOLDER_PRINT("LockRelease: found", holder);
|
|
|
|
Assert(holder->tag.lock == MAKE_OFFSET(lock));
|
1998-08-25 23:20:32 +02:00
|
|
|
|
|
|
|
/*
|
1998-09-01 06:40:42 +02:00
|
|
|
* Check that we are actually holding a lock of the type we want to
|
|
|
|
* release.
|
1998-08-25 23:20:32 +02:00
|
|
|
*/
|
2000-12-22 01:51:54 +01:00
|
|
|
if (!(holder->holders[lockmode] > 0))
|
1998-09-01 06:40:42 +02:00
|
|
|
{
|
1998-08-25 23:20:32 +02:00
|
|
|
SpinRelease(masterLock);
|
2000-12-22 01:51:54 +01:00
|
|
|
HOLDER_PRINT("LockRelease: WRONGTYPE", holder);
|
1998-08-25 23:20:32 +02:00
|
|
|
elog(NOTICE, "LockRelease: you don't own a lock of type %s",
|
|
|
|
lock_types[lockmode]);
|
2000-12-22 01:51:54 +01:00
|
|
|
Assert(holder->holders[lockmode] >= 0);
|
1998-09-01 05:29:17 +02:00
|
|
|
return FALSE;
|
1998-08-25 23:20:32 +02:00
|
|
|
}
|
2000-12-22 01:51:54 +01:00
|
|
|
Assert(holder->nHolding > 0);
|
1998-08-25 23:20:32 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* fix the general lock stats
|
|
|
|
*/
|
|
|
|
lock->nHolding--;
|
|
|
|
lock->holders[lockmode]--;
|
|
|
|
lock->nActive--;
|
1998-09-01 06:40:42 +02:00
|
|
|
lock->activeHolders[lockmode]--;
|
1998-08-25 23:20:32 +02:00
|
|
|
|
2000-12-22 01:51:54 +01:00
|
|
|
if (!(lock->activeHolders[lockmode]))
|
|
|
|
{
|
|
|
|
/* change the conflict mask. No more of this lock type. */
|
|
|
|
lock->mask &= BITS_OFF[lockmode];
|
|
|
|
}
|
|
|
|
|
1999-05-07 03:23:11 +02:00
|
|
|
#ifdef NOT_USED
|
1998-08-25 23:20:32 +02:00
|
|
|
/* --------------------------
|
|
|
|
* If there are still active locks of the type I just released, no one
|
|
|
|
* should be woken up. Whoever is asleep will still conflict
|
|
|
|
* with the remaining locks.
|
|
|
|
* --------------------------
|
|
|
|
*/
|
|
|
|
if (lock->activeHolders[lockmode])
|
|
|
|
wakeupNeeded = false;
|
|
|
|
else
|
1999-05-07 03:23:11 +02:00
|
|
|
#endif
|
1999-05-25 18:15:34 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Above is not valid any more (due to MVCC lock modes). Actually
|
|
|
|
* we should compare activeHolders[lockmode] with number of
|
|
|
|
* waiters holding lock of this type and try to wakeup only if
|
|
|
|
* these numbers are equal (and lock released conflicts with locks
|
|
|
|
* requested by waiters). For the moment we only check the last
|
|
|
|
* condition.
|
|
|
|
*/
|
1999-05-07 03:23:11 +02:00
|
|
|
if (lockMethodTable->ctl->conflictTab[lockmode] & lock->waitMask)
|
|
|
|
wakeupNeeded = true;
|
|
|
|
|
1998-08-25 23:20:32 +02:00
|
|
|
LOCK_PRINT("LockRelease: updated", lock, lockmode);
|
|
|
|
Assert((lock->nHolding >= 0) && (lock->holders[lockmode] >= 0));
|
|
|
|
Assert((lock->nActive >= 0) && (lock->activeHolders[lockmode] >= 0));
|
|
|
|
Assert(lock->nActive <= lock->nHolding);
|
|
|
|
|
|
|
|
if (!lock->nHolding)
|
|
|
|
{
|
|
|
|
/* ------------------
|
|
|
|
* if there's no one waiting in the queue,
|
2000-12-22 01:51:54 +01:00
|
|
|
* we just released the last lock on this object.
|
1998-08-25 23:20:32 +02:00
|
|
|
* Delete it from the lock table.
|
|
|
|
* ------------------
|
|
|
|
*/
|
|
|
|
Assert(lockMethodTable->lockHash->hash == tag_hash);
|
|
|
|
lock = (LOCK *) hash_search(lockMethodTable->lockHash,
|
|
|
|
(Pointer) &(lock->tag),
|
|
|
|
HASH_REMOVE,
|
|
|
|
&found);
|
|
|
|
Assert(lock && found);
|
|
|
|
wakeupNeeded = false;
|
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/*
|
2000-12-22 01:51:54 +01:00
|
|
|
* Now fix the per-holder lock stats.
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
2000-12-22 01:51:54 +01:00
|
|
|
holder->holders[lockmode]--;
|
|
|
|
holder->nHolding--;
|
|
|
|
HOLDER_PRINT("LockRelease: updated", holder);
|
|
|
|
Assert((holder->nHolding >= 0) && (holder->holders[lockmode] >= 0));
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/*
|
2000-12-22 01:51:54 +01:00
|
|
|
* If this was my last hold on this lock, delete my entry in the holder
|
1997-09-07 07:04:48 +02:00
|
|
|
* table.
|
|
|
|
*/
|
2000-12-22 01:51:54 +01:00
|
|
|
if (!holder->nHolding)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
2000-12-22 01:51:54 +01:00
|
|
|
if (holder->queue.prev == INVALID_OFFSET)
|
|
|
|
elog(NOTICE, "LockRelease: holder.prev == INVALID_OFFSET");
|
|
|
|
if (holder->queue.next == INVALID_OFFSET)
|
|
|
|
elog(NOTICE, "LockRelease: holder.next == INVALID_OFFSET");
|
|
|
|
if (holder->queue.next != INVALID_OFFSET)
|
|
|
|
SHMQueueDelete(&holder->queue);
|
|
|
|
HOLDER_PRINT("LockRelease: deleting", holder);
|
|
|
|
holder = (HOLDER *) hash_search(holderTable, (Pointer) &holder,
|
|
|
|
HASH_REMOVE_SAVED, &found);
|
|
|
|
if (!holder || !found)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-07 07:04:48 +02:00
|
|
|
SpinRelease(masterLock);
|
2000-12-22 01:51:54 +01:00
|
|
|
elog(NOTICE, "LockRelease: remove holder, table corrupted");
|
1998-09-01 05:29:17 +02:00
|
|
|
return FALSE;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
if (wakeupNeeded)
|
2000-12-22 01:51:54 +01:00
|
|
|
ProcLockWakeup(lockmethod, lock);
|
2000-05-31 02:28:42 +02:00
|
|
|
#ifdef LOCK_DEBUG
|
|
|
|
else if (LOCK_DEBUG_ENABLED(lock))
|
|
|
|
elog(DEBUG, "LockRelease: no wakeup needed");
|
|
|
|
#endif
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
SpinRelease(masterLock);
|
1998-09-01 05:29:17 +02:00
|
|
|
return TRUE;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
1996-10-11 05:22:59 +02:00
|
|
|
/*
|
2000-12-22 01:51:54 +01:00
|
|
|
* LockReleaseAll -- Release all locks in a process's lock queue.
|
|
|
|
*
|
|
|
|
* Well, not really *all* locks.
|
|
|
|
*
|
|
|
|
* If 'allxids' is TRUE, all locks of the specified lock method are
|
|
|
|
* released, regardless of transaction affiliation.
|
|
|
|
*
|
|
|
|
* If 'allxids' is FALSE, all locks of the specified lock method and
|
|
|
|
* specified XID are released.
|
1996-10-11 05:22:59 +02:00
|
|
|
*/
|
1996-07-09 08:22:35 +02:00
|
|
|
bool
|
2000-12-22 01:51:54 +01:00
|
|
|
LockReleaseAll(LOCKMETHOD lockmethod, PROC *proc,
|
|
|
|
bool allxids, TransactionId xid)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
2000-12-22 01:51:54 +01:00
|
|
|
HOLDER *holder = NULL;
|
|
|
|
HOLDER *nextHolder = NULL;
|
|
|
|
SHM_QUEUE *lockQueue = &(proc->lockQueue);
|
1997-09-08 04:41:22 +02:00
|
|
|
SHMEM_OFFSET end = MAKE_OFFSET(lockQueue);
|
|
|
|
SPINLOCK masterLock;
|
1998-09-01 06:40:42 +02:00
|
|
|
LOCKMETHODTABLE *lockMethodTable;
|
1997-09-08 04:41:22 +02:00
|
|
|
int i,
|
1998-06-30 04:33:34 +02:00
|
|
|
numLockModes;
|
1997-09-08 04:41:22 +02:00
|
|
|
LOCK *lock;
|
|
|
|
bool found;
|
2000-12-22 01:51:54 +01:00
|
|
|
int nleft;
|
1996-10-11 05:22:59 +02:00
|
|
|
|
2000-05-31 02:28:42 +02:00
|
|
|
#ifdef LOCK_DEBUG
|
|
|
|
if (lockmethod == USER_LOCKMETHOD ? Trace_userlocks : Trace_locks)
|
2000-12-22 01:51:54 +01:00
|
|
|
elog(DEBUG, "LockReleaseAll: lockmethod=%d, pid=%d",
|
|
|
|
lockmethod, MyProcPid);
|
1996-10-11 05:22:59 +02:00
|
|
|
#endif
|
|
|
|
|
1998-06-30 04:33:34 +02:00
|
|
|
Assert(lockmethod < NumLockMethods);
|
|
|
|
lockMethodTable = LockMethodTable[lockmethod];
|
1998-09-01 06:40:42 +02:00
|
|
|
if (!lockMethodTable)
|
|
|
|
{
|
2000-12-22 01:51:54 +01:00
|
|
|
elog(NOTICE, "LockReleaseAll: bad lockmethod %d", lockmethod);
|
1998-09-01 05:29:17 +02:00
|
|
|
return FALSE;
|
1998-08-25 23:20:32 +02:00
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
if (SHMQueueEmpty(lockQueue))
|
|
|
|
return TRUE;
|
|
|
|
|
1998-08-25 23:20:32 +02:00
|
|
|
numLockModes = lockMethodTable->ctl->numLockModes;
|
|
|
|
masterLock = lockMethodTable->ctl->masterLock;
|
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
SpinAcquire(masterLock);
|
|
|
|
|
2000-12-22 01:51:54 +01:00
|
|
|
SHMQueueFirst(lockQueue, (Pointer *) &holder, &holder->queue);
|
|
|
|
|
|
|
|
nleft = 0;
|
|
|
|
|
|
|
|
do
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1999-05-25 18:15:34 +02:00
|
|
|
bool wakeupNeeded = false;
|
1998-09-01 06:40:42 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
/* ---------------------------
|
|
|
|
* XXX Here we assume the shared memory queue is circular and
|
|
|
|
* that we know its internal structure. Should have some sort of
|
|
|
|
* macros to allow one to walk it. mer 20 July 1991
|
|
|
|
* ---------------------------
|
|
|
|
*/
|
2000-12-22 01:51:54 +01:00
|
|
|
if (holder->queue.next == end)
|
|
|
|
nextHolder = NULL;
|
|
|
|
else
|
|
|
|
SHMQueueFirst(&holder->queue,
|
|
|
|
(Pointer *) &nextHolder, &nextHolder->queue);
|
|
|
|
|
|
|
|
Assert(holder->tag.pid == proc->pid);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2000-12-22 01:51:54 +01:00
|
|
|
lock = (LOCK *) MAKE_PTR(holder->tag.lock);
|
|
|
|
|
|
|
|
/* Ignore items that are not of the lockmethod to be removed */
|
|
|
|
if (LOCK_LOCKMETHOD(*lock) != lockmethod)
|
1998-09-01 06:40:42 +02:00
|
|
|
{
|
2000-12-22 01:51:54 +01:00
|
|
|
nleft++;
|
|
|
|
goto next_item;
|
1998-08-25 23:20:32 +02:00
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2000-12-22 01:51:54 +01:00
|
|
|
/* If not allxids, ignore items that are of the wrong xid */
|
|
|
|
if (!allxids && xid != holder->tag.xid)
|
1998-09-01 06:40:42 +02:00
|
|
|
{
|
1998-08-25 23:20:32 +02:00
|
|
|
nleft++;
|
|
|
|
goto next_item;
|
1996-10-11 05:22:59 +02:00
|
|
|
}
|
1998-08-25 23:20:32 +02:00
|
|
|
|
2000-12-22 01:51:54 +01:00
|
|
|
HOLDER_PRINT("LockReleaseAll", holder);
|
|
|
|
LOCK_PRINT("LockReleaseAll", lock, 0);
|
1998-08-25 23:20:32 +02:00
|
|
|
Assert(lock->nHolding > 0);
|
|
|
|
Assert(lock->nActive > 0);
|
|
|
|
Assert(lock->nActive <= lock->nHolding);
|
2000-12-22 01:51:54 +01:00
|
|
|
Assert(holder->nHolding >= 0);
|
|
|
|
Assert(holder->nHolding <= lock->nHolding);
|
1996-10-11 05:22:59 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
/* ------------------
|
|
|
|
* fix the general lock stats
|
|
|
|
* ------------------
|
|
|
|
*/
|
2000-12-22 01:51:54 +01:00
|
|
|
if (lock->nHolding != holder->nHolding)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1998-06-30 04:33:34 +02:00
|
|
|
for (i = 1; i <= numLockModes; i++)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
2000-12-22 01:51:54 +01:00
|
|
|
Assert(holder->holders[i] >= 0);
|
|
|
|
lock->holders[i] -= holder->holders[i];
|
|
|
|
lock->activeHolders[i] -= holder->holders[i];
|
1998-08-25 23:20:32 +02:00
|
|
|
Assert((lock->holders[i] >= 0) \
|
1998-09-01 06:40:42 +02:00
|
|
|
&&(lock->activeHolders[i] >= 0));
|
1997-09-07 07:04:48 +02:00
|
|
|
if (!lock->activeHolders[i])
|
|
|
|
lock->mask &= BITS_OFF[i];
|
1999-05-25 18:15:34 +02:00
|
|
|
|
1999-05-07 03:23:11 +02:00
|
|
|
/*
|
|
|
|
* Read comments in LockRelease
|
|
|
|
*/
|
2000-12-22 01:51:54 +01:00
|
|
|
if (!wakeupNeeded && holder->holders[i] > 0 &&
|
1999-05-07 03:23:11 +02:00
|
|
|
lockMethodTable->ctl->conflictTab[i] & lock->waitMask)
|
|
|
|
wakeupNeeded = true;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
2000-12-22 01:51:54 +01:00
|
|
|
lock->nHolding -= holder->nHolding;
|
|
|
|
lock->nActive -= holder->nHolding;
|
1998-08-25 23:20:32 +02:00
|
|
|
Assert((lock->nHolding >= 0) && (lock->nActive >= 0));
|
|
|
|
Assert(lock->nActive <= lock->nHolding);
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
else
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-07 07:04:48 +02:00
|
|
|
/* --------------
|
|
|
|
* set nHolding to zero so that we can garbage collect the lock
|
|
|
|
* down below...
|
|
|
|
* --------------
|
|
|
|
*/
|
|
|
|
lock->nHolding = 0;
|
1998-08-25 23:20:32 +02:00
|
|
|
/* Fix the lock status, just for next LOCK_PRINT message. */
|
1998-09-01 06:40:42 +02:00
|
|
|
for (i = 1; i <= numLockModes; i++)
|
|
|
|
{
|
1998-08-25 23:20:32 +02:00
|
|
|
Assert(lock->holders[i] == lock->activeHolders[i]);
|
|
|
|
lock->holders[i] = lock->activeHolders[i] = 0;
|
|
|
|
}
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
1998-08-25 23:20:32 +02:00
|
|
|
LOCK_PRINT("LockReleaseAll: updated", lock, 0);
|
|
|
|
|
2000-12-22 01:51:54 +01:00
|
|
|
HOLDER_PRINT("LockReleaseAll: deleting", holder);
|
|
|
|
|
1998-08-25 23:20:32 +02:00
|
|
|
/*
|
2000-12-22 01:51:54 +01:00
|
|
|
* Remove the holder entry from the process' lock queue
|
1998-08-25 23:20:32 +02:00
|
|
|
*/
|
2000-12-22 01:51:54 +01:00
|
|
|
SHMQueueDelete(&holder->queue);
|
1998-08-25 23:20:32 +02:00
|
|
|
|
2000-12-22 01:51:54 +01:00
|
|
|
/*
|
|
|
|
* remove the holder entry from the hashtable
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
2000-12-22 01:51:54 +01:00
|
|
|
holder = (HOLDER *) hash_search(lockMethodTable->holderHash,
|
|
|
|
(Pointer) holder,
|
|
|
|
HASH_REMOVE,
|
|
|
|
&found);
|
|
|
|
if (!holder || !found)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-07 07:04:48 +02:00
|
|
|
SpinRelease(masterLock);
|
2000-12-22 01:51:54 +01:00
|
|
|
elog(NOTICE, "LockReleaseAll: holder table corrupted");
|
1998-09-01 05:29:17 +02:00
|
|
|
return FALSE;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
if (!lock->nHolding)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-07 07:04:48 +02:00
|
|
|
/* --------------------
|
|
|
|
* if there's no one waiting in the queue, we've just released
|
|
|
|
* the last lock.
|
|
|
|
* --------------------
|
|
|
|
*/
|
1998-08-25 23:20:32 +02:00
|
|
|
LOCK_PRINT("LockReleaseAll: deleting", lock, 0);
|
1998-06-30 04:33:34 +02:00
|
|
|
Assert(lockMethodTable->lockHash->hash == tag_hash);
|
1998-08-25 23:20:32 +02:00
|
|
|
lock = (LOCK *) hash_search(lockMethodTable->lockHash,
|
|
|
|
(Pointer) &(lock->tag),
|
|
|
|
HASH_REMOVE, &found);
|
1997-09-07 07:04:48 +02:00
|
|
|
if ((!lock) || (!found))
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-07 07:04:48 +02:00
|
|
|
SpinRelease(masterLock);
|
|
|
|
elog(NOTICE, "LockReleaseAll: cannot remove lock from HTAB");
|
1998-09-01 05:29:17 +02:00
|
|
|
return FALSE;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
}
|
1999-05-07 03:23:11 +02:00
|
|
|
else if (wakeupNeeded)
|
2000-12-22 01:51:54 +01:00
|
|
|
ProcLockWakeup(lockmethod, lock);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
next_item:
|
2000-12-22 01:51:54 +01:00
|
|
|
holder = nextHolder;
|
|
|
|
} while (holder);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Reinitialize the queue only if nothing has been left in.
|
|
|
|
*/
|
1998-09-01 06:40:42 +02:00
|
|
|
if (nleft == 0)
|
|
|
|
{
|
2000-05-31 02:28:42 +02:00
|
|
|
#ifdef LOCK_DEBUG
|
|
|
|
if (lockmethod == USER_LOCKMETHOD ? Trace_userlocks : Trace_locks)
|
|
|
|
elog(DEBUG, "LockReleaseAll: reinitializing lockQueue");
|
|
|
|
#endif
|
1997-09-07 07:04:48 +02:00
|
|
|
SHMQueueInit(lockQueue);
|
1998-08-25 23:20:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
SpinRelease(masterLock);
|
2000-05-31 02:28:42 +02:00
|
|
|
#ifdef LOCK_DEBUG
|
|
|
|
if (lockmethod == USER_LOCKMETHOD ? Trace_userlocks : Trace_locks)
|
|
|
|
elog(DEBUG, "LockReleaseAll: done");
|
|
|
|
#endif
|
1998-08-25 23:20:32 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
return TRUE;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
1999-02-21 02:41:55 +01:00
|
|
|
LockShmemSize(int maxBackends)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
int size = 0;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1999-02-22 07:16:57 +01:00
|
|
|
size += MAXALIGN(sizeof(PROC_HDR)); /* ProcGlobal */
|
1999-05-25 18:15:34 +02:00
|
|
|
size += MAXALIGN(maxBackends * sizeof(PROC)); /* each MyProc */
|
1999-02-21 02:41:55 +01:00
|
|
|
size += MAXALIGN(maxBackends * sizeof(LOCKMETHODCTL)); /* each
|
1998-09-01 06:40:42 +02:00
|
|
|
* lockMethodTable->ctl */
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1999-02-22 07:16:57 +01:00
|
|
|
/* lockHash table */
|
|
|
|
size += hash_estimate_size(NLOCKENTS(maxBackends),
|
1999-03-06 22:17:56 +01:00
|
|
|
SHMEM_LOCKTAB_KEYSIZE,
|
|
|
|
SHMEM_LOCKTAB_DATASIZE);
|
1999-02-22 07:16:57 +01:00
|
|
|
|
2000-12-22 01:51:54 +01:00
|
|
|
/* holderHash table */
|
1999-05-29 08:14:43 +02:00
|
|
|
size += hash_estimate_size(NLOCKENTS(maxBackends),
|
2000-12-22 01:51:54 +01:00
|
|
|
SHMEM_HOLDERTAB_KEYSIZE,
|
|
|
|
SHMEM_HOLDERTAB_DATASIZE);
|
1999-03-06 22:17:56 +01:00
|
|
|
|
1999-05-25 18:15:34 +02:00
|
|
|
/*
|
|
|
|
* Since the lockHash entry count above is only an estimate, add 10%
|
|
|
|
* safety margin.
|
1999-03-06 22:17:56 +01:00
|
|
|
*/
|
|
|
|
size += size / 10;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
return size;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
1998-01-27 04:00:43 +01:00
|
|
|
/*
|
|
|
|
* DeadlockCheck -- Checks for deadlocks for a given process
|
|
|
|
*
|
|
|
|
* We can't block on user locks, so no sense testing for deadlock
|
|
|
|
* because there is no blocking, and no timer for the block.
|
|
|
|
*
|
|
|
|
* This code takes a list of locks a process holds, and the lock that
|
|
|
|
* the process is sleeping on, and tries to find if any of the processes
|
1998-01-27 16:35:30 +01:00
|
|
|
* waiting on its locks hold the lock it is waiting for. If no deadlock
|
|
|
|
* is found, it goes on to look at all the processes waiting on their locks.
|
1998-01-27 04:00:43 +01:00
|
|
|
*
|
|
|
|
* We have already locked the master lock before being called.
|
|
|
|
*/
|
|
|
|
bool
|
2000-12-22 01:51:54 +01:00
|
|
|
DeadLockCheck(PROC *thisProc, LOCK *findlock)
|
1998-01-27 04:00:43 +01:00
|
|
|
{
|
2000-12-22 01:51:54 +01:00
|
|
|
HOLDER *holder = NULL;
|
|
|
|
HOLDER *nextHolder = NULL;
|
|
|
|
PROC *waitProc;
|
|
|
|
PROC_QUEUE *waitQueue;
|
1999-05-25 18:15:34 +02:00
|
|
|
SHM_QUEUE *lockQueue = &(thisProc->lockQueue);
|
|
|
|
SHMEM_OFFSET end = MAKE_OFFSET(lockQueue);
|
2000-12-22 01:51:54 +01:00
|
|
|
LOCKMETHODCTL *lockctl = LockMethodTable[DEFAULT_LOCKMETHOD]->ctl;
|
1999-05-25 18:15:34 +02:00
|
|
|
LOCK *lock;
|
|
|
|
int i,
|
|
|
|
j;
|
2000-12-22 01:51:54 +01:00
|
|
|
bool first_run = (thisProc == MyProc);
|
1999-05-25 18:15:34 +02:00
|
|
|
|
|
|
|
static PROC *checked_procs[MAXBACKENDS];
|
|
|
|
static int nprocs;
|
1998-02-26 05:46:47 +01:00
|
|
|
|
1998-01-28 03:29:40 +01:00
|
|
|
/* initialize at start of recursion */
|
1999-05-13 17:55:45 +02:00
|
|
|
if (first_run)
|
1998-01-27 16:35:30 +01:00
|
|
|
{
|
2000-12-22 01:51:54 +01:00
|
|
|
checked_procs[0] = thisProc;
|
1998-01-27 16:35:30 +01:00
|
|
|
nprocs = 1;
|
1998-02-26 05:46:47 +01:00
|
|
|
}
|
1999-05-07 03:23:11 +02:00
|
|
|
|
1998-01-27 04:00:43 +01:00
|
|
|
if (SHMQueueEmpty(lockQueue))
|
|
|
|
return false;
|
|
|
|
|
2000-12-22 01:51:54 +01:00
|
|
|
SHMQueueFirst(lockQueue, (Pointer *) &holder, &holder->queue);
|
1998-01-27 04:00:43 +01:00
|
|
|
|
2000-12-22 01:51:54 +01:00
|
|
|
do
|
1998-01-27 04:00:43 +01:00
|
|
|
{
|
2000-12-22 01:51:54 +01:00
|
|
|
/* ---------------------------
|
|
|
|
* XXX Here we assume the shared memory queue is circular and
|
|
|
|
* that we know its internal structure. Should have some sort of
|
|
|
|
* macros to allow one to walk it. mer 20 July 1991
|
|
|
|
* ---------------------------
|
|
|
|
*/
|
|
|
|
if (holder->queue.next == end)
|
|
|
|
nextHolder = NULL;
|
|
|
|
else
|
|
|
|
SHMQueueFirst(&holder->queue,
|
|
|
|
(Pointer *) &nextHolder, &nextHolder->queue);
|
1998-01-27 04:00:43 +01:00
|
|
|
|
2000-12-22 01:51:54 +01:00
|
|
|
Assert(holder->tag.pid == thisProc->pid);
|
|
|
|
|
|
|
|
lock = (LOCK *) MAKE_PTR(holder->tag.lock);
|
1998-01-27 04:00:43 +01:00
|
|
|
|
2000-12-22 01:51:54 +01:00
|
|
|
/* Ignore user locks */
|
|
|
|
if (lock->tag.lockmethod != DEFAULT_LOCKMETHOD)
|
1999-05-13 17:55:45 +02:00
|
|
|
goto nxtl;
|
|
|
|
|
2000-12-22 01:51:54 +01:00
|
|
|
HOLDER_PRINT("DeadLockCheck", holder);
|
|
|
|
LOCK_PRINT("DeadLockCheck", lock, 0);
|
|
|
|
|
1998-01-27 04:00:43 +01:00
|
|
|
/*
|
1999-05-25 18:15:34 +02:00
|
|
|
* waitLock is always in lockQueue of waiting proc, if !first_run
|
|
|
|
* then upper caller will handle waitProcs queue of waitLock.
|
1998-01-27 04:00:43 +01:00
|
|
|
*/
|
1999-05-13 17:55:45 +02:00
|
|
|
if (thisProc->waitLock == lock && !first_run)
|
|
|
|
goto nxtl;
|
1998-01-27 16:35:30 +01:00
|
|
|
|
1999-05-13 17:55:45 +02:00
|
|
|
/*
|
1999-05-25 18:15:34 +02:00
|
|
|
* If we found proc holding findlock and sleeping on some my other
|
|
|
|
* lock then we have to check does it block me or another waiters.
|
1999-05-13 17:55:45 +02:00
|
|
|
*/
|
|
|
|
if (lock == findlock && !first_run)
|
1998-02-26 05:46:47 +01:00
|
|
|
{
|
1999-05-25 18:15:34 +02:00
|
|
|
int lm;
|
1999-05-13 17:55:45 +02:00
|
|
|
|
2000-12-22 01:51:54 +01:00
|
|
|
Assert(holder->nHolding > 0);
|
1999-05-13 17:55:45 +02:00
|
|
|
for (lm = 1; lm <= lockctl->numLockModes; lm++)
|
|
|
|
{
|
2000-12-22 01:51:54 +01:00
|
|
|
if (holder->holders[lm] > 0 &&
|
1999-05-13 17:55:45 +02:00
|
|
|
lockctl->conflictTab[lm] & findlock->waitMask)
|
|
|
|
return true;
|
|
|
|
}
|
1999-05-25 18:15:34 +02:00
|
|
|
|
1999-05-13 17:55:45 +02:00
|
|
|
/*
|
|
|
|
* Else - get the next lock from thisProc's lockQueue
|
|
|
|
*/
|
1999-05-25 18:15:34 +02:00
|
|
|
goto nxtl;
|
1999-05-13 17:55:45 +02:00
|
|
|
}
|
1998-02-26 05:46:47 +01:00
|
|
|
|
1999-05-13 17:55:45 +02:00
|
|
|
waitQueue = &(lock->waitProcs);
|
|
|
|
waitProc = (PROC *) MAKE_PTR(waitQueue->links.prev);
|
|
|
|
|
2000-12-22 01:51:54 +01:00
|
|
|
/*
|
|
|
|
* NOTE: loop must count down because we want to examine each item
|
|
|
|
* in the queue even if waitQueue->size decreases due to waking up
|
|
|
|
* some of the processes.
|
|
|
|
*/
|
|
|
|
for (i = waitQueue->size; --i >= 0; )
|
1999-05-13 17:55:45 +02:00
|
|
|
{
|
2000-12-22 01:51:54 +01:00
|
|
|
Assert(waitProc->waitLock == lock);
|
1999-05-13 17:55:45 +02:00
|
|
|
if (waitProc == thisProc)
|
|
|
|
{
|
2000-12-22 01:51:54 +01:00
|
|
|
/* This should only happen at first level */
|
1999-05-13 17:55:45 +02:00
|
|
|
Assert(waitProc == MyProc);
|
2000-12-22 01:51:54 +01:00
|
|
|
goto nextWaitProc;
|
1999-05-13 17:55:45 +02:00
|
|
|
}
|
1999-05-25 18:15:34 +02:00
|
|
|
if (lock == findlock) /* first_run also true */
|
1998-01-27 04:00:43 +01:00
|
|
|
{
|
1999-05-07 03:23:11 +02:00
|
|
|
/*
|
1999-05-13 17:55:45 +02:00
|
|
|
* If me blocked by his holdlock...
|
1999-05-07 03:23:11 +02:00
|
|
|
*/
|
2000-12-22 01:51:54 +01:00
|
|
|
if (lockctl->conflictTab[MyProc->waitLockMode] & waitProc->holdLock)
|
1998-01-28 03:29:40 +01:00
|
|
|
{
|
1999-05-13 17:55:45 +02:00
|
|
|
/* and he blocked by me -> deadlock */
|
2000-12-22 01:51:54 +01:00
|
|
|
if (lockctl->conflictTab[waitProc->waitLockMode] & MyProc->holdLock)
|
1998-01-28 07:52:58 +01:00
|
|
|
return true;
|
1999-05-13 17:55:45 +02:00
|
|
|
/* we shouldn't look at lockQueue of our blockers */
|
2000-12-22 01:51:54 +01:00
|
|
|
goto nextWaitProc;
|
1998-01-28 03:29:40 +01:00
|
|
|
}
|
1999-05-25 18:15:34 +02:00
|
|
|
|
1998-01-28 03:29:40 +01:00
|
|
|
/*
|
1999-05-25 18:15:34 +02:00
|
|
|
* If he isn't blocked by me and we request
|
|
|
|
* non-conflicting lock modes - no deadlock here because
|
2000-12-22 01:51:54 +01:00
|
|
|
* he isn't blocked by me in any sense (explicitly or
|
1999-05-25 18:15:34 +02:00
|
|
|
* implicitly). Note that we don't do like test if
|
|
|
|
* !first_run (when thisProc is holder and non-waiter on
|
|
|
|
* lock) and so we call DeadLockCheck below for every
|
|
|
|
* waitProc in thisProc->lockQueue, even for waitProc-s
|
|
|
|
* un-blocked by thisProc. Should we? This could save us
|
|
|
|
* some time...
|
1998-01-28 03:29:40 +01:00
|
|
|
*/
|
2000-12-22 01:51:54 +01:00
|
|
|
if (!(lockctl->conflictTab[waitProc->waitLockMode] & MyProc->holdLock) &&
|
|
|
|
!(lockctl->conflictTab[waitProc->waitLockMode] & (1 << MyProc->waitLockMode)))
|
|
|
|
goto nextWaitProc;
|
1999-05-13 17:55:45 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2000-12-22 01:51:54 +01:00
|
|
|
* Skip this waiter if already checked.
|
1999-05-13 17:55:45 +02:00
|
|
|
*/
|
|
|
|
for (j = 0; j < nprocs; j++)
|
|
|
|
{
|
|
|
|
if (checked_procs[j] == waitProc)
|
2000-12-22 01:51:54 +01:00
|
|
|
goto nextWaitProc;
|
1999-05-13 17:55:45 +02:00
|
|
|
}
|
2000-12-22 01:51:54 +01:00
|
|
|
|
|
|
|
/* Recursively check this process's lockQueue. */
|
|
|
|
Assert(nprocs < MAXBACKENDS);
|
|
|
|
checked_procs[nprocs++] = waitProc;
|
|
|
|
|
|
|
|
if (DeadLockCheck(waitProc, findlock))
|
1999-05-13 17:55:45 +02:00
|
|
|
{
|
2000-12-22 01:51:54 +01:00
|
|
|
int holdLock;
|
1999-05-13 17:55:45 +02:00
|
|
|
|
2000-12-22 01:51:54 +01:00
|
|
|
/*
|
|
|
|
* Ok, but is waitProc waiting for me (thisProc) ?
|
|
|
|
*/
|
|
|
|
if (thisProc->waitLock == lock)
|
|
|
|
{
|
|
|
|
Assert(first_run);
|
|
|
|
holdLock = thisProc->holdLock;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* should we cache holdLock to speed this up? */
|
|
|
|
holdLock = LockGetMyHoldLocks(holder->tag.lock, thisProc);
|
|
|
|
Assert(holdLock != 0);
|
|
|
|
}
|
|
|
|
if (lockctl->conflictTab[waitProc->waitLockMode] & holdLock)
|
1999-05-13 17:55:45 +02:00
|
|
|
{
|
|
|
|
/*
|
2000-12-22 01:51:54 +01:00
|
|
|
* Last attempt to avoid deadlock: try to wakeup myself.
|
1999-05-13 17:55:45 +02:00
|
|
|
*/
|
2000-12-22 01:51:54 +01:00
|
|
|
if (first_run)
|
1999-05-13 17:55:45 +02:00
|
|
|
{
|
2000-12-22 01:51:54 +01:00
|
|
|
if (LockResolveConflicts(DEFAULT_LOCKMETHOD,
|
|
|
|
MyProc->waitLockMode,
|
|
|
|
MyProc->waitLock,
|
|
|
|
MyProc->waitHolder,
|
|
|
|
MyProc,
|
|
|
|
NULL) == STATUS_OK)
|
1999-05-13 17:55:45 +02:00
|
|
|
{
|
2000-12-22 01:51:54 +01:00
|
|
|
GrantLock(MyProc->waitLock,
|
|
|
|
MyProc->waitHolder,
|
|
|
|
MyProc->waitLockMode);
|
|
|
|
ProcWakeup(MyProc, NO_ERROR);
|
|
|
|
return false;
|
1999-05-13 17:55:45 +02:00
|
|
|
}
|
1998-01-28 03:29:40 +01:00
|
|
|
}
|
2000-12-22 01:51:54 +01:00
|
|
|
return true;
|
|
|
|
}
|
1999-05-25 18:15:34 +02:00
|
|
|
|
2000-12-22 01:51:54 +01:00
|
|
|
/*
|
|
|
|
* Hell! Is he blocked by any (other) holder ?
|
|
|
|
*/
|
|
|
|
if (LockResolveConflicts(DEFAULT_LOCKMETHOD,
|
|
|
|
waitProc->waitLockMode,
|
|
|
|
lock,
|
|
|
|
waitProc->waitHolder,
|
|
|
|
waitProc,
|
|
|
|
NULL) != STATUS_OK)
|
|
|
|
{
|
1999-05-13 17:55:45 +02:00
|
|
|
/*
|
2000-12-22 01:51:54 +01:00
|
|
|
* Blocked by others - no deadlock...
|
1999-05-13 17:55:45 +02:00
|
|
|
*/
|
2000-12-22 01:51:54 +01:00
|
|
|
LOCK_PRINT("DeadLockCheck: blocked by others",
|
|
|
|
lock, waitProc->waitLockMode);
|
|
|
|
goto nextWaitProc;
|
1998-01-27 04:00:43 +01:00
|
|
|
}
|
2000-12-22 01:51:54 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Well - wakeup this guy! This is the case of
|
|
|
|
* implicit blocking: thisProc blocked someone who
|
|
|
|
* blocked waitProc by the fact that he/someone is
|
|
|
|
* already waiting for lock. We do this for
|
|
|
|
* anti-starving.
|
|
|
|
*/
|
|
|
|
GrantLock(lock, waitProc->waitHolder, waitProc->waitLockMode);
|
|
|
|
waitProc = ProcWakeup(waitProc, NO_ERROR);
|
|
|
|
/*
|
|
|
|
* Use next-proc link returned by ProcWakeup, since this
|
|
|
|
* proc's own links field is now cleared.
|
|
|
|
*/
|
|
|
|
continue;
|
1998-01-27 04:00:43 +01:00
|
|
|
}
|
2000-12-22 01:51:54 +01:00
|
|
|
|
|
|
|
nextWaitProc:
|
1999-05-13 17:55:45 +02:00
|
|
|
waitProc = (PROC *) MAKE_PTR(waitProc->links.prev);
|
1998-01-27 04:00:43 +01:00
|
|
|
}
|
1998-02-26 05:46:47 +01:00
|
|
|
|
2000-12-22 01:51:54 +01:00
|
|
|
nxtl:
|
|
|
|
holder = nextHolder;
|
|
|
|
} while (holder);
|
1998-01-27 04:00:43 +01:00
|
|
|
|
|
|
|
/* if we got here, no deadlock */
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2000-05-31 02:28:42 +02:00
|
|
|
#ifdef LOCK_DEBUG
|
1997-02-12 06:25:13 +01:00
|
|
|
/*
|
1998-08-25 23:20:32 +02:00
|
|
|
* Dump all locks in the proc->lockQueue. Must have already acquired
|
|
|
|
* the masterLock.
|
1997-02-12 06:25:13 +01:00
|
|
|
*/
|
|
|
|
void
|
2000-12-22 01:51:54 +01:00
|
|
|
DumpLocks(void)
|
1997-02-12 06:25:13 +01:00
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
SHMEM_OFFSET location;
|
|
|
|
PROC *proc;
|
|
|
|
SHM_QUEUE *lockQueue;
|
2000-12-22 01:51:54 +01:00
|
|
|
HOLDER *holder = NULL;
|
|
|
|
HOLDER *nextHolder = NULL;
|
1997-09-08 04:41:22 +02:00
|
|
|
SHMEM_OFFSET end;
|
|
|
|
LOCK *lock;
|
1998-08-25 23:20:32 +02:00
|
|
|
int lockmethod = DEFAULT_LOCKMETHOD;
|
1998-09-01 06:40:42 +02:00
|
|
|
LOCKMETHODTABLE *lockMethodTable;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1998-01-25 06:15:15 +01:00
|
|
|
ShmemPIDLookup(MyProcPid, &location);
|
1997-09-07 07:04:48 +02:00
|
|
|
if (location == INVALID_OFFSET)
|
|
|
|
return;
|
|
|
|
proc = (PROC *) MAKE_PTR(location);
|
|
|
|
if (proc != MyProc)
|
|
|
|
return;
|
|
|
|
lockQueue = &proc->lockQueue;
|
2000-12-22 01:51:54 +01:00
|
|
|
end = MAKE_OFFSET(lockQueue);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1998-06-30 04:33:34 +02:00
|
|
|
Assert(lockmethod < NumLockMethods);
|
|
|
|
lockMethodTable = LockMethodTable[lockmethod];
|
|
|
|
if (!lockMethodTable)
|
1997-09-07 07:04:48 +02:00
|
|
|
return;
|
|
|
|
|
2000-12-22 01:51:54 +01:00
|
|
|
if (proc->waitLock)
|
|
|
|
LOCK_PRINT("DumpLocks: waiting on", proc->waitLock, 0);
|
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
if (SHMQueueEmpty(lockQueue))
|
|
|
|
return;
|
|
|
|
|
2000-12-22 01:51:54 +01:00
|
|
|
SHMQueueFirst(lockQueue, (Pointer *) &holder, &holder->queue);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2000-12-22 01:51:54 +01:00
|
|
|
do
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
|
|
|
/* ---------------------------
|
|
|
|
* XXX Here we assume the shared memory queue is circular and
|
|
|
|
* that we know its internal structure. Should have some sort of
|
|
|
|
* macros to allow one to walk it. mer 20 July 1991
|
|
|
|
* ---------------------------
|
|
|
|
*/
|
2000-12-22 01:51:54 +01:00
|
|
|
if (holder->queue.next == end)
|
|
|
|
nextHolder = NULL;
|
|
|
|
else
|
|
|
|
SHMQueueFirst(&holder->queue,
|
|
|
|
(Pointer *) &nextHolder, &nextHolder->queue);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2000-12-22 01:51:54 +01:00
|
|
|
Assert(holder->tag.pid == proc->pid);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2000-12-22 01:51:54 +01:00
|
|
|
lock = (LOCK *) MAKE_PTR(holder->tag.lock);
|
1998-08-25 23:20:32 +02:00
|
|
|
|
2000-12-22 01:51:54 +01:00
|
|
|
HOLDER_PRINT("DumpLocks", holder);
|
|
|
|
LOCK_PRINT("DumpLocks", lock, 0);
|
|
|
|
|
|
|
|
holder = nextHolder;
|
|
|
|
} while (holder);
|
1997-02-12 06:25:13 +01:00
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1998-08-25 23:20:32 +02:00
|
|
|
/*
|
|
|
|
* Dump all postgres locks. Must have already acquired the masterLock.
|
|
|
|
*/
|
|
|
|
void
|
2000-12-22 01:51:54 +01:00
|
|
|
DumpAllLocks(void)
|
1998-08-25 23:20:32 +02:00
|
|
|
{
|
|
|
|
SHMEM_OFFSET location;
|
1998-09-01 06:40:42 +02:00
|
|
|
PROC *proc;
|
2000-12-22 01:51:54 +01:00
|
|
|
HOLDER *holder = NULL;
|
1998-09-01 06:40:42 +02:00
|
|
|
LOCK *lock;
|
|
|
|
int pid;
|
1998-08-25 23:20:32 +02:00
|
|
|
int lockmethod = DEFAULT_LOCKMETHOD;
|
1998-09-01 06:40:42 +02:00
|
|
|
LOCKMETHODTABLE *lockMethodTable;
|
2000-12-22 01:51:54 +01:00
|
|
|
HTAB *holderTable;
|
2001-01-02 05:33:24 +01:00
|
|
|
HASH_SEQ_STATUS status;
|
1998-08-25 23:20:32 +02:00
|
|
|
|
|
|
|
pid = getpid();
|
1998-09-01 06:40:42 +02:00
|
|
|
ShmemPIDLookup(pid, &location);
|
1998-08-25 23:20:32 +02:00
|
|
|
if (location == INVALID_OFFSET)
|
|
|
|
return;
|
|
|
|
proc = (PROC *) MAKE_PTR(location);
|
|
|
|
if (proc != MyProc)
|
|
|
|
return;
|
|
|
|
|
|
|
|
Assert(lockmethod < NumLockMethods);
|
|
|
|
lockMethodTable = LockMethodTable[lockmethod];
|
|
|
|
if (!lockMethodTable)
|
|
|
|
return;
|
|
|
|
|
2000-12-22 01:51:54 +01:00
|
|
|
holderTable = lockMethodTable->holderHash;
|
1998-08-25 23:20:32 +02:00
|
|
|
|
2000-12-22 01:51:54 +01:00
|
|
|
if (proc->waitLock)
|
|
|
|
LOCK_PRINT("DumpAllLocks: waiting on", proc->waitLock, 0);
|
1998-08-25 23:20:32 +02:00
|
|
|
|
2001-01-02 05:33:24 +01:00
|
|
|
hash_seq_init(&status, holderTable);
|
|
|
|
while ((holder = (HOLDER *) hash_seq_search(&status)) &&
|
2000-12-22 01:51:54 +01:00
|
|
|
(holder != (HOLDER *) TRUE))
|
1998-09-01 06:40:42 +02:00
|
|
|
{
|
2000-12-22 01:51:54 +01:00
|
|
|
HOLDER_PRINT("DumpAllLocks", holder);
|
1998-08-25 23:20:32 +02:00
|
|
|
|
2000-12-22 01:51:54 +01:00
|
|
|
if (holder->tag.lock)
|
1998-09-01 06:40:42 +02:00
|
|
|
{
|
2000-12-22 01:51:54 +01:00
|
|
|
lock = (LOCK *) MAKE_PTR(holder->tag.lock);
|
2000-05-31 02:28:42 +02:00
|
|
|
LOCK_PRINT("DumpAllLocks", lock, 0);
|
1998-08-25 23:20:32 +02:00
|
|
|
}
|
1998-09-01 06:40:42 +02:00
|
|
|
else
|
2000-12-22 01:51:54 +01:00
|
|
|
elog(DEBUG, "DumpAllLocks: holder->tag.lock = NULL");
|
1998-08-25 23:20:32 +02:00
|
|
|
}
|
|
|
|
}
|
1998-09-01 06:40:42 +02:00
|
|
|
|
2000-05-31 02:28:42 +02:00
|
|
|
#endif /* LOCK_DEBUG */
|