/*-------------------------------------------------------------------------
 *
 * lock.h
 *	  POSTGRES low-level lock mechanism
 *
 *
 * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 * $Id: lock.h,v 1.40 2000/12/22 00:51:54 tgl Exp $
 *
 *-------------------------------------------------------------------------
 */
#ifndef LOCK_H_
#define LOCK_H_

#include "storage/ipc.h"
#include "storage/itemptr.h"
#include "storage/shmem.h"


/* originally in procq.h */
typedef struct PROC_QUEUE
{
	SHM_QUEUE	links;
	int			size;
} PROC_QUEUE;

/* struct proc is declared in storage/proc.h, but must forward-reference it */
typedef struct proc PROC;


extern SPINLOCK LockMgrLock;

#ifdef LOCK_DEBUG
extern int  Trace_lock_oidmin;
extern bool Trace_locks;
extern bool Trace_userlocks;
extern int  Trace_lock_table;
extern bool Debug_deadlocks;
#endif /* LOCK_DEBUG */


/* ----------------------
 * The following defines are used to estimate how much shared
 * memory the lock manager is going to require.
 * See LockShmemSize() in lock.c.
 *
 * NLOCKS_PER_XACT - The number of unique objects locked in a transaction
 *					 (this should be configurable!)
 * NLOCKENTS - The maximum number of lock entries in the lock table.
 * ----------------------
 */
#define NLOCKS_PER_XACT			64
#define NLOCKENTS(maxBackends)	(NLOCKS_PER_XACT*(maxBackends))

typedef int LOCKMASK;

typedef int LOCKMODE;
typedef int LOCKMETHOD;

/* MAX_LOCKMODES cannot be larger than the bits in LOCKMASK */
#define MAX_LOCKMODES	8

/*
 * MAX_LOCK_METHODS corresponds to the number of spin locks allocated in
 * CreateSpinLocks() or the number of shared memory locations allocated
 * for lock table spin locks in the case of machines with TAS instructions.
 */
#define MAX_LOCK_METHODS	3

#define INVALID_TABLEID		0

#define INVALID_LOCKMETHOD	INVALID_TABLEID
#define DEFAULT_LOCKMETHOD	1
#define USER_LOCKMETHOD		2
#define MIN_LOCKMETHOD		DEFAULT_LOCKMETHOD


/*
 * This is the control structure for a lock table.	It
 * lives in shared memory:
 *
 * lockmethod -- the handle used by the lock table's clients to
 *		refer to the type of lock table being used.
 *
 * numLockModes -- number of lock types (READ,WRITE,etc) that
 *		are defined on this lock table
 *
 * conflictTab -- this is an array of bitmasks showing lock
 *		type conflicts. conflictTab[i] is a mask with the j-th bit
 *		turned on if lock types i and j conflict.
 *
 * prio -- each lockmode has a priority, so, for example, waiting
 *		writers can be given priority over readers (to avoid
 *		starvation).
 *
 * masterlock -- synchronizes access to the table
 */
typedef struct LOCKMETHODCTL
{
	LOCKMETHOD	lockmethod;
	int			numLockModes;
	int			conflictTab[MAX_LOCKMODES];
	int			prio[MAX_LOCKMODES];
	SPINLOCK	masterLock;
} LOCKMETHODCTL;

/*
 * Non-shared header for a lock table.
 *
 * lockHash -- hash table holding per-locked-object lock information
 * holderHash -- hash table holding per-lock-holder lock information
 * ctl - shared control structure described above.
 */
typedef struct LOCKMETHODTABLE
{
	HTAB	   *lockHash;
	HTAB	   *holderHash;
	LOCKMETHODCTL *ctl;
} LOCKMETHODTABLE;


/*
 * LOCKTAG is the key information needed to look up a LOCK item in the
 * lock hashtable.  A LOCKTAG value uniquely identifies a lockable object.
 */
typedef struct LOCKTAG
{
	Oid			relId;
	Oid			dbId;
	union
	{
		BlockNumber blkno;
		TransactionId xid;
	}			objId;

	/*
	 * offnum should be part of objId.tupleId above, but would increase
	 * sizeof(LOCKTAG) and so moved here; currently used by userlocks
	 * only.
	 */
	OffsetNumber offnum;

	uint16		lockmethod;		/* needed by userlocks */
} LOCKTAG;


/*
 * Per-locked-object lock information:
 *
 * tag -- uniquely identifies the object being locked
 * mask -- union of the conflict masks of all lock types
 *		currently held on this object.
 * waitProcs -- queue of processes waiting for this lock
 * holders -- count of each lock type currently held on the
 *		lock.
 * nHolding -- total locks of all types.
 */
typedef struct LOCK
{
	/* hash key */
	LOCKTAG		tag;

	/* data */
	int			mask;
	int			waitMask;
	PROC_QUEUE	waitProcs;
	int			holders[MAX_LOCKMODES];
	int			nHolding;
	int			activeHolders[MAX_LOCKMODES];
	int			nActive;
} LOCK;

#define SHMEM_LOCKTAB_KEYSIZE  sizeof(LOCKTAG)
#define SHMEM_LOCKTAB_DATASIZE (sizeof(LOCK) - SHMEM_LOCKTAB_KEYSIZE)

#define LOCK_LOCKMETHOD(lock) ((lock).tag.lockmethod)


/*
 * We may have several different transactions holding or awaiting locks
 * on the same lockable object.  We need to store some per-holder information
 * for each such holder (or would-be holder).
 *
 * HOLDERTAG is the key information needed to look up a HOLDER item in the
 * holder hashtable.  A HOLDERTAG value uniquely identifies a lock holder.
 *
 * There are two possible kinds of holder tags: a transaction (identified
 * both by the PID of the backend running it, and the xact's own ID) and
 * a session (identified by backend PID, with xid = InvalidTransactionId).
 *
 * Currently, session holders are used for user locks and for cross-xact
 * locks obtained for VACUUM.  We assume that a session lock never conflicts
 * with per-transaction locks obtained by the same backend.
 */
typedef struct HOLDERTAG
{
	SHMEM_OFFSET lock;			/* link to per-lockable-object information */
	int			pid;			/* PID of backend */
	TransactionId xid;			/* xact ID, or InvalidTransactionId */
} HOLDERTAG;

typedef struct HOLDER
{
	/* tag */
	HOLDERTAG	tag;

	/* data */
	int			holders[MAX_LOCKMODES];
	int			nHolding;
	SHM_QUEUE	queue;
} HOLDER;

#define SHMEM_HOLDERTAB_KEYSIZE  sizeof(HOLDERTAG)
#define SHMEM_HOLDERTAB_DATASIZE (sizeof(HOLDER) - SHMEM_HOLDERTAB_KEYSIZE)

#define HOLDER_LOCKMETHOD(holder) \
		(((LOCK *) MAKE_PTR((holder).tag.lock))->tag.lockmethod)



#define LockLockTable() SpinAcquire(LockMgrLock)
#define UnlockLockTable() SpinRelease(LockMgrLock)


/*
 * function prototypes
 */
extern void InitLocks(void);
extern void LockDisable(bool status);
extern bool LockingDisabled(void);
extern LOCKMETHOD LockMethodTableInit(char *tabName, LOCKMASK *conflictsP,
					int *prioP, int numModes, int maxBackends);
extern LOCKMETHOD LockMethodTableRename(LOCKMETHOD lockmethod);
extern bool LockAcquire(LOCKMETHOD lockmethod, LOCKTAG *locktag,
						TransactionId xid, LOCKMODE lockmode);
extern bool LockRelease(LOCKMETHOD lockmethod, LOCKTAG *locktag,
						TransactionId xid, LOCKMODE lockmode);
extern bool LockReleaseAll(LOCKMETHOD lockmethod, PROC *proc,
						   bool allxids, TransactionId xid);
extern int LockResolveConflicts(LOCKMETHOD lockmethod, LOCKMODE lockmode,
								LOCK *lock, HOLDER *holder, PROC *proc,
								int *myHolders);
extern void GrantLock(LOCK *lock, HOLDER *holder, LOCKMODE lockmode);
extern int	LockShmemSize(int maxBackends);
extern bool DeadLockCheck(PROC *thisProc, LOCK *findlock);

#ifdef LOCK_DEBUG
extern void DumpLocks(void);
extern void DumpAllLocks(void);
#endif

#endif	 /* LOCK_H */
