1996-07-09 08:22:35 +02:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
1999-02-14 00:22:53 +01:00
|
|
|
* lock.c
|
1997-09-07 07:04:48 +02:00
|
|
|
* simple lock acquisition
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
* Copyright (c) 1994, Regents of the University of California
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* IDENTIFICATION
|
1999-02-21 04:49:55 +01:00
|
|
|
* $Header: /cvsroot/pgsql/src/backend/storage/lmgr/lock.c,v 1.44 1999/02/21 03:49:22 scrappy 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
|
1998-01-27 16:35:30 +01:00
|
|
|
* a process tries to acquire a lock of a type that conflictRs
|
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
|
|
|
*
|
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(),
|
|
|
|
* LockMethodTableRename(), LockReleaseAll, LockOwners()
|
|
|
|
* LockResolveConflicts(), GrantLock()
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
1997-09-07 07:04:48 +02:00
|
|
|
* NOTE: This module is used to define new lock tables. The
|
|
|
|
* multi-level lock table (multi.c) used by the heap
|
|
|
|
* access methods calls these routines. See multi.c for
|
|
|
|
* examples showing how to use this interface.
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
*/
|
1997-09-07 07:04:48 +02:00
|
|
|
#include <stdio.h> /* for sprintf() */
|
1996-11-08 07:02:30 +01:00
|
|
|
#include <string.h>
|
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"
|
1998-01-25 06:15:15 +01:00
|
|
|
#include "miscadmin.h"
|
1996-07-09 08:22:35 +02:00
|
|
|
#include "storage/shmem.h"
|
1998-01-27 16:35:30 +01:00
|
|
|
#include "storage/sinvaladt.h"
|
1996-07-09 08:22:35 +02:00
|
|
|
#include "storage/spin.h"
|
|
|
|
#include "storage/proc.h"
|
|
|
|
#include "storage/lock.h"
|
1996-11-08 07:02:30 +01:00
|
|
|
#include "utils/dynahash.h"
|
1996-07-09 08:22:35 +02:00
|
|
|
#include "utils/hsearch.h"
|
1996-11-08 07:02:30 +01:00
|
|
|
#include "utils/memutils.h"
|
1996-07-09 08:22:35 +02:00
|
|
|
#include "utils/palloc.h"
|
|
|
|
#include "access/xact.h"
|
1997-02-14 05:52:59 +01:00
|
|
|
#include "access/transam.h"
|
1998-08-25 23:20:32 +02:00
|
|
|
#include "utils/trace.h"
|
|
|
|
#include "utils/ps_status.h"
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1998-08-25 23:20:32 +02:00
|
|
|
static int WaitOnLock(LOCKMETHOD lockmethod, LOCK *lock, LOCKMODE lockmode,
|
1998-09-01 06:40:42 +02:00
|
|
|
TransactionId xid);
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1998-08-25 23:20:32 +02:00
|
|
|
/*
|
|
|
|
* lockDebugRelation can be used to trace unconditionally a single relation,
|
|
|
|
* for example pg_listener, if you suspect there are locking problems.
|
|
|
|
*
|
|
|
|
* lockDebugOidMin is is used to avoid tracing postgres relations, which
|
|
|
|
* would produce a lot of output. Unfortunately most system relations are
|
|
|
|
* created after bootstrap and have oid greater than BootstrapObjectIdData.
|
|
|
|
* If you are using tprintf you should specify a value greater than the max
|
|
|
|
* oid of system relations, which can be found with the following query:
|
|
|
|
*
|
1998-09-01 06:40:42 +02:00
|
|
|
* select max(int4in(int4out(oid))) from pg_class where relname ~ '^pg_';
|
1998-08-25 23:20:32 +02:00
|
|
|
*
|
|
|
|
* To get a useful lock trace you can use the following pg_options:
|
|
|
|
*
|
1998-09-01 06:40:42 +02:00
|
|
|
* -T "verbose,query,locks,userlocks,lock_debug_oidmin=17500"
|
1998-08-25 23:20:32 +02:00
|
|
|
*/
|
|
|
|
#define LOCKDEBUG(lockmethod) (pg_options[TRACE_SHORTLOCKS+lockmethod])
|
|
|
|
#define lockDebugRelation (pg_options[TRACE_LOCKRELATION])
|
|
|
|
#define lockDebugOidMin (pg_options[TRACE_LOCKOIDMIN])
|
|
|
|
#define lockReadPriority (pg_options[OPT_LOCKREADPRIORITY])
|
|
|
|
|
|
|
|
#ifdef LOCK_MGR_DEBUG
|
|
|
|
#define LOCK_PRINT(where,lock,type) \
|
1998-09-01 06:40:42 +02:00
|
|
|
if (((LOCKDEBUG(LOCK_LOCKMETHOD(*(lock))) >= 1) \
|
|
|
|
&& (lock->tag.relId >= lockDebugOidMin)) \
|
1999-01-17 21:59:56 +01:00
|
|
|
|| \
|
|
|
|
(lockDebugRelation && (lock->tag.relId == lockDebugRelation))) \
|
1998-09-01 06:40:42 +02:00
|
|
|
LOCK_PRINT_AUX(where,lock,type)
|
1998-08-25 23:20:32 +02:00
|
|
|
|
|
|
|
#define LOCK_PRINT_AUX(where,lock,type) \
|
|
|
|
TPRINTF(TRACE_ALL, \
|
1998-12-15 13:47:01 +01:00
|
|
|
"%s: lock(%x) tbl(%d) rel(%d) db(%d) obj(%u) mask(%x) " \
|
1998-08-25 23:20:32 +02:00
|
|
|
"hold(%d,%d,%d,%d,%d)=%d " \
|
|
|
|
"act(%d,%d,%d,%d,%d)=%d wait(%d) type(%s)", \
|
|
|
|
where, \
|
|
|
|
MAKE_OFFSET(lock), \
|
|
|
|
lock->tag.lockmethod, \
|
|
|
|
lock->tag.relId, \
|
|
|
|
lock->tag.dbId, \
|
1998-12-15 13:47:01 +01:00
|
|
|
lock->tag.objId.blkno, \
|
1998-08-25 23:20:32 +02:00
|
|
|
lock->mask, \
|
|
|
|
lock->holders[1], \
|
|
|
|
lock->holders[2], \
|
|
|
|
lock->holders[3], \
|
|
|
|
lock->holders[4], \
|
|
|
|
lock->holders[5], \
|
|
|
|
lock->nHolding, \
|
|
|
|
lock->activeHolders[1], \
|
|
|
|
lock->activeHolders[2], \
|
|
|
|
lock->activeHolders[3], \
|
|
|
|
lock->activeHolders[4], \
|
|
|
|
lock->activeHolders[5], \
|
|
|
|
lock->nActive, \
|
|
|
|
lock->waitProcs.size, \
|
|
|
|
lock_types[type])
|
|
|
|
|
|
|
|
#define XID_PRINT(where,xidentP) \
|
|
|
|
if (((LOCKDEBUG(XIDENT_LOCKMETHOD(*(xidentP))) >= 1) \
|
1998-09-01 06:40:42 +02:00
|
|
|
&& (((LOCK *)MAKE_PTR(xidentP->tag.lock))->tag.relId \
|
1998-08-25 23:20:32 +02:00
|
|
|
>= lockDebugOidMin)) \
|
1999-01-17 21:59:56 +01:00
|
|
|
|| (lockDebugRelation && \
|
|
|
|
(((LOCK *)MAKE_PTR(xidentP->tag.lock))->tag.relId \
|
|
|
|
== lockDebugRelation))) \
|
1998-09-01 06:40:42 +02:00
|
|
|
XID_PRINT_AUX(where,xidentP)
|
1998-08-25 23:20:32 +02:00
|
|
|
|
|
|
|
#define XID_PRINT_AUX(where,xidentP) \
|
|
|
|
TPRINTF(TRACE_ALL, \
|
|
|
|
"%s: xid(%x) lock(%x) tbl(%d) pid(%d) xid(%d) " \
|
|
|
|
"hold(%d,%d,%d,%d,%d)=%d", \
|
|
|
|
where, \
|
|
|
|
MAKE_OFFSET(xidentP), \
|
|
|
|
xidentP->tag.lock, \
|
|
|
|
XIDENT_LOCKMETHOD(*(xidentP)), \
|
|
|
|
xidentP->tag.pid, \
|
|
|
|
xidentP->tag.xid, \
|
|
|
|
xidentP->holders[1], \
|
|
|
|
xidentP->holders[2], \
|
|
|
|
xidentP->holders[3], \
|
|
|
|
xidentP->holders[4], \
|
|
|
|
xidentP->holders[5], \
|
|
|
|
xidentP->nHolding)
|
|
|
|
|
1998-09-01 06:40:42 +02:00
|
|
|
#else /* !LOCK_MGR_DEBUG */
|
1998-08-25 23:20:32 +02:00
|
|
|
#define LOCK_PRINT(where,lock,type)
|
|
|
|
#define LOCK_PRINT_AUX(where,lock,type)
|
1996-07-09 08:22:35 +02:00
|
|
|
#define XID_PRINT(where,xidentP)
|
1998-08-25 23:20:32 +02:00
|
|
|
#define XID_PRINT_AUX(where,xidentP)
|
1998-09-01 06:40:42 +02:00
|
|
|
#endif /* !LOCK_MGR_DEBUG */
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1997-09-08 04:41:22 +02:00
|
|
|
static char *lock_types[] = {
|
1998-08-25 23:20:32 +02:00
|
|
|
"",
|
1997-09-07 07:04:48 +02:00
|
|
|
"WRITE",
|
|
|
|
"READ",
|
|
|
|
"WRITE INTENT",
|
|
|
|
"READ INTENT",
|
|
|
|
"EXTEND"
|
1997-03-15 02:23:58 +01:00
|
|
|
};
|
1997-02-12 06:25:13 +01:00
|
|
|
|
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 */
|
|
|
|
|
1998-06-30 04:33:34 +02:00
|
|
|
static MASK BITS_OFF[MAX_LOCKMODES];
|
|
|
|
static MASK BITS_ON[MAX_LOCKMODES];
|
1996-07-09 08:22:35 +02:00
|
|
|
|
|
|
|
/* -----------------
|
|
|
|
* XXX Want to move this to this file
|
|
|
|
* -----------------
|
|
|
|
*/
|
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
|
|
|
|
InitLocks()
|
|
|
|
{
|
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
|
|
|
* remember 0th lockmode is invalid
|
1997-09-07 07:04:48 +02:00
|
|
|
* -------------------
|
|
|
|
*/
|
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
|
|
|
}
|
1998-08-25 23:20:32 +02:00
|
|
|
|
|
|
|
#ifdef LOCK_MGR_DEBUG
|
1998-09-01 06:40:42 +02:00
|
|
|
|
1998-08-25 23:20:32 +02:00
|
|
|
/*
|
1998-09-01 06:40:42 +02:00
|
|
|
* If lockDebugOidMin value has not been specified in pg_options set a
|
|
|
|
* default value.
|
1998-08-25 23:20:32 +02:00
|
|
|
*/
|
1998-09-01 06:40:42 +02:00
|
|
|
if (!lockDebugOidMin)
|
1998-08-25 23:20:32 +02:00
|
|
|
lockDebugOidMin = BootstrapObjectIdData;
|
|
|
|
#endif
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* -------------------
|
|
|
|
* LockDisable -- sets LockingIsDisabled flag to TRUE or FALSE.
|
|
|
|
* ------------------
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
LockDisable(int status)
|
|
|
|
{
|
1997-09-07 07:04:48 +02:00
|
|
|
LockingIsDisabled = status;
|
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
|
1998-09-01 06:40:42 +02:00
|
|
|
LockMethodInit(LOCKMETHODTABLE * lockMethodTable,
|
|
|
|
MASK *conflictsP,
|
|
|
|
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
|
|
|
*
|
|
|
|
*/
|
1998-06-30 04:33:34 +02:00
|
|
|
LOCKMETHOD
|
|
|
|
LockMethodTableInit(char *tabName,
|
1998-09-01 06:40:42 +02:00
|
|
|
MASK *conflictsP,
|
|
|
|
int *prioP,
|
|
|
|
int numModes)
|
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;
|
|
|
|
int status = TRUE;
|
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
|
|
|
|
1998-06-27 17:47:48 +02:00
|
|
|
/* allocate a string for the shmem index table lookup */
|
1997-09-07 07:04:48 +02:00
|
|
|
shmemName = (char *) palloc((unsigned) (strlen(tabName) + 32));
|
|
|
|
if (!shmemName)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1998-06-30 04:33:34 +02:00
|
|
|
elog(NOTICE, "LockMethodTableInit: couldn't malloc string %s \n", tabName);
|
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
|
|
|
|
|
|
|
/* each lock table has a non-shared header */
|
1998-06-30 04:33:34 +02:00
|
|
|
lockMethodTable = (LOCKMETHODTABLE *) palloc((unsigned) sizeof(LOCKMETHODTABLE));
|
|
|
|
if (!lockMethodTable)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1998-06-30 04:33:34 +02:00
|
|
|
elog(NOTICE, "LockMethodTableInit: couldn't malloc lock table %s\n", tabName);
|
1997-09-07 07:04:48 +02:00
|
|
|
pfree(shmemName);
|
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
|
|
|
|
|
|
|
/* ------------------------
|
|
|
|
* 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 *)
|
|
|
|
ShmemInitStruct(shmemName, (unsigned) sizeof(LOCKMETHODCTL), &found);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1998-06-30 04:33:34 +02:00
|
|
|
if (!lockMethodTable->ctl)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1998-06-30 04:33:34 +02:00
|
|
|
elog(FATAL, "LockMethodTableInit: couldn't initialize %s", tabName);
|
1997-09-07 07:04:48 +02:00
|
|
|
status = FALSE;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
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
|
|
|
|
|
|
|
/* --------------------
|
1998-06-30 04:33:34 +02:00
|
|
|
* other modules refer to the lock table by a lockmethod
|
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
|
|
|
|
|
|
|
/* ----------------------
|
|
|
|
* allocate a hash table for the lock tags. This is used
|
|
|
|
* to find the different locks.
|
|
|
|
* ----------------------
|
|
|
|
*/
|
|
|
|
info.keysize = sizeof(LOCKTAG);
|
|
|
|
info.datasize = sizeof(LOCK);
|
|
|
|
info.hash = tag_hash;
|
|
|
|
hash_flags = (HASH_ELEM | HASH_FUNCTION);
|
|
|
|
|
|
|
|
sprintf(shmemName, "%s (lock hash)", tabName);
|
1998-06-30 04:33:34 +02:00
|
|
|
lockMethodTable->lockHash = (HTAB *) ShmemInitHash(shmemName,
|
1997-09-07 07:04:48 +02:00
|
|
|
INIT_TABLE_SIZE, MAX_TABLE_SIZE,
|
1998-09-01 06:40:42 +02:00
|
|
|
&info, hash_flags);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1998-06-30 04:33:34 +02:00
|
|
|
Assert(lockMethodTable->lockHash->hash == tag_hash);
|
|
|
|
if (!lockMethodTable->lockHash)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1998-06-30 04:33:34 +02:00
|
|
|
elog(FATAL, "LockMethodTableInit: couldn't initialize %s", tabName);
|
1997-09-07 07:04:48 +02:00
|
|
|
status = FALSE;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/* -------------------------
|
|
|
|
* allocate an xid table. When different transactions hold
|
|
|
|
* the same lock, additional information must be saved (locks per tx).
|
|
|
|
* -------------------------
|
|
|
|
*/
|
|
|
|
info.keysize = XID_TAGSIZE;
|
|
|
|
info.datasize = sizeof(XIDLookupEnt);
|
|
|
|
info.hash = tag_hash;
|
|
|
|
hash_flags = (HASH_ELEM | HASH_FUNCTION);
|
|
|
|
|
|
|
|
sprintf(shmemName, "%s (xid hash)", tabName);
|
1998-06-30 04:33:34 +02:00
|
|
|
lockMethodTable->xidHash = (HTAB *) ShmemInitHash(shmemName,
|
1997-09-07 07:04:48 +02:00
|
|
|
INIT_TABLE_SIZE, MAX_TABLE_SIZE,
|
1998-09-01 06:40:42 +02:00
|
|
|
&info, hash_flags);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1998-06-30 04:33:34 +02:00
|
|
|
if (!lockMethodTable->xidHash)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1998-06-30 04:33:34 +02:00
|
|
|
elog(FATAL, "LockMethodTableInit: couldn't initialize %s", tabName);
|
1997-09-07 07:04:48 +02:00
|
|
|
status = FALSE;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
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);
|
|
|
|
|
|
|
|
if (status)
|
1998-09-01 05:29:17 +02:00
|
|
|
return lockMethodTable->ctl->lockmethod;
|
1997-09-07 07:04:48 +02:00
|
|
|
else
|
1998-09-01 05:29:17 +02:00
|
|
|
return INVALID_LOCKMETHOD;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
1998-06-30 04:33:34 +02:00
|
|
|
* LockMethodTableRename -- allocate another lockmethod 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
|
1997-09-07 07:04:48 +02:00
|
|
|
* short term and long term locks.
|
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
|
|
|
|
1998-06-30 04:33:34 +02:00
|
|
|
/* other modules refer to the lock table by a lockmethod */
|
|
|
|
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
|
|
|
*
|
1996-10-11 05:22:59 +02:00
|
|
|
#ifdef USER_LOCKS
|
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
|
|
|
|
* for 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
|
1997-09-07 07:04:48 +02:00
|
|
|
* tag.relId rel oid 0
|
|
|
|
* tag.ItemPointerData.ip_blkid block id lock id2
|
|
|
|
* tag.ItemPointerData.ip_posid tuple offset lock id1
|
|
|
|
* xid.pid 0 backend pid
|
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
|
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-10-11 05:22:59 +02:00
|
|
|
#endif
|
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
|
1998-06-30 04:33:34 +02:00
|
|
|
LockAcquire(LOCKMETHOD lockmethod, LOCKTAG *locktag, LOCKMODE lockmode)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
XIDLookupEnt *result,
|
|
|
|
item;
|
|
|
|
HTAB *xidTable;
|
|
|
|
bool found;
|
|
|
|
LOCK *lock = NULL;
|
|
|
|
SPINLOCK masterLock;
|
1998-09-01 06:40:42 +02:00
|
|
|
LOCKMETHODTABLE *lockMethodTable;
|
1997-09-08 04:41:22 +02:00
|
|
|
int status;
|
1998-08-25 23:20:32 +02:00
|
|
|
TransactionId xid;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1996-10-11 05:22:59 +02:00
|
|
|
#ifdef USER_LOCKS
|
1997-09-08 04:41:22 +02:00
|
|
|
int is_user_lock;
|
1996-10-11 05:22:59 +02:00
|
|
|
|
1998-08-25 23:20:32 +02:00
|
|
|
is_user_lock = (lockmethod == USER_LOCKMETHOD);
|
1997-09-07 07:04:48 +02:00
|
|
|
if (is_user_lock)
|
|
|
|
{
|
1996-10-11 05:22:59 +02:00
|
|
|
#ifdef USER_LOCKS_DEBUG
|
1998-12-15 13:47:01 +01:00
|
|
|
TPRINTF(TRACE_USERLOCKS, "LockAcquire: user lock [%u] %s",
|
|
|
|
locktag->objId.blkno,
|
1998-08-25 23:20:32 +02:00
|
|
|
lock_types[lockmode]);
|
1996-10-11 05:22:59 +02:00
|
|
|
#endif
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
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
|
|
|
|
|
|
|
/* --------------------
|
|
|
|
* if there was nothing else there, complete initialization
|
|
|
|
* --------------------
|
|
|
|
*/
|
|
|
|
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));
|
1998-12-15 13:47:01 +01:00
|
|
|
Assert(lock->tag.objId.blkno == locktag->objId.blkno);
|
1998-08-25 23:20:32 +02:00
|
|
|
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
|
|
|
|
|
|
|
/* ------------------
|
|
|
|
* add an element to the lock queue so that we can clear the
|
|
|
|
* locks at end of transaction.
|
|
|
|
* ------------------
|
|
|
|
*/
|
1998-06-30 04:33:34 +02:00
|
|
|
xidTable = lockMethodTable->xidHash;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/* ------------------
|
|
|
|
* Zero out all of the tag bytes (this clears the padding bytes for long
|
|
|
|
* word alignment and ensures hashing consistency).
|
|
|
|
* ------------------
|
|
|
|
*/
|
1998-02-26 05:46:47 +01:00
|
|
|
MemSet(&item, 0, XID_TAGSIZE); /* must clear padding, needed */
|
1997-09-07 07:04:48 +02:00
|
|
|
item.tag.lock = MAKE_OFFSET(lock);
|
1998-08-25 23:20:32 +02:00
|
|
|
#ifdef USE_XIDTAG_LOCKMETHOD
|
|
|
|
item.tag.lockmethod = lockmethod;
|
1996-07-09 08:22:35 +02:00
|
|
|
#endif
|
1996-10-11 05:22:59 +02:00
|
|
|
#ifdef USER_LOCKS
|
1997-09-07 07:04:48 +02:00
|
|
|
if (is_user_lock)
|
|
|
|
{
|
1998-01-25 06:15:15 +01:00
|
|
|
item.tag.pid = MyProcPid;
|
1998-08-25 23:20:32 +02:00
|
|
|
item.tag.xid = xid = 0;
|
1998-09-01 06:40:42 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
1998-08-25 23:20:32 +02:00
|
|
|
xid = GetCurrentTransactionId();
|
|
|
|
TransactionIdStore(xid, &item.tag.xid);
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
1998-08-25 23:20:32 +02:00
|
|
|
#else
|
|
|
|
xid = GetCurrentTransactionId();
|
|
|
|
TransactionIdStore(xid, &item.tag.xid);
|
1996-10-11 05:22:59 +02:00
|
|
|
#endif
|
|
|
|
|
1998-08-25 23:20:32 +02:00
|
|
|
/*
|
|
|
|
* Find or create an xid entry with this tag
|
|
|
|
*/
|
|
|
|
result = (XIDLookupEnt *) hash_search(xidTable, (Pointer) &item,
|
|
|
|
HASH_ENTER, &found);
|
1997-09-07 07:04:48 +02:00
|
|
|
if (!result)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-07 07:04:48 +02:00
|
|
|
elog(NOTICE, "LockAcquire: xid table corrupted");
|
1998-09-01 05:29:17 +02:00
|
|
|
return STATUS_ERROR;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
1998-08-25 23:20:32 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If not found initialize the new entry
|
|
|
|
*/
|
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
|
|
|
result->nHolding = 0;
|
1998-06-30 04:33:34 +02:00
|
|
|
MemSet((char *) result->holders, 0, sizeof(int) * MAX_LOCKMODES);
|
1998-08-25 23:20:32 +02:00
|
|
|
ProcAddLock(&result->queue);
|
|
|
|
XID_PRINT("LockAcquire: new", result);
|
1998-09-01 06:40:42 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
1998-08-25 23:20:32 +02:00
|
|
|
XID_PRINT("LockAcquire: found", result);
|
|
|
|
Assert((result->nHolding > 0) && (result->holders[lockmode] >= 0));
|
|
|
|
Assert(result->nHolding <= lock->nActive);
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/* ----------------
|
|
|
|
* lock->nholding tells us how many processes have _tried_ to
|
|
|
|
* acquire this lock, Regardless of whether they succeeded or
|
|
|
|
* failed in doing so.
|
|
|
|
* ----------------
|
|
|
|
*/
|
|
|
|
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
|
|
|
|
|
|
|
/* --------------------
|
|
|
|
* If I'm the only one holding a lock, then there
|
|
|
|
* cannot be a conflict. Need to subtract one from the
|
|
|
|
* lock's count since we just bumped the count up by 1
|
|
|
|
* above.
|
|
|
|
* --------------------
|
|
|
|
*/
|
|
|
|
if (result->nHolding == lock->nActive)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1998-06-30 04:33:34 +02:00
|
|
|
result->holders[lockmode]++;
|
1997-09-07 07:04:48 +02:00
|
|
|
result->nHolding++;
|
1998-08-25 23:20:32 +02:00
|
|
|
XID_PRINT("LockAcquire: owning", result);
|
|
|
|
Assert((result->nHolding > 0) && (result->holders[lockmode] > 0));
|
1998-06-30 04:33:34 +02:00
|
|
|
GrantLock(lock, lockmode);
|
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
|
|
|
|
1998-08-25 23:20:32 +02:00
|
|
|
status = LockResolveConflicts(lockmethod, lock, lockmode, xid, result);
|
1997-09-07 07:04:48 +02:00
|
|
|
if (status == STATUS_OK)
|
1998-06-30 04:33:34 +02:00
|
|
|
GrantLock(lock, 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
|
|
|
|
* remove the xid entry and return FALSE without waiting.
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
|
|
|
if (is_user_lock)
|
|
|
|
{
|
|
|
|
if (!result->nHolding)
|
|
|
|
{
|
|
|
|
SHMQueueDelete(&result->queue);
|
1998-08-25 23:20:32 +02:00
|
|
|
result = (XIDLookupEnt *) hash_search(xidTable,
|
1998-08-28 14:08:03 +02:00
|
|
|
(Pointer) result,
|
1998-09-01 06:40:42 +02:00
|
|
|
HASH_REMOVE, &found);
|
|
|
|
if (!result || !found)
|
1998-08-25 23:20:32 +02:00
|
|
|
elog(NOTICE, "LockAcquire: remove xid, table corrupted");
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
1998-09-01 06:40:42 +02:00
|
|
|
else
|
|
|
|
XID_PRINT_AUX("LockAcquire: NHOLDING", result);
|
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
|
|
|
}
|
1996-10-11 05:22:59 +02:00
|
|
|
#endif
|
1998-08-25 23:20:32 +02:00
|
|
|
status = WaitOnLock(lockmethod, lock, lockmode, xid);
|
1998-09-01 06:40:42 +02:00
|
|
|
|
1998-08-25 23:20:32 +02:00
|
|
|
/*
|
1998-09-01 06:40:42 +02:00
|
|
|
* Check the xid entry status, in case something in the ipc
|
|
|
|
* communication doesn't work correctly.
|
1998-08-25 23:20:32 +02:00
|
|
|
*/
|
1998-09-01 06:40:42 +02:00
|
|
|
if (!((result->nHolding > 0) && (result->holders[lockmode] > 0)))
|
|
|
|
{
|
1998-08-25 23:20:32 +02:00
|
|
|
XID_PRINT_AUX("LockAcquire: INCONSISTENT ", result);
|
|
|
|
LOCK_PRINT_AUX("LockAcquire: INCONSISTENT ", lock, lockmode);
|
|
|
|
/* Should we retry ? */
|
1998-09-01 05:29:17 +02:00
|
|
|
return FALSE;
|
1998-08-25 23:20:32 +02:00
|
|
|
}
|
|
|
|
XID_PRINT("LockAcquire: granted", result);
|
|
|
|
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.
|
|
|
|
*
|
1997-09-07 07:04:48 +02:00
|
|
|
* For example, if I am already holding a WRITE_INTENT lock,
|
|
|
|
* there will not be a conflict with my own READ_LOCK. If I
|
|
|
|
* don't consider the intent lock when checking for conflicts,
|
|
|
|
* I find no conflict.
|
1996-07-09 08:22:35 +02:00
|
|
|
* ----------------------------
|
|
|
|
*/
|
|
|
|
int
|
1998-06-30 04:33:34 +02:00
|
|
|
LockResolveConflicts(LOCKMETHOD lockmethod,
|
1997-09-08 23:56:23 +02:00
|
|
|
LOCK *lock,
|
1998-06-30 04:33:34 +02:00
|
|
|
LOCKMODE lockmode,
|
1998-08-25 23:20:32 +02:00
|
|
|
TransactionId xid,
|
|
|
|
XIDLookupEnt *xidentP) /* xident ptr or NULL */
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
XIDLookupEnt *result,
|
|
|
|
item;
|
|
|
|
int *myHolders;
|
1998-06-30 04:33:34 +02:00
|
|
|
int numLockModes;
|
1997-09-08 04:41:22 +02:00
|
|
|
HTAB *xidTable;
|
|
|
|
bool found;
|
|
|
|
int bitmask;
|
|
|
|
int i,
|
|
|
|
tmpMask;
|
1998-09-01 06:40:42 +02:00
|
|
|
|
1998-08-25 23:20:32 +02:00
|
|
|
#ifdef USER_LOCKS
|
|
|
|
int is_user_lock;
|
1998-09-01 06:40:42 +02:00
|
|
|
|
1998-08-25 23:20:32 +02:00
|
|
|
#endif
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1998-06-30 04:33:34 +02:00
|
|
|
numLockModes = LockMethodTable[lockmethod]->ctl->numLockModes;
|
|
|
|
xidTable = LockMethodTable[lockmethod]->xidHash;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1998-09-01 06:40:42 +02:00
|
|
|
if (xidentP)
|
|
|
|
{
|
|
|
|
|
1998-08-25 23:20:32 +02:00
|
|
|
/*
|
|
|
|
* A pointer to the xid entry was supplied from the caller.
|
|
|
|
* Actually only LockAcquire can do it.
|
|
|
|
*/
|
|
|
|
result = xidentP;
|
1998-09-01 06:40:42 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
1998-08-25 23:20:32 +02:00
|
|
|
/* ---------------------
|
|
|
|
* read my own statistics from the xid table. If there
|
|
|
|
* isn't an entry, then we'll just add one.
|
|
|
|
*
|
|
|
|
* Zero out the tag, this clears the padding bytes for long
|
|
|
|
* word alignment and ensures hashing consistency.
|
|
|
|
* ------------------
|
|
|
|
*/
|
|
|
|
MemSet(&item, 0, XID_TAGSIZE);
|
|
|
|
item.tag.lock = MAKE_OFFSET(lock);
|
|
|
|
#ifdef USE_XIDTAG_LOCKMETHOD
|
|
|
|
item.tag.lockmethod = lockmethod;
|
|
|
|
#endif
|
|
|
|
#ifdef USER_LOCKS
|
|
|
|
is_user_lock = (lockmethod == 2);
|
1998-09-01 06:40:42 +02:00
|
|
|
if (is_user_lock)
|
|
|
|
{
|
1998-08-25 23:20:32 +02:00
|
|
|
item.tag.pid = MyProcPid;
|
|
|
|
item.tag.xid = 0;
|
|
|
|
}
|
1998-09-01 06:40:42 +02:00
|
|
|
else
|
|
|
|
TransactionIdStore(xid, &item.tag.xid);
|
1998-08-25 23:20:32 +02:00
|
|
|
#else
|
|
|
|
TransactionIdStore(xid, &item.tag.xid);
|
1996-07-09 08:22:35 +02:00
|
|
|
#endif
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1998-08-25 23:20:32 +02:00
|
|
|
/*
|
|
|
|
* Find or create an xid entry with this tag
|
|
|
|
*/
|
|
|
|
result = (XIDLookupEnt *) hash_search(xidTable, (Pointer) &item,
|
|
|
|
HASH_ENTER, &found);
|
|
|
|
if (!result)
|
|
|
|
{
|
|
|
|
elog(NOTICE, "LockResolveConflicts: xid table corrupted");
|
1998-09-01 05:29:17 +02:00
|
|
|
return STATUS_ERROR;
|
1998-08-25 23:20:32 +02:00
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1998-08-25 23:20:32 +02:00
|
|
|
/*
|
1998-09-01 06:40:42 +02:00
|
|
|
* If not found initialize the new entry. THIS SHOULD NEVER
|
|
|
|
* HAPPEN, if we are trying to resolve a conflict we must already
|
|
|
|
* have allocated an xid entry for this lock. dz 21-11-1997
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
1998-08-25 23:20:32 +02:00
|
|
|
if (!found)
|
|
|
|
{
|
|
|
|
/* ---------------
|
|
|
|
* we're not holding any type of lock yet. Clear
|
|
|
|
* the lock stats.
|
|
|
|
* ---------------
|
|
|
|
*/
|
1998-09-01 06:40:42 +02:00
|
|
|
MemSet(result->holders, 0, numLockModes * sizeof(*(lock->holders)));
|
1998-08-25 23:20:32 +02:00
|
|
|
result->nHolding = 0;
|
|
|
|
XID_PRINT_AUX("LockResolveConflicts: NOT FOUND", result);
|
|
|
|
}
|
1998-09-01 06:40:42 +02:00
|
|
|
else
|
|
|
|
XID_PRINT("LockResolveConflicts: found", result);
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
1998-08-25 23:20:32 +02:00
|
|
|
Assert((result->nHolding >= 0) && (result->holders[lockmode] >= 0));
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1998-08-25 23:20:32 +02:00
|
|
|
/*
|
|
|
|
* We can control runtime this option. Default is lockReadPriority=0
|
|
|
|
*/
|
|
|
|
if (!lockReadPriority)
|
1998-01-23 23:16:48 +01:00
|
|
|
{
|
|
|
|
/* ------------------------
|
|
|
|
* If someone with a greater priority is waiting for the lock,
|
1998-02-26 05:46:47 +01:00
|
|
|
* do not continue and share the lock, even if we can. bjm
|
1998-01-23 23:16:48 +01:00
|
|
|
* ------------------------
|
|
|
|
*/
|
1998-06-30 04:33:34 +02:00
|
|
|
int myprio = LockMethodTable[lockmethod]->ctl->prio[lockmode];
|
1998-02-26 05:46:47 +01:00
|
|
|
PROC_QUEUE *waitQueue = &(lock->waitProcs);
|
|
|
|
PROC *topproc = (PROC *) MAKE_PTR(waitQueue->links.prev);
|
1998-01-23 23:16:48 +01:00
|
|
|
|
1998-09-01 06:40:42 +02:00
|
|
|
if (waitQueue->size && topproc->prio > myprio)
|
|
|
|
{
|
1998-08-25 23:20:32 +02:00
|
|
|
XID_PRINT("LockResolveConflicts: higher priority proc waiting",
|
|
|
|
result);
|
1998-01-23 23:16:48 +01:00
|
|
|
return STATUS_FOUND;
|
1998-08-25 23:20:32 +02:00
|
|
|
}
|
1998-01-23 23:16:48 +01:00
|
|
|
}
|
|
|
|
|
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.
|
|
|
|
* ----------------------------
|
|
|
|
*/
|
1998-06-30 04:33:34 +02:00
|
|
|
if (!(LockMethodTable[lockmethod]->ctl->conflictTab[lockmode] & lock->mask))
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1998-06-30 04:33:34 +02:00
|
|
|
result->holders[lockmode]++;
|
1997-09-07 07:04:48 +02:00
|
|
|
result->nHolding++;
|
1998-08-25 23:20:32 +02:00
|
|
|
XID_PRINT("LockResolveConflicts: no conflict", result);
|
|
|
|
Assert((result->nHolding > 0) && (result->holders[lockmode] > 0));
|
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
|
|
|
|
* that does not reflect our own locks.
|
|
|
|
* ------------------------
|
|
|
|
*/
|
1998-08-25 23:20:32 +02:00
|
|
|
myHolders = result->holders;
|
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.
|
|
|
|
* ------------------------
|
|
|
|
*/
|
1998-06-30 04:33:34 +02:00
|
|
|
if (!(LockMethodTable[lockmethod]->ctl->conflictTab[lockmode] & bitmask))
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-07 07:04:48 +02:00
|
|
|
/* no conflict. Get the lock and go on */
|
1998-06-30 04:33:34 +02:00
|
|
|
result->holders[lockmode]++;
|
1997-09-07 07:04:48 +02:00
|
|
|
result->nHolding++;
|
1998-08-25 23:20:32 +02:00
|
|
|
XID_PRINT("LockResolveConflicts: resolved", result);
|
|
|
|
Assert((result->nHolding > 0) && (result->holders[lockmode] > 0));
|
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
|
|
|
|
1998-08-25 23:20:32 +02:00
|
|
|
XID_PRINT("LockResolveConflicts: conflicting", result);
|
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
|
|
|
/*
|
|
|
|
* GrantLock -- update the lock data structure to show
|
|
|
|
* the new lock holder.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
GrantLock(LOCK *lock, LOCKMODE lockmode)
|
|
|
|
{
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
1997-08-19 23:40:56 +02:00
|
|
|
static int
|
1998-08-25 23:20:32 +02:00
|
|
|
WaitOnLock(LOCKMETHOD lockmethod, LOCK *lock, LOCKMODE lockmode,
|
|
|
|
TransactionId xid)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
PROC_QUEUE *waitQueue = &(lock->waitProcs);
|
1998-09-01 06:40:42 +02:00
|
|
|
LOCKMETHODTABLE *lockMethodTable = LockMethodTable[lockmethod];
|
1998-06-30 04:33:34 +02:00
|
|
|
int prio = lockMethodTable->ctl->prio[lockmode];
|
1998-08-25 23:20:32 +02:00
|
|
|
char old_status[64],
|
|
|
|
new_status[64];
|
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.
|
|
|
|
*/
|
1998-08-25 23:20:32 +02:00
|
|
|
LOCK_PRINT_AUX("WaitOnLock: sleeping on lock", lock, lockmode);
|
|
|
|
strcpy(old_status, PS_STATUS);
|
|
|
|
strcpy(new_status, PS_STATUS);
|
|
|
|
strcat(new_status, " waiting");
|
|
|
|
PS_SET_STATUS(new_status);
|
1997-09-07 07:04:48 +02:00
|
|
|
if (ProcSleep(waitQueue,
|
1998-06-30 04:33:34 +02:00
|
|
|
lockMethodTable->ctl->masterLock,
|
|
|
|
lockmode,
|
1997-09-07 07:04:48 +02:00
|
|
|
prio,
|
1998-08-25 23:20:32 +02:00
|
|
|
lock,
|
|
|
|
xid) != NO_ERROR)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-07 07:04:48 +02:00
|
|
|
/* -------------------
|
1998-08-25 23:20:32 +02:00
|
|
|
* This could have happend as a result of a deadlock,
|
|
|
|
* see HandleDeadLock().
|
|
|
|
* Decrement the lock nHolding and holders fields as
|
|
|
|
* we are no longer waiting on this 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
|
|
|
LOCK_PRINT_AUX("WaitOnLock: aborting on lock", lock, lockmode);
|
|
|
|
Assert((lock->nHolding >= 0) && (lock->holders[lockmode] >= 0));
|
|
|
|
Assert(lock->nActive <= lock->nHolding);
|
1998-06-30 04:33:34 +02:00
|
|
|
SpinRelease(lockMethodTable->ctl->masterLock);
|
1998-01-07 22:07:04 +01:00
|
|
|
elog(ERROR, "WaitOnLock: error on wakeup - Aborting this transaction");
|
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
|
|
|
|
1998-08-25 23:20:32 +02:00
|
|
|
PS_SET_STATUS(old_status);
|
|
|
|
LOCK_PRINT_AUX("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
|
|
|
|
* come along and request the lock).
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
|
|
|
bool
|
1998-06-30 04:33:34 +02:00
|
|
|
LockRelease(LOCKMETHOD lockmethod, LOCKTAG *locktag, LOCKMODE lockmode)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
LOCK *lock = NULL;
|
|
|
|
SPINLOCK masterLock;
|
|
|
|
bool found;
|
1998-09-01 06:40:42 +02:00
|
|
|
LOCKMETHODTABLE *lockMethodTable;
|
1997-09-08 04:41:22 +02:00
|
|
|
XIDLookupEnt *result,
|
|
|
|
item;
|
|
|
|
HTAB *xidTable;
|
1998-08-25 23:20:32 +02:00
|
|
|
TransactionId xid;
|
1997-09-08 04:41:22 +02:00
|
|
|
bool wakeupNeeded = true;
|
1998-08-25 23:20:32 +02:00
|
|
|
int trace_flag;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1996-10-11 05:22:59 +02:00
|
|
|
#ifdef USER_LOCKS
|
1997-09-08 04:41:22 +02:00
|
|
|
int is_user_lock;
|
1996-10-11 05:22:59 +02:00
|
|
|
|
1998-08-25 23:20:32 +02:00
|
|
|
is_user_lock = (lockmethod == USER_LOCKMETHOD);
|
1997-09-07 07:04:48 +02:00
|
|
|
if (is_user_lock)
|
|
|
|
{
|
1998-12-15 13:47:01 +01:00
|
|
|
TPRINTF(TRACE_USERLOCKS, "LockRelease: user lock tag [%u] %d",
|
|
|
|
locktag->objId.blkno,
|
1998-08-25 23:20:32 +02:00
|
|
|
lockmode);
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
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;
|
|
|
|
|
|
|
|
#ifdef USER_LOCKS
|
|
|
|
trace_flag = \
|
|
|
|
(lockmethod == USER_LOCKMETHOD) ? TRACE_USERLOCKS : TRACE_LOCKS;
|
|
|
|
#else
|
|
|
|
trace_flag = TRACE_LOCKS;
|
|
|
|
#endif
|
|
|
|
|
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);
|
1998-08-25 23:20:32 +02:00
|
|
|
#ifdef USER_LOCKS
|
1998-09-01 06:40:42 +02:00
|
|
|
if (is_user_lock)
|
|
|
|
{
|
1998-08-25 23:20:32 +02:00
|
|
|
TPRINTF(TRACE_USERLOCKS, "LockRelease: no lock with this tag");
|
1998-09-01 05:29:17 +02:00
|
|
|
return FALSE;
|
1998-08-25 23:20:32 +02:00
|
|
|
}
|
|
|
|
#endif
|
1997-09-07 07:04:48 +02:00
|
|
|
elog(NOTICE, "LockRelease: locktable lookup failed, no 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
|
|
|
|
|
|
|
|
|
|
|
/* ------------------
|
|
|
|
* Zero out all of the tag bytes (this clears the padding bytes for long
|
|
|
|
* word alignment and ensures hashing consistency).
|
|
|
|
* ------------------
|
|
|
|
*/
|
1997-09-18 22:22:58 +02:00
|
|
|
MemSet(&item, 0, XID_TAGSIZE);
|
1997-09-07 07:04:48 +02:00
|
|
|
item.tag.lock = MAKE_OFFSET(lock);
|
1998-08-25 23:20:32 +02:00
|
|
|
#ifdef USE_XIDTAG_LOCKMETHOD
|
|
|
|
item.tag.lockmethod = lockmethod;
|
1996-07-09 08:22:35 +02:00
|
|
|
#endif
|
1996-10-11 05:22:59 +02:00
|
|
|
#ifdef USER_LOCKS
|
1998-09-01 06:40:42 +02:00
|
|
|
if (is_user_lock)
|
|
|
|
{
|
1998-01-25 06:15:15 +01:00
|
|
|
item.tag.pid = MyProcPid;
|
1998-08-25 23:20:32 +02:00
|
|
|
item.tag.xid = xid = 0;
|
1998-09-01 06:40:42 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
1998-08-25 23:20:32 +02:00
|
|
|
xid = GetCurrentTransactionId();
|
|
|
|
TransactionIdStore(xid, &item.tag.xid);
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
1998-08-25 23:20:32 +02:00
|
|
|
#else
|
|
|
|
xid = GetCurrentTransactionId();
|
|
|
|
TransactionIdStore(xid, &item.tag.xid);
|
1996-10-11 05:22:59 +02:00
|
|
|
#endif
|
|
|
|
|
1998-08-25 23:20:32 +02:00
|
|
|
/*
|
|
|
|
* Find an xid entry with this tag
|
|
|
|
*/
|
|
|
|
xidTable = lockMethodTable->xidHash;
|
|
|
|
result = (XIDLookupEnt *) hash_search(xidTable, (Pointer) &item,
|
|
|
|
HASH_FIND_SAVE, &found);
|
|
|
|
if (!result || !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
|
1998-09-01 06:40:42 +02:00
|
|
|
if (!found && is_user_lock)
|
1998-08-25 23:20:32 +02:00
|
|
|
TPRINTF(TRACE_USERLOCKS, "LockRelease: no lock with this tag");
|
1998-09-01 06:40:42 +02:00
|
|
|
else
|
1996-10-11 05:22:59 +02:00
|
|
|
#endif
|
1998-09-01 06:40:42 +02:00
|
|
|
elog(NOTICE, "LockReplace: xid table corrupted");
|
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
|
|
|
XID_PRINT("LockRelease: found", result);
|
|
|
|
Assert(result->tag.lock == MAKE_OFFSET(lock));
|
|
|
|
|
|
|
|
/*
|
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
|
|
|
*/
|
1998-09-01 06:40:42 +02:00
|
|
|
if (!(result->holders[lockmode] > 0))
|
|
|
|
{
|
1998-08-25 23:20:32 +02:00
|
|
|
SpinRelease(masterLock);
|
|
|
|
XID_PRINT_AUX("LockAcquire: WRONGTYPE", result);
|
|
|
|
elog(NOTICE, "LockRelease: you don't own a lock of type %s",
|
|
|
|
lock_types[lockmode]);
|
|
|
|
Assert(result->holders[lockmode] >= 0);
|
1998-09-01 05:29:17 +02:00
|
|
|
return FALSE;
|
1998-08-25 23:20:32 +02:00
|
|
|
}
|
|
|
|
Assert(result->nHolding > 0);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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
|
|
|
|
|
|
|
/* --------------------------
|
|
|
|
* 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
|
|
|
|
{
|
|
|
|
/* change the conflict mask. No more of this lock type. */
|
|
|
|
lock->mask &= BITS_OFF[lockmode];
|
|
|
|
}
|
|
|
|
|
|
|
|
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,
|
|
|
|
* we just released the last lock.
|
|
|
|
* 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
|
|
|
|
|
|
|
/*
|
|
|
|
* now check to see if I have any private locks. If I do, decrement
|
|
|
|
* the counts associated with them.
|
|
|
|
*/
|
1998-06-30 04:33:34 +02:00
|
|
|
result->holders[lockmode]--;
|
1997-09-07 07:04:48 +02:00
|
|
|
result->nHolding--;
|
1998-08-25 23:20:32 +02:00
|
|
|
XID_PRINT("LockRelease: updated", result);
|
|
|
|
Assert((result->nHolding >= 0) && (result->holders[lockmode] >= 0));
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If this was my last hold on this lock, delete my entry in the XID
|
|
|
|
* table.
|
|
|
|
*/
|
|
|
|
if (!result->nHolding)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1998-09-01 06:40:42 +02:00
|
|
|
if (result->queue.prev == INVALID_OFFSET)
|
1997-09-07 07:04:48 +02:00
|
|
|
elog(NOTICE, "LockRelease: xid.prev == INVALID_OFFSET");
|
1998-09-01 06:40:42 +02:00
|
|
|
if (result->queue.next == INVALID_OFFSET)
|
1997-09-07 07:04:48 +02:00
|
|
|
elog(NOTICE, "LockRelease: xid.next == INVALID_OFFSET");
|
|
|
|
if (result->queue.next != INVALID_OFFSET)
|
|
|
|
SHMQueueDelete(&result->queue);
|
1998-08-25 23:20:32 +02:00
|
|
|
XID_PRINT("LockRelease: deleting", result);
|
|
|
|
result = (XIDLookupEnt *) hash_search(xidTable, (Pointer) &result,
|
|
|
|
HASH_REMOVE_SAVED, &found);
|
|
|
|
if (!result || !found)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-07 07:04:48 +02:00
|
|
|
SpinRelease(masterLock);
|
|
|
|
elog(NOTICE, "LockRelease: remove xid, 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)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-07 07:04:48 +02:00
|
|
|
/* --------------------------
|
|
|
|
* Wake the first waiting process and grant him the lock if it
|
|
|
|
* doesn't conflict. The woken process must record the lock
|
|
|
|
* himself.
|
|
|
|
* --------------------------
|
|
|
|
*/
|
1998-06-30 04:33:34 +02:00
|
|
|
ProcLockWakeup(&(lock->waitProcs), lockmethod, lock);
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
1998-08-25 23:20:32 +02:00
|
|
|
else
|
1998-09-09 20:32:05 +02:00
|
|
|
{
|
|
|
|
if (((LOCKDEBUG(LOCK_LOCKMETHOD(*(lock))) >= 1) \
|
|
|
|
&& (lock->tag.relId >= lockDebugOidMin)) \
|
1999-01-17 21:59:56 +01:00
|
|
|
|| \
|
|
|
|
(lockDebugRelation && (lock->tag.relId == lockDebugRelation)))
|
1998-09-09 20:32:05 +02:00
|
|
|
TPRINTF(TRACE_ALL, "LockRelease: no wakeup needed");
|
|
|
|
}
|
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
|
|
|
/*
|
|
|
|
* LockReleaseAll -- Release all locks in a process lock queue.
|
|
|
|
*/
|
1996-07-09 08:22:35 +02:00
|
|
|
bool
|
1998-06-30 04:33:34 +02:00
|
|
|
LockReleaseAll(LOCKMETHOD lockmethod, SHM_QUEUE *lockQueue)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
PROC_QUEUE *waitQueue;
|
|
|
|
int done;
|
|
|
|
XIDLookupEnt *xidLook = NULL;
|
|
|
|
XIDLookupEnt *tmp = NULL;
|
1998-08-25 23:20:32 +02:00
|
|
|
XIDLookupEnt *result;
|
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;
|
1998-08-25 23:20:32 +02:00
|
|
|
int trace_flag;
|
|
|
|
int xidtag_lockmethod;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1996-10-11 05:22:59 +02:00
|
|
|
#ifdef USER_LOCKS
|
1997-09-08 04:41:22 +02:00
|
|
|
int is_user_lock_table,
|
|
|
|
count,
|
1998-08-25 23:20:32 +02:00
|
|
|
nleft;
|
1996-10-11 05:22:59 +02:00
|
|
|
|
1998-08-25 23:20:32 +02:00
|
|
|
count = nleft = 0;
|
|
|
|
|
|
|
|
is_user_lock_table = (lockmethod == USER_LOCKMETHOD);
|
|
|
|
trace_flag = (lockmethod == 2) ? TRACE_USERLOCKS : TRACE_LOCKS;
|
|
|
|
#else
|
|
|
|
trace_flag = TRACE_LOCKS;
|
1996-10-11 05:22:59 +02:00
|
|
|
#endif
|
1998-08-25 23:20:32 +02:00
|
|
|
TPRINTF(trace_flag, "LockReleaseAll: lockmethod=%d, pid=%d",
|
|
|
|
lockmethod, MyProcPid);
|
1996-10-11 05:22:59 +02:00
|
|
|
|
1998-06-30 04:33:34 +02:00
|
|
|
Assert(lockmethod < NumLockMethods);
|
|
|
|
lockMethodTable = LockMethodTable[lockmethod];
|
1998-09-01 06:40:42 +02:00
|
|
|
if (!lockMethodTable)
|
|
|
|
{
|
1998-08-25 23:20:32 +02:00
|
|
|
elog(NOTICE, "LockAcquire: 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);
|
1997-09-08 23:56:23 +02:00
|
|
|
SHMQueueFirst(lockQueue, (Pointer *) &xidLook, &xidLook->queue);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
for (;;)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1998-09-01 06:40:42 +02:00
|
|
|
|
1998-08-25 23:20:32 +02:00
|
|
|
/*
|
|
|
|
* Sometimes the queue appears to be messed up.
|
|
|
|
*/
|
|
|
|
if (count++ > 1000)
|
|
|
|
{
|
|
|
|
elog(NOTICE, "LockReleaseAll: xid loop detected, giving up");
|
|
|
|
nleft = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
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
|
|
|
|
* ---------------------------
|
|
|
|
*/
|
|
|
|
done = (xidLook->queue.next == end);
|
|
|
|
lock = (LOCK *) MAKE_PTR(xidLook->tag.lock);
|
|
|
|
|
1998-08-25 23:20:32 +02:00
|
|
|
xidtag_lockmethod = XIDENT_LOCKMETHOD(*xidLook);
|
1999-01-17 21:59:56 +01:00
|
|
|
if ((xidtag_lockmethod == lockmethod) && pg_options[trace_flag])
|
1998-09-01 06:40:42 +02:00
|
|
|
{
|
1998-08-25 23:20:32 +02:00
|
|
|
XID_PRINT("LockReleaseAll", xidLook);
|
|
|
|
LOCK_PRINT("LockReleaseAll", lock, 0);
|
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1998-08-25 23:20:32 +02:00
|
|
|
#ifdef USE_XIDTAG_LOCKMETHOD
|
|
|
|
if (xidtag_lockmethod != LOCK_LOCKMETHOD(*lock))
|
|
|
|
elog(NOTICE, "LockReleaseAll: xid/lock method mismatch: %d != %d",
|
|
|
|
xidtag_lockmethod, lock->tag.lockmethod);
|
|
|
|
#endif
|
1998-09-01 06:40:42 +02:00
|
|
|
if ((xidtag_lockmethod != lockmethod) && (trace_flag >= 2))
|
|
|
|
{
|
1998-08-25 23:20:32 +02:00
|
|
|
TPRINTF(trace_flag, "LockReleaseAll: skipping other table");
|
|
|
|
nleft++;
|
|
|
|
goto next_item;
|
1996-10-11 05:22:59 +02:00
|
|
|
}
|
1998-08-25 23:20:32 +02:00
|
|
|
|
|
|
|
Assert(lock->nHolding > 0);
|
|
|
|
Assert(lock->nActive > 0);
|
|
|
|
Assert(lock->nActive <= lock->nHolding);
|
|
|
|
Assert(xidLook->nHolding >= 0);
|
|
|
|
Assert(xidLook->nHolding <= lock->nHolding);
|
|
|
|
|
|
|
|
#ifdef USER_LOCKS
|
1997-09-07 07:04:48 +02:00
|
|
|
if (is_user_lock_table)
|
|
|
|
{
|
|
|
|
if ((xidLook->tag.pid == 0) || (xidLook->tag.xid != 0))
|
|
|
|
{
|
1998-08-25 23:20:32 +02:00
|
|
|
TPRINTF(TRACE_USERLOCKS,
|
|
|
|
"LockReleaseAll: skiping normal lock [%d,%d,%d]",
|
1998-09-01 06:40:42 +02:00
|
|
|
xidLook->tag.lock, xidLook->tag.pid, xidLook->tag.xid);
|
1998-08-25 23:20:32 +02:00
|
|
|
nleft++;
|
1997-09-07 07:04:48 +02:00
|
|
|
goto next_item;
|
|
|
|
}
|
1998-01-25 06:15:15 +01:00
|
|
|
if (xidLook->tag.pid != MyProcPid)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
1998-08-25 23:20:32 +02:00
|
|
|
/* Should never happen */
|
1997-09-07 07:04:48 +02:00
|
|
|
elog(NOTICE,
|
1998-12-15 13:47:01 +01:00
|
|
|
"LockReleaseAll: INVALID PID: [%u] [%d,%d,%d]",
|
|
|
|
lock->tag.objId.blkno,
|
1998-09-01 06:40:42 +02:00
|
|
|
xidLook->tag.lock, xidLook->tag.pid, xidLook->tag.xid);
|
1998-08-25 23:20:32 +02:00
|
|
|
nleft++;
|
1997-09-07 07:04:48 +02:00
|
|
|
goto next_item;
|
|
|
|
}
|
1998-08-25 23:20:32 +02:00
|
|
|
TPRINTF(TRACE_USERLOCKS,
|
1998-12-15 13:47:01 +01:00
|
|
|
"LockReleaseAll: releasing user lock [%u] [%d,%d,%d]",
|
|
|
|
lock->tag.objId.blkno,
|
1998-09-01 06:40:42 +02:00
|
|
|
xidLook->tag.lock, xidLook->tag.pid, xidLook->tag.xid);
|
1996-10-11 05:22:59 +02:00
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
else
|
|
|
|
{
|
1998-09-01 06:40:42 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Can't check xidLook->tag.xid, can be 0 also for normal
|
|
|
|
* locks
|
|
|
|
*/
|
1998-08-25 23:20:32 +02:00
|
|
|
if (xidLook->tag.pid != 0)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
1998-08-25 23:20:32 +02:00
|
|
|
TPRINTF(TRACE_LOCKS,
|
1998-12-15 13:47:01 +01:00
|
|
|
"LockReleaseAll: skiping user lock [%u] [%d,%d,%d]",
|
|
|
|
lock->tag.objId.blkno,
|
1998-09-01 06:40:42 +02:00
|
|
|
xidLook->tag.lock, xidLook->tag.pid, xidLook->tag.xid);
|
1998-08-25 23:20:32 +02:00
|
|
|
nleft++;
|
1997-09-07 07:04:48 +02:00
|
|
|
goto next_item;
|
|
|
|
}
|
|
|
|
}
|
1996-10-11 05:22:59 +02:00
|
|
|
#endif
|
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
/* ------------------
|
|
|
|
* fix the general lock stats
|
|
|
|
* ------------------
|
|
|
|
*/
|
|
|
|
if (lock->nHolding != xidLook->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
|
|
|
{
|
1998-08-25 23:20:32 +02:00
|
|
|
Assert(xidLook->holders[i] >= 0);
|
1997-09-07 07:04:48 +02:00
|
|
|
lock->holders[i] -= xidLook->holders[i];
|
|
|
|
lock->activeHolders[i] -= xidLook->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];
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
1998-08-25 23:20:32 +02:00
|
|
|
lock->nHolding -= xidLook->nHolding;
|
|
|
|
lock->nActive -= xidLook->nHolding;
|
|
|
|
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);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Remove the xid from the process lock queue
|
|
|
|
*/
|
|
|
|
SHMQueueDelete(&xidLook->queue);
|
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
/* ----------------
|
|
|
|
* always remove the xidLookup entry, we're done with it now
|
|
|
|
* ----------------
|
|
|
|
*/
|
1998-08-25 23:20:32 +02:00
|
|
|
|
|
|
|
XID_PRINT("LockReleaseAll: deleting", xidLook);
|
|
|
|
result = (XIDLookupEnt *) hash_search(lockMethodTable->xidHash,
|
1998-09-01 06:40:42 +02:00
|
|
|
(Pointer) xidLook,
|
1998-08-25 23:20:32 +02:00
|
|
|
HASH_REMOVE,
|
|
|
|
&found);
|
|
|
|
if (!result || !found)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-07 07:04:48 +02:00
|
|
|
SpinRelease(masterLock);
|
|
|
|
elog(NOTICE, "LockReleaseAll: xid 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
|
|
|
}
|
|
|
|
}
|
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
|
|
|
/* --------------------
|
|
|
|
* Wake the first waiting process and grant him the lock if it
|
|
|
|
* doesn't conflict. The woken process must record the lock
|
|
|
|
* him/herself.
|
|
|
|
* --------------------
|
|
|
|
*/
|
|
|
|
waitQueue = &(lock->waitProcs);
|
1998-06-30 04:33:34 +02:00
|
|
|
ProcLockWakeup(waitQueue, lockmethod, lock);
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1996-10-11 05:22:59 +02:00
|
|
|
#ifdef USER_LOCKS
|
1997-09-07 07:04:48 +02:00
|
|
|
next_item:
|
1996-10-11 05:22:59 +02:00
|
|
|
#endif
|
1997-09-07 07:04:48 +02:00
|
|
|
if (done)
|
|
|
|
break;
|
1997-09-08 23:56:23 +02:00
|
|
|
SHMQueueFirst(&xidLook->queue, (Pointer *) &tmp, &tmp->queue);
|
1997-09-07 07:04:48 +02:00
|
|
|
xidLook = tmp;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
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)
|
|
|
|
{
|
1998-08-25 23:20:32 +02:00
|
|
|
TPRINTF(trace_flag, "LockReleaseAll: reinitializing lockQueue");
|
1997-09-07 07:04:48 +02:00
|
|
|
SHMQueueInit(lockQueue);
|
1998-08-25 23:20:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
SpinRelease(masterLock);
|
|
|
|
TPRINTF(trace_flag, "LockReleaseAll: done");
|
|
|
|
|
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;
|
1999-02-21 02:41:55 +01:00
|
|
|
int nLockEnts = NLOCKENTS(maxBackends);
|
1997-09-08 04:41:22 +02:00
|
|
|
int nLockBuckets,
|
|
|
|
nLockSegs;
|
|
|
|
int nXidBuckets,
|
|
|
|
nXidSegs;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1999-02-21 02:41:55 +01:00
|
|
|
nLockBuckets = 1 << (int) my_log2((nLockEnts - 1) / DEF_FFACTOR + 1);
|
1997-09-07 07:04:48 +02:00
|
|
|
nLockSegs = 1 << (int) my_log2((nLockBuckets - 1) / DEF_SEGSIZE + 1);
|
|
|
|
|
|
|
|
nXidBuckets = 1 << (int) my_log2((NLOCKS_PER_XACT - 1) / DEF_FFACTOR + 1);
|
|
|
|
nXidSegs = 1 << (int) my_log2((nLockBuckets - 1) / DEF_SEGSIZE + 1);
|
|
|
|
|
1999-02-21 02:41:55 +01:00
|
|
|
size += MAXALIGN(maxBackends * sizeof(PROC)); /* each MyProc */
|
|
|
|
size += MAXALIGN(maxBackends * sizeof(LOCKMETHODCTL)); /* each
|
1998-09-01 06:40:42 +02:00
|
|
|
* lockMethodTable->ctl */
|
1997-09-07 07:04:48 +02:00
|
|
|
size += MAXALIGN(sizeof(PROC_HDR)); /* ProcGlobal */
|
|
|
|
|
1999-02-21 02:41:55 +01:00
|
|
|
size += MAXALIGN(my_log2(nLockEnts) * sizeof(void *));
|
1997-09-07 07:04:48 +02:00
|
|
|
size += MAXALIGN(sizeof(HHDR));
|
|
|
|
size += nLockSegs * MAXALIGN(DEF_SEGSIZE * sizeof(SEGMENT));
|
1999-02-21 02:41:55 +01:00
|
|
|
size += nLockEnts * /* XXX not multiple of BUCKET_ALLOC_INCR? */
|
1997-09-07 07:04:48 +02:00
|
|
|
(MAXALIGN(sizeof(BUCKET_INDEX)) +
|
|
|
|
MAXALIGN(sizeof(LOCK))); /* contains hash key */
|
|
|
|
|
1999-02-21 02:41:55 +01:00
|
|
|
size += MAXALIGN(my_log2(maxBackends) * sizeof(void *));
|
1997-09-07 07:04:48 +02:00
|
|
|
size += MAXALIGN(sizeof(HHDR));
|
|
|
|
size += nXidSegs * MAXALIGN(DEF_SEGSIZE * sizeof(SEGMENT));
|
1999-02-21 02:41:55 +01:00
|
|
|
size += maxBackends * /* XXX not multiple of BUCKET_ALLOC_INCR? */
|
1997-09-07 07:04:48 +02:00
|
|
|
(MAXALIGN(sizeof(BUCKET_INDEX)) +
|
|
|
|
MAXALIGN(sizeof(XIDLookupEnt))); /* contains hash key */
|
|
|
|
|
|
|
|
return size;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* -----------------
|
|
|
|
* Boolean function to determine current locking status
|
|
|
|
* -----------------
|
|
|
|
*/
|
|
|
|
bool
|
|
|
|
LockingDisabled()
|
|
|
|
{
|
1997-09-07 07:04:48 +02:00
|
|
|
return LockingIsDisabled;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
1997-02-12 06:25:13 +01: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
|
|
|
|
DeadLockCheck(SHM_QUEUE *lockQueue, LOCK *findlock, bool skip_check)
|
|
|
|
{
|
|
|
|
int done;
|
|
|
|
XIDLookupEnt *xidLook = NULL;
|
|
|
|
XIDLookupEnt *tmp = NULL;
|
|
|
|
SHMEM_OFFSET end = MAKE_OFFSET(lockQueue);
|
|
|
|
LOCK *lock;
|
|
|
|
|
1998-09-01 06:40:42 +02:00
|
|
|
LOCKMETHODTABLE *lockMethodTable;
|
1998-01-28 03:29:40 +01:00
|
|
|
XIDLookupEnt *result,
|
|
|
|
item;
|
|
|
|
HTAB *xidTable;
|
|
|
|
bool found;
|
|
|
|
|
1999-02-19 07:06:39 +01:00
|
|
|
static PROC *checked_procs[MAXBACKENDS];
|
1998-02-26 05:46:47 +01:00
|
|
|
static int nprocs;
|
|
|
|
static bool MyNHolding;
|
|
|
|
|
1998-01-28 03:29:40 +01:00
|
|
|
/* initialize at start of recursion */
|
1998-01-27 16:35:30 +01:00
|
|
|
if (skip_check)
|
|
|
|
{
|
|
|
|
checked_procs[0] = MyProc;
|
|
|
|
nprocs = 1;
|
1998-01-28 03:29:40 +01:00
|
|
|
|
1998-08-25 23:20:32 +02:00
|
|
|
lockMethodTable = LockMethodTable[DEFAULT_LOCKMETHOD];
|
1998-06-30 04:33:34 +02:00
|
|
|
xidTable = lockMethodTable->xidHash;
|
1998-01-28 03:29:40 +01:00
|
|
|
|
|
|
|
MemSet(&item, 0, XID_TAGSIZE);
|
|
|
|
TransactionIdStore(MyProc->xid, &item.tag.xid);
|
|
|
|
item.tag.lock = MAKE_OFFSET(findlock);
|
1999-02-21 04:49:55 +01:00
|
|
|
#ifdef NOT_USED
|
1998-01-28 03:29:40 +01:00
|
|
|
item.tag.pid = pid;
|
|
|
|
#endif
|
1998-02-26 05:46:47 +01:00
|
|
|
|
1998-01-28 07:52:58 +01:00
|
|
|
if (!(result = (XIDLookupEnt *)
|
1998-02-26 05:46:47 +01:00
|
|
|
hash_search(xidTable, (Pointer) &item, HASH_FIND, &found)) || !found)
|
1998-01-28 07:52:58 +01:00
|
|
|
{
|
|
|
|
elog(NOTICE, "LockAcquire: xid table corrupted");
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
MyNHolding = result->nHolding;
|
1998-02-26 05:46:47 +01:00
|
|
|
}
|
1998-01-27 04:00:43 +01:00
|
|
|
if (SHMQueueEmpty(lockQueue))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
SHMQueueFirst(lockQueue, (Pointer *) &xidLook, &xidLook->queue);
|
|
|
|
|
|
|
|
XID_PRINT("DeadLockCheck", xidLook);
|
|
|
|
|
|
|
|
for (;;)
|
|
|
|
{
|
|
|
|
/* ---------------------------
|
|
|
|
* 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
|
|
|
|
* ---------------------------
|
|
|
|
*/
|
|
|
|
done = (xidLook->queue.next == end);
|
|
|
|
lock = (LOCK *) MAKE_PTR(xidLook->tag.lock);
|
|
|
|
|
1998-08-25 23:20:32 +02:00
|
|
|
LOCK_PRINT("DeadLockCheck", lock, 0);
|
1998-01-27 04:00:43 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* This is our only check to see if we found the lock we want.
|
|
|
|
*
|
1998-02-26 05:46:47 +01:00
|
|
|
* The lock we are waiting for is already in MyProc->lockQueue so we
|
|
|
|
* need to skip it here. We are trying to find it in someone
|
|
|
|
* else's lockQueue.
|
1998-01-27 04:00:43 +01:00
|
|
|
*/
|
|
|
|
if (lock == findlock && !skip_check)
|
|
|
|
return true;
|
1998-01-27 16:35:30 +01:00
|
|
|
|
1998-02-26 05:46:47 +01:00
|
|
|
{
|
|
|
|
PROC_QUEUE *waitQueue = &(lock->waitProcs);
|
|
|
|
PROC *proc;
|
1998-01-27 04:00:43 +01:00
|
|
|
int i;
|
1998-01-27 16:35:30 +01:00
|
|
|
int j;
|
1998-02-26 05:46:47 +01:00
|
|
|
|
1998-01-27 04:00:43 +01:00
|
|
|
proc = (PROC *) MAKE_PTR(waitQueue->links.prev);
|
|
|
|
for (i = 0; i < waitQueue->size; i++)
|
|
|
|
{
|
1998-01-28 03:29:40 +01:00
|
|
|
if (proc != MyProc &&
|
|
|
|
lock == findlock && /* skip_check also true */
|
|
|
|
MyNHolding) /* I already hold some lock on it */
|
|
|
|
{
|
1998-02-26 05:46:47 +01:00
|
|
|
|
1998-01-28 03:29:40 +01:00
|
|
|
/*
|
1998-02-26 05:46:47 +01:00
|
|
|
* For findlock's wait queue, we are interested in
|
|
|
|
* procs who are blocked waiting for a write-lock on
|
|
|
|
* the table we are waiting on, and already hold a
|
|
|
|
* lock on it. We first check to see if there is an
|
|
|
|
* escalation deadlock, where we hold a readlock and
|
|
|
|
* want a writelock, and someone else holds readlock
|
|
|
|
* on the same table, and wants a writelock.
|
1998-01-28 03:29:40 +01:00
|
|
|
*
|
1998-02-26 05:46:47 +01:00
|
|
|
* Basically, the test is, "Do we both hold some lock on
|
|
|
|
* findlock, and we are both waiting in the lock
|
|
|
|
* queue?"
|
1998-01-28 03:29:40 +01:00
|
|
|
*/
|
|
|
|
|
|
|
|
Assert(skip_check);
|
1998-12-15 13:47:01 +01:00
|
|
|
Assert(MyProc->prio >= 2);
|
1998-02-26 05:46:47 +01:00
|
|
|
|
1998-06-30 04:33:34 +02:00
|
|
|
lockMethodTable = LockMethodTable[1];
|
|
|
|
xidTable = lockMethodTable->xidHash;
|
1998-01-28 03:29:40 +01:00
|
|
|
|
|
|
|
MemSet(&item, 0, XID_TAGSIZE);
|
|
|
|
TransactionIdStore(proc->xid, &item.tag.xid);
|
|
|
|
item.tag.lock = MAKE_OFFSET(findlock);
|
1999-02-21 04:49:55 +01:00
|
|
|
#ifdef NOT_USED
|
1998-01-28 03:29:40 +01:00
|
|
|
item.tag.pid = pid;
|
|
|
|
#endif
|
1998-02-26 05:46:47 +01:00
|
|
|
|
1998-01-28 07:52:58 +01:00
|
|
|
if (!(result = (XIDLookupEnt *)
|
1998-02-26 05:46:47 +01:00
|
|
|
hash_search(xidTable, (Pointer) &item, HASH_FIND, &found)) || !found)
|
1998-01-28 03:29:40 +01:00
|
|
|
{
|
1998-01-28 07:52:58 +01:00
|
|
|
elog(NOTICE, "LockAcquire: xid table corrupted");
|
|
|
|
return true;
|
1998-01-28 03:29:40 +01:00
|
|
|
}
|
1998-01-28 07:52:58 +01:00
|
|
|
if (result->nHolding)
|
|
|
|
return true;
|
1998-01-28 03:29:40 +01:00
|
|
|
}
|
1998-02-26 05:46:47 +01:00
|
|
|
|
1998-01-28 03:29:40 +01:00
|
|
|
/*
|
1998-02-26 05:46:47 +01:00
|
|
|
* No sense in looking at the wait queue of the lock we
|
|
|
|
* are looking for. If lock == findlock, and I got here,
|
|
|
|
* skip_check must be true too.
|
1998-01-28 03:29:40 +01:00
|
|
|
*/
|
|
|
|
if (lock != findlock)
|
1998-01-27 04:00:43 +01:00
|
|
|
{
|
1998-01-28 03:29:40 +01:00
|
|
|
for (j = 0; j < nprocs; j++)
|
|
|
|
if (checked_procs[j] == proc)
|
|
|
|
break;
|
|
|
|
if (j >= nprocs && lock != findlock)
|
|
|
|
{
|
1999-02-21 02:41:55 +01:00
|
|
|
Assert(nprocs < MAXBACKENDS);
|
1998-01-28 03:29:40 +01:00
|
|
|
checked_procs[nprocs++] = proc;
|
1998-02-26 05:46:47 +01:00
|
|
|
|
1998-01-28 03:29:40 +01:00
|
|
|
/*
|
1998-02-26 05:46:47 +01:00
|
|
|
* For non-MyProc entries, we are looking only
|
|
|
|
* waiters, not necessarily people who already
|
|
|
|
* hold locks and are waiting. Now we check for
|
|
|
|
* cases where we have two or more tables in a
|
|
|
|
* deadlock. We do this by continuing to search
|
|
|
|
* for someone holding a lock
|
1998-01-28 03:29:40 +01:00
|
|
|
*/
|
|
|
|
if (DeadLockCheck(&(proc->lockQueue), findlock, false))
|
|
|
|
return true;
|
|
|
|
}
|
1998-01-27 04:00:43 +01:00
|
|
|
}
|
|
|
|
proc = (PROC *) MAKE_PTR(proc->links.prev);
|
|
|
|
}
|
|
|
|
}
|
1998-02-26 05:46:47 +01:00
|
|
|
|
1998-01-27 04:00:43 +01:00
|
|
|
if (done)
|
|
|
|
break;
|
|
|
|
SHMQueueFirst(&xidLook->queue, (Pointer *) &tmp, &tmp->queue);
|
|
|
|
xidLook = tmp;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* if we got here, no deadlock */
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
1998-10-08 20:30:52 +02:00
|
|
|
#ifdef NOT_USED
|
1998-08-25 23:20:32 +02:00
|
|
|
/*
|
|
|
|
* Return an array with the pids of all processes owning a lock.
|
|
|
|
* This works only for user locks because normal locks have no
|
|
|
|
* pid information in the corresponding XIDLookupEnt.
|
|
|
|
*/
|
1998-09-01 06:40:42 +02:00
|
|
|
ArrayType *
|
1998-08-25 23:20:32 +02:00
|
|
|
LockOwners(LOCKMETHOD lockmethod, LOCKTAG *locktag)
|
|
|
|
{
|
|
|
|
XIDLookupEnt *xidLook = NULL;
|
|
|
|
SPINLOCK masterLock;
|
1998-09-01 06:40:42 +02:00
|
|
|
LOCK *lock;
|
1998-08-25 23:20:32 +02:00
|
|
|
SHMEM_OFFSET lock_offset;
|
1998-09-01 06:40:42 +02:00
|
|
|
int count = 0;
|
|
|
|
LOCKMETHODTABLE *lockMethodTable;
|
|
|
|
HTAB *xidTable;
|
1998-08-25 23:20:32 +02:00
|
|
|
bool found;
|
|
|
|
int ndims,
|
|
|
|
nitems,
|
|
|
|
hdrlen,
|
|
|
|
size;
|
|
|
|
int lbounds[1],
|
|
|
|
hbounds[1];
|
1998-09-01 06:40:42 +02:00
|
|
|
ArrayType *array;
|
|
|
|
int *data_ptr;
|
1998-08-25 23:20:32 +02:00
|
|
|
|
|
|
|
/* Assume that no one will modify the result */
|
1998-09-01 06:40:42 +02:00
|
|
|
static int empty_array[] = {20, 1, 0, 0, 0};
|
1998-08-25 23:20:32 +02:00
|
|
|
|
|
|
|
#ifdef USER_LOCKS
|
|
|
|
int is_user_lock;
|
|
|
|
|
|
|
|
is_user_lock = (lockmethod == USER_LOCKMETHOD);
|
|
|
|
if (is_user_lock)
|
|
|
|
{
|
1998-12-15 13:47:01 +01:00
|
|
|
TPRINTF(TRACE_USERLOCKS, "LockOwners: user lock tag [%u]",
|
|
|
|
locktag->objId.blkno;,
|
1998-08-25 23:20:32 +02:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* This must be changed when short term locks will be used */
|
|
|
|
locktag->lockmethod = lockmethod;
|
|
|
|
|
|
|
|
Assert((lockmethod >= MIN_LOCKMETHOD) && (lockmethod < NumLockMethods));
|
|
|
|
lockMethodTable = LockMethodTable[lockmethod];
|
|
|
|
if (!lockMethodTable)
|
|
|
|
{
|
|
|
|
elog(NOTICE, "lockMethodTable is null in LockOwners");
|
1998-09-01 05:29:17 +02:00
|
|
|
return (ArrayType *) &empty_array;
|
1998-08-25 23:20:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (LockingIsDisabled)
|
1998-09-01 05:29:17 +02:00
|
|
|
return (ArrayType *) &empty_array;
|
1998-08-25 23:20:32 +02:00
|
|
|
|
|
|
|
masterLock = lockMethodTable->ctl->masterLock;
|
|
|
|
SpinAcquire(masterLock);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Find a lock with this tag
|
|
|
|
*/
|
|
|
|
Assert(lockMethodTable->lockHash->hash == tag_hash);
|
|
|
|
lock = (LOCK *) hash_search(lockMethodTable->lockHash, (Pointer) locktag,
|
|
|
|
HASH_FIND, &found);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* let the caller print its own error message, too. Do not elog(WARN).
|
|
|
|
*/
|
|
|
|
if (!lock)
|
|
|
|
{
|
|
|
|
SpinRelease(masterLock);
|
|
|
|
elog(NOTICE, "LockOwners: locktable corrupted");
|
1998-09-01 05:29:17 +02:00
|
|
|
return (ArrayType *) &empty_array;
|
1998-08-25 23:20:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!found)
|
|
|
|
{
|
|
|
|
SpinRelease(masterLock);
|
|
|
|
#ifdef USER_LOCKS
|
1998-09-01 06:40:42 +02:00
|
|
|
if (is_user_lock)
|
|
|
|
{
|
1998-08-25 23:20:32 +02:00
|
|
|
TPRINTF(TRACE_USERLOCKS, "LockOwners: no lock with this tag");
|
1998-09-01 05:29:17 +02:00
|
|
|
return (ArrayType *) &empty_array;
|
1998-08-25 23:20:32 +02:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
elog(NOTICE, "LockOwners: locktable lookup failed, no lock");
|
1998-09-01 05:29:17 +02:00
|
|
|
return (ArrayType *) &empty_array;
|
1998-08-25 23:20:32 +02:00
|
|
|
}
|
|
|
|
LOCK_PRINT("LockOwners: found", lock, 0);
|
|
|
|
Assert((lock->nHolding > 0) && (lock->nActive > 0));
|
|
|
|
Assert(lock->nActive <= lock->nHolding);
|
|
|
|
lock_offset = MAKE_OFFSET(lock);
|
|
|
|
|
|
|
|
/* Construct a 1-dimensional array */
|
|
|
|
ndims = 1;
|
|
|
|
hdrlen = ARR_OVERHEAD(ndims);
|
|
|
|
lbounds[0] = 0;
|
|
|
|
hbounds[0] = lock->nActive;
|
|
|
|
size = hdrlen + sizeof(int) * hbounds[0];
|
|
|
|
array = (ArrayType *) palloc(size);
|
|
|
|
MemSet(array, 0, size);
|
|
|
|
memmove((char *) array, (char *) &size, sizeof(int));
|
|
|
|
memmove((char *) ARR_NDIM_PTR(array), (char *) &ndims, sizeof(int));
|
|
|
|
memmove((char *) ARR_DIMS(array), (char *) hbounds, ndims * sizeof(int));
|
|
|
|
memmove((char *) ARR_LBOUND(array), (char *) lbounds, ndims * sizeof(int));
|
|
|
|
SET_LO_FLAG(false, array);
|
|
|
|
data_ptr = (int *) ARR_DATA_PTR(array);
|
|
|
|
|
|
|
|
xidTable = lockMethodTable->xidHash;
|
|
|
|
hash_seq(NULL);
|
|
|
|
nitems = 0;
|
1998-09-01 06:40:42 +02:00
|
|
|
while ((xidLook = (XIDLookupEnt *) hash_seq(xidTable)) &&
|
|
|
|
(xidLook != (XIDLookupEnt *) TRUE))
|
|
|
|
{
|
|
|
|
if (count++ > 1000)
|
|
|
|
{
|
|
|
|
elog(NOTICE, "LockOwners: possible loop, giving up");
|
1998-08-25 23:20:32 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
1998-09-01 06:40:42 +02:00
|
|
|
if (xidLook->tag.pid == 0)
|
|
|
|
{
|
1998-08-25 23:20:32 +02:00
|
|
|
XID_PRINT("LockOwners: no pid", xidLook);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
1998-09-01 06:40:42 +02:00
|
|
|
if (!xidLook->tag.lock)
|
|
|
|
{
|
1998-08-25 23:20:32 +02:00
|
|
|
XID_PRINT("LockOwners: NULL LOCK", xidLook);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
1998-09-01 06:40:42 +02:00
|
|
|
if (xidLook->tag.lock != lock_offset)
|
|
|
|
{
|
1998-08-25 23:20:32 +02:00
|
|
|
XID_PRINT("LockOwners: different lock", xidLook);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
1998-09-01 06:40:42 +02:00
|
|
|
if (LOCK_LOCKMETHOD(*lock) != lockmethod)
|
|
|
|
{
|
1998-08-25 23:20:32 +02:00
|
|
|
XID_PRINT("LockOwners: other table", xidLook);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
1998-09-01 06:40:42 +02:00
|
|
|
if (xidLook->nHolding <= 0)
|
|
|
|
{
|
1998-08-25 23:20:32 +02:00
|
|
|
XID_PRINT("LockOwners: not holding", xidLook);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
1998-09-01 06:40:42 +02:00
|
|
|
if (nitems >= hbounds[0])
|
|
|
|
{
|
|
|
|
elog(NOTICE, "LockOwners: array size exceeded");
|
1998-08-25 23:20:32 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
1998-09-01 06:40:42 +02:00
|
|
|
* Check that the holding process is still alive by sending him an
|
|
|
|
* unused (ignored) signal. If the kill fails the process is not
|
|
|
|
* alive.
|
1998-08-25 23:20:32 +02:00
|
|
|
*/
|
|
|
|
if ((xidLook->tag.pid != MyProcPid) \
|
1998-09-01 06:40:42 +02:00
|
|
|
&&(kill(xidLook->tag.pid, SIGCHLD)) != 0)
|
1998-08-25 23:20:32 +02:00
|
|
|
{
|
|
|
|
/* Return a negative pid to signal that process is dead */
|
1998-09-01 06:40:42 +02:00
|
|
|
data_ptr[nitems++] = -(xidLook->tag.pid);
|
1998-08-25 23:20:32 +02:00
|
|
|
XID_PRINT("LockOwners: not alive", xidLook);
|
|
|
|
/* XXX - TODO: remove this entry and update lock stats */
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Found a process holding the lock */
|
|
|
|
XID_PRINT("LockOwners: holding", xidLook);
|
|
|
|
data_ptr[nitems++] = xidLook->tag.pid;
|
|
|
|
}
|
|
|
|
|
|
|
|
SpinRelease(masterLock);
|
|
|
|
|
|
|
|
/* Adjust the actual size of the array */
|
|
|
|
hbounds[0] = nitems;
|
|
|
|
size = hdrlen + sizeof(int) * hbounds[0];
|
|
|
|
memmove((char *) array, (char *) &size, sizeof(int));
|
|
|
|
memmove((char *) ARR_DIMS(array), (char *) hbounds, ndims * sizeof(int));
|
|
|
|
|
1998-09-01 05:29:17 +02:00
|
|
|
return array;
|
1998-08-25 23:20:32 +02:00
|
|
|
}
|
1998-10-08 20:30:52 +02:00
|
|
|
#endif
|
1998-08-25 23:20:32 +02:00
|
|
|
|
1997-02-12 06:25:13 +01:00
|
|
|
#ifdef DEADLOCK_DEBUG
|
|
|
|
/*
|
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
|
|
|
|
DumpLocks()
|
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
SHMEM_OFFSET location;
|
|
|
|
PROC *proc;
|
|
|
|
SHM_QUEUE *lockQueue;
|
|
|
|
int done;
|
|
|
|
XIDLookupEnt *xidLook = NULL;
|
|
|
|
XIDLookupEnt *tmp = NULL;
|
|
|
|
SHMEM_OFFSET end;
|
|
|
|
SPINLOCK masterLock;
|
1998-06-30 04:33:34 +02:00
|
|
|
int numLockModes;
|
1997-09-08 04:41:22 +02:00
|
|
|
LOCK *lock;
|
1998-08-25 23:20:32 +02:00
|
|
|
int count = 0;
|
|
|
|
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;
|
|
|
|
|
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;
|
|
|
|
|
1998-06-30 04:33:34 +02:00
|
|
|
numLockModes = lockMethodTable->ctl->numLockModes;
|
|
|
|
masterLock = lockMethodTable->ctl->masterLock;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
if (SHMQueueEmpty(lockQueue))
|
|
|
|
return;
|
|
|
|
|
1997-09-08 23:56:23 +02:00
|
|
|
SHMQueueFirst(lockQueue, (Pointer *) &xidLook, &xidLook->queue);
|
1997-09-07 07:04:48 +02:00
|
|
|
end = MAKE_OFFSET(lockQueue);
|
|
|
|
|
1998-09-01 06:40:42 +02:00
|
|
|
if (MyProc->waitLock)
|
1998-08-25 23:20:32 +02:00
|
|
|
LOCK_PRINT_AUX("DumpLocks: waiting on", MyProc->waitLock, 0);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1998-08-25 23:20:32 +02:00
|
|
|
for (;;)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
1998-08-25 23:20:32 +02:00
|
|
|
if (count++ > 2000)
|
|
|
|
{
|
|
|
|
elog(NOTICE, "DumpLocks: xid loop detected, giving up");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
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
|
|
|
|
* ---------------------------
|
|
|
|
*/
|
|
|
|
done = (xidLook->queue.next == end);
|
|
|
|
lock = (LOCK *) MAKE_PTR(xidLook->tag.lock);
|
|
|
|
|
1998-08-25 23:20:32 +02:00
|
|
|
XID_PRINT_AUX("DumpLocks", xidLook);
|
|
|
|
LOCK_PRINT_AUX("DumpLocks", lock, 0);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
if (done)
|
|
|
|
break;
|
1998-08-25 23:20:32 +02:00
|
|
|
|
1997-09-08 23:56:23 +02:00
|
|
|
SHMQueueFirst(&xidLook->queue, (Pointer *) &tmp, &tmp->queue);
|
1997-09-07 07:04:48 +02:00
|
|
|
xidLook = tmp;
|
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
|
|
|
|
DumpAllLocks()
|
|
|
|
{
|
|
|
|
SHMEM_OFFSET location;
|
1998-09-01 06:40:42 +02:00
|
|
|
PROC *proc;
|
1998-08-25 23:20:32 +02:00
|
|
|
XIDLookupEnt *xidLook = NULL;
|
1998-09-01 06:40:42 +02:00
|
|
|
LOCK *lock;
|
|
|
|
int pid;
|
|
|
|
int count = 0;
|
1998-08-25 23:20:32 +02:00
|
|
|
int lockmethod = DEFAULT_LOCKMETHOD;
|
1998-09-01 06:40:42 +02:00
|
|
|
LOCKMETHODTABLE *lockMethodTable;
|
|
|
|
HTAB *xidTable;
|
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;
|
|
|
|
|
|
|
|
xidTable = lockMethodTable->xidHash;
|
|
|
|
|
1998-09-01 06:40:42 +02:00
|
|
|
if (MyProc->waitLock)
|
|
|
|
LOCK_PRINT_AUX("DumpAllLocks: waiting on", MyProc->waitLock, 0);
|
1998-08-25 23:20:32 +02:00
|
|
|
|
|
|
|
hash_seq(NULL);
|
1998-09-01 06:40:42 +02:00
|
|
|
while ((xidLook = (XIDLookupEnt *) hash_seq(xidTable)) &&
|
|
|
|
(xidLook != (XIDLookupEnt *) TRUE))
|
|
|
|
{
|
1998-08-25 23:20:32 +02:00
|
|
|
XID_PRINT_AUX("DumpAllLocks", xidLook);
|
|
|
|
|
1998-09-01 06:40:42 +02:00
|
|
|
if (xidLook->tag.lock)
|
|
|
|
{
|
1998-08-25 23:20:32 +02:00
|
|
|
lock = (LOCK *) MAKE_PTR(xidLook->tag.lock);
|
|
|
|
LOCK_PRINT_AUX("DumpAllLocks", lock, 0);
|
|
|
|
}
|
1998-09-01 06:40:42 +02:00
|
|
|
else
|
|
|
|
elog(DEBUG, "DumpAllLocks: xidLook->tag.lock = NULL");
|
1998-08-25 23:20:32 +02:00
|
|
|
|
1998-09-01 06:40:42 +02:00
|
|
|
if (count++ > 2000)
|
|
|
|
{
|
|
|
|
elog(NOTICE, "DumpAllLocks: possible loop, giving up");
|
1998-08-25 23:20:32 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
1998-09-01 06:40:42 +02:00
|
|
|
|
1997-02-12 06:25:13 +01:00
|
|
|
#endif
|