diff options
author | Marc G. Fournier <scrappy@hub.org> | 1996-07-09 06:22:35 +0000 |
---|---|---|
committer | Marc G. Fournier <scrappy@hub.org> | 1996-07-09 06:22:35 +0000 |
commit | d31084e9d1118b25fd16580d9d8c2924b5740dff (patch) | |
tree | 3179e66307d54df9c7b966543550e601eb55e668 /src/backend/storage/lmgr/multi.c | |
download | postgresql-PG95-1_01.tar.gz postgresql-PG95-1_01.zip |
Postgres95 1.01 Distribution - Virgin SourcesPG95-1_01
Diffstat (limited to 'src/backend/storage/lmgr/multi.c')
-rw-r--r-- | src/backend/storage/lmgr/multi.c | 415 |
1 files changed, 415 insertions, 0 deletions
diff --git a/src/backend/storage/lmgr/multi.c b/src/backend/storage/lmgr/multi.c new file mode 100644 index 00000000000..c1702d18cb8 --- /dev/null +++ b/src/backend/storage/lmgr/multi.c @@ -0,0 +1,415 @@ +/*------------------------------------------------------------------------- + * + * multi.c-- + * multi level lock table manager + * + * Standard multi-level lock manager as per the Gray paper + * (at least, that is what it is supposed to be). We implement + * three levels -- RELN, PAGE, TUPLE. Tuple is actually TID + * a physical record pointer. It isn't an object id. + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/storage/lmgr/Attic/multi.c,v 1.1.1.1 1996/07/09 06:21:56 scrappy Exp $ + * + * NOTES: + * (1) The lock.c module assumes that the caller here is doing + * two phase locking. + * + *------------------------------------------------------------------------- + */ +#include <stdio.h> +#include <string.h> +#include "storage/lmgr.h" +#include "storage/multilev.h" + +#include "utils/rel.h" +#include "utils/elog.h" +#include "miscadmin.h" /* MyDatabaseId */ + + +/* + * INTENT indicates to higher level that a lower level lock has been + * set. For example, a write lock on a tuple conflicts with a write + * lock on a relation. This conflict is detected as a WRITE_INTENT/ + * WRITE conflict between the tuple's intent lock and the relation's + * write lock. + */ +static int MultiConflicts[] = { + (int)NULL, + /* All reads and writes at any level conflict with a write lock */ + (1 << WRITE_LOCK)|(1 << WRITE_INTENT)|(1 << READ_LOCK)|(1 << READ_INTENT), + /* read locks conflict with write locks at curr and lower levels */ + (1 << WRITE_LOCK)| (1 << WRITE_INTENT), + /* write intent locks */ + (1 << READ_LOCK) | (1 << WRITE_LOCK), + /* read intent locks*/ + (1 << WRITE_LOCK), + /* extend locks for archive storage manager conflict only w/extend locks */ + (1 << EXTEND_LOCK) +}; + +/* + * write locks have higher priority than read locks and extend locks. May + * want to treat INTENT locks differently. + */ +static int MultiPrios[] = { + (int)NULL, + 2, + 1, + 2, + 1, + 1 +}; + +/* + * Lock table identifier for this lock table. The multi-level + * lock table is ONE lock table, not three. + */ +LockTableId MultiTableId = (LockTableId)NULL; +LockTableId ShortTermTableId = (LockTableId)NULL; + +/* + * Create the lock table described by MultiConflicts and Multiprio. + */ +LockTableId +InitMultiLevelLockm() +{ + int tableId; + + /* ----------------------- + * If we're already initialized just return the table id. + * ----------------------- + */ + if (MultiTableId) + return MultiTableId; + + tableId = LockTabInit("LockTable", MultiConflicts, MultiPrios, 5); + MultiTableId = tableId; + if (! (MultiTableId)) { + elog(WARN,"InitMultiLockm: couldnt initialize lock table"); + } + /* ----------------------- + * No short term lock table for now. -Jeff 15 July 1991 + * + * ShortTermTableId = LockTabRename(tableId); + * if (! (ShortTermTableId)) { + * elog(WARN,"InitMultiLockm: couldnt rename lock table"); + * } + * ----------------------- + */ + return MultiTableId; +} + +/* + * MultiLockReln -- lock a relation + * + * Returns: TRUE if the lock can be set, FALSE otherwise. + */ +bool +MultiLockReln(LockInfo linfo, LOCKT lockt) +{ + LOCKTAG tag; + + /* LOCKTAG has two bytes of padding, unfortunately. The + * hash function will return miss if the padding bytes aren't + * zero'd. + */ + memset(&tag,0,sizeof(tag)); + tag.relId = linfo->lRelId.relId; + tag.dbId = linfo->lRelId.dbId; + return(MultiAcquire(MultiTableId, &tag, lockt, RELN_LEVEL)); +} + +/* + * MultiLockTuple -- Lock the TID associated with a tuple + * + * Returns: TRUE if lock is set, FALSE otherwise. + * + * Side Effects: causes intention level locks to be set + * at the page and relation level. + */ +bool +MultiLockTuple(LockInfo linfo, ItemPointer tidPtr, LOCKT lockt) +{ + LOCKTAG tag; + + /* LOCKTAG has two bytes of padding, unfortunately. The + * hash function will return miss if the padding bytes aren't + * zero'd. + */ + memset(&tag,0,sizeof(tag)); + + tag.relId = linfo->lRelId.relId; + tag.dbId = linfo->lRelId.dbId; + + /* not locking any valid Tuple, just the page */ + tag.tupleId = *tidPtr; + return(MultiAcquire(MultiTableId, &tag, lockt, TUPLE_LEVEL)); +} + +/* + * same as above at page level + */ +bool +MultiLockPage(LockInfo linfo, ItemPointer tidPtr, LOCKT lockt) +{ + LOCKTAG tag; + + /* LOCKTAG has two bytes of padding, unfortunately. The + * hash function will return miss if the padding bytes aren't + * zero'd. + */ + memset(&tag,0,sizeof(tag)); + + + /* ---------------------------- + * Now we want to set the page offset to be invalid + * and lock the block. There is some confusion here as to what + * a page is. In Postgres a page is an 8k block, however this + * block may be partitioned into many subpages which are sometimes + * also called pages. The term is overloaded, so don't be fooled + * when we say lock the page we mean the 8k block. -Jeff 16 July 1991 + * ---------------------------- + */ + tag.relId = linfo->lRelId.relId; + tag.dbId = linfo->lRelId.dbId; + BlockIdCopy(&(tag.tupleId.ip_blkid), &(tidPtr->ip_blkid)); + return(MultiAcquire(MultiTableId, &tag, lockt, PAGE_LEVEL)); +} + +/* + * MultiAcquire -- acquire multi level lock at requested level + * + * Returns: TRUE if lock is set, FALSE if not + * Side Effects: + */ +bool +MultiAcquire(LockTableId tableId, + LOCKTAG *tag, + LOCKT lockt, + LOCK_LEVEL level) +{ + LOCKT locks[N_LEVELS]; + int i,status; + LOCKTAG xxTag, *tmpTag = &xxTag; + int retStatus = TRUE; + + /* + * Three levels implemented. If we set a low level (e.g. Tuple) + * lock, we must set INTENT locks on the higher levels. The + * intent lock detects conflicts between the low level lock + * and an existing high level lock. For example, setting a + * write lock on a tuple in a relation is disallowed if there + * is an existing read lock on the entire relation. The + * write lock would set a WRITE + INTENT lock on the relation + * and that lock would conflict with the read. + */ + switch (level) { + case RELN_LEVEL: + locks[0] = lockt; + locks[1] = NO_LOCK; + locks[2] = NO_LOCK; + break; + case PAGE_LEVEL: + locks[0] = lockt + INTENT; + locks[1] = lockt; + locks[2] = NO_LOCK; + break; + case TUPLE_LEVEL: + locks[0] = lockt + INTENT; + locks[1] = lockt + INTENT; + locks[2] = lockt; + break; + default: + elog(WARN,"MultiAcquire: bad lock level"); + return(FALSE); + } + + /* + * construct a new tag as we go. Always loop through all levels, + * but if we arent' seting a low level lock, locks[i] is set to + * NO_LOCK for the lower levels. Always start from the highest + * level and go to the lowest level. + */ + memset(tmpTag,0,sizeof(*tmpTag)); + tmpTag->relId = tag->relId; + tmpTag->dbId = tag->dbId; + + for (i=0;i<N_LEVELS;i++) { + if (locks[i] != NO_LOCK) { + switch (i) { + case RELN_LEVEL: + /* ------------- + * Set the block # and offset to invalid + * ------------- + */ + BlockIdSet(&(tmpTag->tupleId.ip_blkid), InvalidBlockNumber); + tmpTag->tupleId.ip_posid = InvalidOffsetNumber; + break; + case PAGE_LEVEL: + /* ------------- + * Copy the block #, set the offset to invalid + * ------------- + */ + BlockIdCopy(&(tmpTag->tupleId.ip_blkid), + &(tag->tupleId.ip_blkid)); + tmpTag->tupleId.ip_posid = InvalidOffsetNumber; + break; + case TUPLE_LEVEL: + /* -------------- + * Copy the entire tuple id. + * -------------- + */ + ItemPointerCopy(&tmpTag->tupleId, &tag->tupleId); + break; + } + + status = LockAcquire(tableId, tmpTag, locks[i]); + if (! status) { + /* failed for some reason. Before returning we have + * to release all of the locks we just acquired. + * MultiRelease(xx,xx,xx, i) means release starting from + * the last level lock we successfully acquired + */ + retStatus = FALSE; + (void) MultiRelease(tableId, tag, lockt, i); + /* now leave the loop. Don't try for any more locks */ + break; + } + } + } + return(retStatus); +} + +/* ------------------ + * Release a page in the multi-level lock table + * ------------------ + */ +bool +MultiReleasePage(LockInfo linfo, ItemPointer tidPtr, LOCKT lockt) +{ + LOCKTAG tag; + + /* ------------------ + * LOCKTAG has two bytes of padding, unfortunately. The + * hash function will return miss if the padding bytes aren't + * zero'd. + * ------------------ + */ + memset(&tag, 0,sizeof(LOCKTAG)); + + tag.relId = linfo->lRelId.relId; + tag.dbId = linfo->lRelId.dbId; + BlockIdCopy(&(tag.tupleId.ip_blkid), &(tidPtr->ip_blkid)); + + return (MultiRelease(MultiTableId, &tag, lockt, PAGE_LEVEL)); +} + +/* ------------------ + * Release a relation in the multi-level lock table + * ------------------ + */ +bool +MultiReleaseReln(LockInfo linfo, LOCKT lockt) +{ + LOCKTAG tag; + + /* ------------------ + * LOCKTAG has two bytes of padding, unfortunately. The + * hash function will return miss if the padding bytes aren't + * zero'd. + * ------------------ + */ + memset(&tag, 0, sizeof(LOCKTAG)); + tag.relId = linfo->lRelId.relId; + tag.dbId = linfo->lRelId.dbId; + + return (MultiRelease(MultiTableId, &tag, lockt, RELN_LEVEL)); +} + +/* + * MultiRelease -- release a multi-level lock + * + * Returns: TRUE if successful, FALSE otherwise. + */ +bool +MultiRelease(LockTableId tableId, + LOCKTAG *tag, + LOCKT lockt, + LOCK_LEVEL level) +{ + LOCKT locks[N_LEVELS]; + int i,status; + LOCKTAG xxTag, *tmpTag = &xxTag; + + /* + * same level scheme as MultiAcquire(). + */ + switch (level) { + case RELN_LEVEL: + locks[0] = lockt; + locks[1] = NO_LOCK; + locks[2] = NO_LOCK; + break; + case PAGE_LEVEL: + locks[0] = lockt + INTENT; + locks[1] = lockt; + locks[2] = NO_LOCK; + break; + case TUPLE_LEVEL: + locks[0] = lockt + INTENT; + locks[1] = lockt + INTENT; + locks[2] = lockt; + break; + default: + elog(WARN,"MultiRelease: bad lockt"); + } + + /* + * again, construct the tag on the fly. This time, however, + * we release the locks in the REVERSE order -- from lowest + * level to highest level. + * + * Must zero out the tag to set padding byes to zero and ensure + * hashing consistency. + */ + memset(tmpTag, 0, sizeof(*tmpTag)); + tmpTag->relId = tag->relId; + tmpTag->dbId = tag->dbId; + + for (i=(N_LEVELS-1); i>=0; i--) { + if (locks[i] != NO_LOCK) { + switch (i) { + case RELN_LEVEL: + /* ------------- + * Set the block # and offset to invalid + * ------------- + */ + BlockIdSet(&(tmpTag->tupleId.ip_blkid), InvalidBlockNumber); + tmpTag->tupleId.ip_posid = InvalidOffsetNumber; + break; + case PAGE_LEVEL: + /* ------------- + * Copy the block #, set the offset to invalid + * ------------- + */ + BlockIdCopy(&(tmpTag->tupleId.ip_blkid), + &(tag->tupleId.ip_blkid)); + tmpTag->tupleId.ip_posid = InvalidOffsetNumber; + break; + case TUPLE_LEVEL: + ItemPointerCopy(&tmpTag->tupleId, &tag->tupleId); + break; + } + status = LockRelease(tableId, tmpTag, locks[i]); + if (! status) { + elog(WARN,"MultiRelease: couldn't release after error"); + } + } + } + /* shouldn't reach here */ + return false; +} |