/*-------------------------------------------------------------------------
 *
 * lock.h
 *
 *
 *
 * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 * $Id: lock.h,v 1.36 2000/01/26 05:58:33 momjian Exp $
 *
 *-------------------------------------------------------------------------
 */
#ifndef LOCK_H_
#define LOCK_H_

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

extern SPINLOCK LockMgrLock;
typedef int LOCKMASK;

#define INIT_TABLE_SIZE			100
#define MAX_TABLE_SIZE			1000


/* ----------------------
 * 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 locks acquired in a transaction
 *                   (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 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


typedef struct LTAG
{
	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;

#define TAGSIZE (sizeof(LOCKTAG))
#define LOCKTAG_LOCKMETHOD(locktag) ((locktag).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;

/*
 * lockHash -- hash table on lock Ids,
 * xidHash -- hash on xid and lockId in case
 *		multiple processes are holding the lock
 * ctl - control structure described above.
 */
typedef struct LOCKMETHODTABLE
{
	HTAB	   *lockHash;
	HTAB	   *xidHash;
	LOCKMETHODCTL *ctl;
} LOCKMETHODTABLE;

/* -----------------------
 * A transaction never conflicts with its own locks.  Hence, if
 * multiple transactions hold non-conflicting locks on the same
 * data, private per-transaction information must be stored in the
 * XID table.  The tag is XID + shared memory lock address so that
 * all locks can use the same XID table.  The private information
 * we store is the number of locks of each type (holders) and the
 * total number of locks (nHolding) held by the transaction.
 *
 * NOTE:
 * There were some problems with the fact that currently TransactionIdData
 * is a 5 byte entity and compilers long word aligning of structure fields.
 * If the 3 byte padding is put in front of the actual xid data then the
 * hash function (which uses XID_TAGSIZE when deciding how many bytes of a
 * struct to look at for the key) might only see the last two bytes of the xid.
 *
 * Clearly this is not good since its likely that these bytes will be the
 * same for many transactions and hence they will share the same entry in
 * hash table causing the entry to be corrupted.  For this long-winded
 * reason I have put the tag in a struct of its own to ensure that the
 * XID_TAGSIZE is computed correctly.  It used to be sizeof (SHMEM_OFFSET) +
 * sizeof(TransactionIdData) which != sizeof(XIDTAG).
 *
 * Finally since the hash function will now look at all 12 bytes of the tag
 * the padding bytes MUST be zero'd before use in hash_search() as they
 * will have random values otherwise.  Jeff 22 July 1991.
 * -----------------------
 */

typedef struct XIDTAG
{
	SHMEM_OFFSET lock;
	int			pid;
	TransactionId xid;
#ifdef USE_XIDTAG_LOCKMETHOD
	uint16		lockmethod;		/* for debug or consistency checking */
#endif
} XIDTAG;

#ifdef USE_XIDTAG_LOCKMETHOD
#define XIDTAG_LOCKMETHOD(xidtag) ((xidtag).lockmethod)
#else
#define XIDTAG_LOCKMETHOD(xidtag) \
		(((LOCK*) MAKE_PTR((xidtag).lock))->tag.lockmethod)
#endif

typedef struct XIDLookupEnt
{
	/* tag */
	XIDTAG		tag;

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

#define SHMEM_XIDTAB_KEYSIZE  sizeof(XIDTAG)
#define SHMEM_XIDTAB_DATASIZE (sizeof(XIDLookupEnt) - SHMEM_XIDTAB_KEYSIZE)

#define XID_TAGSIZE (sizeof(XIDTAG))
#define XIDENT_LOCKMETHOD(xident) (XIDTAG_LOCKMETHOD((xident).tag))

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


/*
 * 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) (LOCKTAG_LOCKMETHOD((lock).tag))

#define LockGetLock_nHolders(l) l->nHolders
#ifdef NOT_USED
#define LockDecrWaitHolders(lock, lockmode) \
( \
  lock->nHolding--, \
  lock->holders[lockmode]-- \
)
#endif
#define LockLockTable() SpinAcquire(LockMgrLock);
#define UnlockLockTable() SpinRelease(LockMgrLock);


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

#ifdef DEADLOCK_DEBUG
extern void DumpLocks(void);
extern void DumpAllLocks(void);

#endif

#endif	 /* LOCK_H */
