$Header: /cvsroot/pgsql/src/backend/storage/lmgr/README,v 1.5 2001/01/16 06:11:34 tgl Exp $

There are two fundamental lock structures: the per-lockable-object LOCK
struct, and the per-lock-holder HOLDER struct.  A LOCK object exists
for each lockable object that currently has locks held or requested on it.
A HOLDER struct exists for each transaction that is holding or requesting
lock(s) on each LOCK object.

Lock methods describe the overall locking behavior.  Currently there are
two lock methods: DEFAULT and USER.  (USER locks are non-blocking.)

Lock modes describe the type of the lock (read/write or shared/exclusive). 
See src/tools/backend/index.html and src/include/storage/lock.h for more
details.

---------------------------------------------------------------------------

The lock manager's LOCK:

tag -
    The key fields that are used for hashing locks in the shared memory
    lock hash table.  This is declared as a separate struct to ensure that
    we always zero out the correct number of bytes.  It is critical that
    any alignment-padding bytes the compiler might insert in the struct
    be zeroed out, else the hash computation will be random.

    tag.relId -
	Uniquely identifies the relation that the lock corresponds to.
    
    tag.dbId -
	Uniquely identifies the database in which the relation lives.  If
	this is a shared system relation (e.g. pg_user) the dbId should be
	set to 0.

    tag.tupleId -
	Uniquely identifies the block/page within the relation and the
	tuple within the block.  If we are setting a table level lock
	both the blockId and tupleId (in an item pointer this is called
	the position) are set to invalid, if it is a page level lock the
	blockId is valid, while the tupleId is still invalid.  Finally if
	this is a tuple level lock (we currently never do this) then both
	the blockId and tupleId are set to valid specifications.  This is
	how we get the appearance of a multi-level lock table while using
	only a single table (see Gray's paper on 2 phase locking if
	you are puzzled about how multi-level lock tables work).

grantMask -
    This bitmask indicates what types of locks are currently held on the
    given lockable object.  It is used (against the lock table's conflict
    table) to determine if a new lock request will conflict with existing
    lock types held.  Conficts are determined by bitwise AND operations
    between the grantMask and the conflict table entry for the requested
    lock type.  Bit i of grantMask is 1 if and only if granted[i] > 0.

waitMask -
    This bitmask shows the types of locks being waited for.  Bit i of waitMask
    is 1 if and only if requested[i] > granted[i].

waitProcs -
    This is a shared memory queue of all process structures corresponding to
    a backend that is waiting (sleeping) until another backend releases this
    lock.  The process structure holds the information needed to determine
    if it should be woken up when this lock is released.

nRequested -
    Keeps a count of how many times this lock has been attempted to be
    acquired.  The count includes attempts by processes which were put
    to sleep due to conflicts.  It also counts the same backend twice
    if, for example, a backend process first acquires a read and then
    acquires a write, or acquires a read lock twice.

requested -
    Keeps a count of how many locks of each type have been attempted.  Only
    elements 1 through MAX_LOCKMODES-1 are used as they correspond to the lock
    type defined constants.  Summing the values of requested[] should come out
    equal to nRequested.

nGranted -
    Keeps count of how many times this lock has been successfully acquired.
    This count does not include attempts that are waiting due to conflicts,
    but can count the same backend twice (e.g. a read then a write -- since
    its the same transaction this won't cause a conflict).

granted -
    Keeps count of how many locks of each type are currently held.  Once again
    only elements 1 through MAX_LOCKMODES-1 are used (0 is not).  Also, like
    requested, summing the values of granted should total to the value
    of nGranted.

We should always have 0 <= nGranted <= nRequested, and
0 <= granted[i] <= requested[i] for each i.  If the request counts go to
zero, the lock object is no longer needed and can be freed.

---------------------------------------------------------------------------

The lock manager's HOLDER:

tag -
    The key fields that are used for hashing entries in the shared memory
    holder hash table.  This is declared as a separate struct to ensure that
    we always zero out the correct number of bytes.

    tag.lock
        SHMEM offset of the LOCK object this holder is for.

    tag.pid
        PID of backend process that owns this holder.

    tag.xid
        XID of transaction this holder is for, or InvalidTransactionId
        if the holder is for session-level locking.

    Note that this structure will support multiple transactions running
    concurrently in one backend, which may be handy if we someday decide
    to support nested transactions.  Currently, the XID field is only needed
    to distinguish per-transaction locks from session locks.  User locks
    are always session locks, and we also use session locks for multi-
    transaction operations like VACUUM.

holding -
    The number of successfully acquired locks of each type for this holder.
    This should be <= the corresponding granted[] value of the lock object!

nHolding -
    Sum of the holding[] array.

queue -
    List link for shared memory queue of all the HOLDER objects for the
    same backend.
