1 /*-------------------------------------------------------------------------
4 * multi level lock table manager
6 * Standard multi-level lock manager as per the Gray paper
7 * (at least, that is what it is supposed to be). We implement
8 * three levels -- RELN, PAGE, TUPLE. Tuple is actually TID
9 * a physical record pointer. It isn't an object id.
11 * Copyright (c) 1994, Regents of the University of California
15 * $Header: /cvsroot/pgsql/src/backend/storage/lmgr/Attic/multi.c,v 1.30 1999/07/15 20:32:27 momjian Exp $
18 * (1) The lock.c module assumes that the caller here is doing
21 *-------------------------------------------------------------------------
26 #include "storage/lmgr.h"
27 #include "storage/multilev.h"
29 #include "utils/rel.h"
30 #include "miscadmin.h" /* MyDatabaseId */
32 static bool MultiAcquire(LOCKMETHOD lockmethod, LOCKTAG *tag,
33 LOCKMODE lockmode, PG_LOCK_LEVEL level);
34 static bool MultiRelease(LOCKMETHOD lockmethod, LOCKTAG *tag,
35 LOCKMODE lockmode, PG_LOCK_LEVEL level);
38 * INTENT indicates to higher level that a lower level lock has been
39 * set. For example, a write lock on a tuple conflicts with a write
40 * lock on a relation. This conflict is detected as a WRITE_INTENT/
41 * WRITE conflict between the tuple's intent lock and the relation's
44 static LOCKMASK MultiConflicts[] = {
46 /* All reads and writes at any level conflict with a write lock */
47 (1 << WRITE_LOCK) | (1 << WRITE_INTENT) | (1 << READ_LOCK) | (1 << READ_INTENT),
48 /* read locks conflict with write locks at curr and lower levels */
49 (1 << WRITE_LOCK) | (1 << WRITE_INTENT),
50 /* write intent locks */
51 (1 << READ_LOCK) | (1 << WRITE_LOCK),
52 /* read intent locks */
56 * extend locks for archive storage manager conflict only w/extend
63 * write locks have higher priority than read locks and extend locks. May
64 * want to treat INTENT locks differently.
66 static int MultiPrios[] = {
76 * Lock table identifier for this lock table. The multi-level
77 * lock table is ONE lock table, not three.
79 LOCKMETHOD MultiTableId = (LOCKMETHOD) NULL;
80 LOCKMETHOD LongTermTableId = (LOCKMETHOD) NULL;
83 LOCKMETHOD ShortTermTableId = (LOCKMETHOD) NULL;
88 * Create the lock table described by MultiConflicts and Multiprio.
95 lockmethod = LockMethodTableInit("MultiLevelLockTable",
96 MultiConflicts, MultiPrios, MAX_LOCKMODES - 1);
97 MultiTableId = lockmethod;
99 elog(ERROR, "InitMultiLocks: couldnt initialize lock table");
100 /* -----------------------
101 * No short term lock table for now. -Jeff 15 July 1991
103 * ShortTermTableId = LockMethodTableRename(lockmethod);
104 * if (! (ShortTermTableId)) {
105 * elog(ERROR,"InitMultiLocks: couldnt rename lock table");
107 * -----------------------
113 * Allocate another tableId for long-term locks
115 LongTermTableId = LockMethodTableRename(MultiTableId);
116 if (!(LongTermTableId))
119 "InitMultiLevelLocks: couldn't rename long-term lock table");
127 * MultiLockReln -- lock a relation
129 * Returns: TRUE if the lock can be set, FALSE otherwise.
132 MultiLockReln(LockInfo lockinfo, LOCKMODE lockmode)
137 * LOCKTAG has two bytes of padding, unfortunately. The hash function
138 * will return miss if the padding bytes aren't zero'd.
140 MemSet(&tag, 0, sizeof(tag));
141 tag.relId = lockinfo->lockRelId.relId;
142 tag.dbId = lockinfo->lockRelId.dbId;
143 return MultiAcquire(MultiTableId, &tag, lockmode, RELN_LEVEL);
148 * MultiLockTuple -- Lock the TID associated with a tuple
150 * Returns: TRUE if lock is set, FALSE otherwise.
152 * Side Effects: causes intention level locks to be set
153 * at the page and relation level.
156 MultiLockTuple(LockInfo lockinfo, ItemPointer tidPtr, LOCKMODE lockmode)
161 * LOCKTAG has two bytes of padding, unfortunately. The hash function
162 * will return miss if the padding bytes aren't zero'd.
164 MemSet(&tag, 0, sizeof(tag));
166 tag.relId = lockinfo->lockRelId.relId;
167 tag.dbId = lockinfo->lockRelId.dbId;
169 /* not locking any valid Tuple, just the page */
170 tag.tupleId = *tidPtr;
171 return MultiAcquire(MultiTableId, &tag, lockmode, TUPLE_LEVEL);
177 * same as above at page level
180 MultiLockPage(LockInfo lockinfo, ItemPointer tidPtr, LOCKMODE lockmode)
185 * LOCKTAG has two bytes of padding, unfortunately. The hash function
186 * will return miss if the padding bytes aren't zero'd.
188 MemSet(&tag, 0, sizeof(tag));
191 /* ----------------------------
192 * Now we want to set the page offset to be invalid
193 * and lock the block. There is some confusion here as to what
194 * a page is. In Postgres a page is an 8k block, however this
195 * block may be partitioned into many subpages which are sometimes
196 * also called pages. The term is overloaded, so don't be fooled
197 * when we say lock the page we mean the 8k block. -Jeff 16 July 1991
198 * ----------------------------
200 tag.relId = lockinfo->lockRelId.relId;
201 tag.dbId = lockinfo->lockRelId.dbId;
202 BlockIdCopy(&(tag.tupleId.ip_blkid), &(tidPtr->ip_blkid));
203 return MultiAcquire(MultiTableId, &tag, lockmode, PAGE_LEVEL);
207 * MultiAcquire -- acquire multi level lock at requested level
209 * Returns: TRUE if lock is set, FALSE if not
213 MultiAcquire(LOCKMETHOD lockmethod,
218 LOCKMODE locks[N_LEVELS];
223 int retStatus = TRUE;
226 * Three levels implemented. If we set a low level (e.g. Tuple) lock,
227 * we must set INTENT locks on the higher levels. The intent lock
228 * detects conflicts between the low level lock and an existing high
229 * level lock. For example, setting a write lock on a tuple in a
230 * relation is disallowed if there is an existing read lock on the
231 * entire relation. The write lock would set a WRITE + INTENT lock on
232 * the relation and that lock would conflict with the read.
242 locks[0] = lockmode + INTENT;
247 locks[0] = lockmode + INTENT;
248 locks[1] = lockmode + INTENT;
252 elog(ERROR, "MultiAcquire: bad lock level");
257 * construct a new tag as we go. Always loop through all levels, but
258 * if we arent' seting a low level lock, locks[i] is set to NO_LOCK
259 * for the lower levels. Always start from the highest level and go
260 * to the lowest level.
262 MemSet(tmpTag, 0, sizeof(*tmpTag));
263 tmpTag->relId = tag->relId;
264 tmpTag->dbId = tag->dbId;
266 for (i = 0; i < N_LEVELS; i++)
268 if (locks[i] != NO_LOCK)
274 * Set the block # and offset to invalid
277 BlockIdSet(&(tmpTag->tupleId.ip_blkid), InvalidBlockNumber);
278 tmpTag->tupleId.ip_posid = InvalidOffsetNumber;
282 * Copy the block #, set the offset to invalid
285 BlockIdCopy(&(tmpTag->tupleId.ip_blkid),
286 &(tag->tupleId.ip_blkid));
287 tmpTag->tupleId.ip_posid = InvalidOffsetNumber;
291 * Copy the entire tuple id.
294 ItemPointerCopy(&tmpTag->tupleId, &tag->tupleId);
298 status = LockAcquire(lockmethod, tmpTag, locks[i]);
303 * failed for some reason. Before returning we have to
304 * release all of the locks we just acquired.
305 * MultiRelease(xx,xx,xx, i) means release starting from
306 * the last level lock we successfully acquired
309 MultiRelease(lockmethod, tag, lockmode, i);
310 /* now leave the loop. Don't try for any more locks */
318 /* ------------------
319 * Release a page in the multi-level lock table
324 MultiReleasePage(LockInfo lockinfo, ItemPointer tidPtr, LOCKMODE lockmode)
328 /* ------------------
329 * LOCKTAG has two bytes of padding, unfortunately. The
330 * hash function will return miss if the padding bytes aren't
334 MemSet(&tag, 0, sizeof(LOCKTAG));
336 tag.relId = lockinfo->lockRelId.relId;
337 tag.dbId = lockinfo->lockRelId.dbId;
338 BlockIdCopy(&(tag.tupleId.ip_blkid), &(tidPtr->ip_blkid));
340 return MultiRelease(MultiTableId, &tag, lockmode, PAGE_LEVEL);
345 /* ------------------
346 * Release a relation in the multi-level lock table
350 MultiReleaseReln(LockInfo lockinfo, LOCKMODE lockmode)
354 /* ------------------
355 * LOCKTAG has two bytes of padding, unfortunately. The
356 * hash function will return miss if the padding bytes aren't
360 MemSet(&tag, 0, sizeof(LOCKTAG));
361 tag.relId = lockinfo->lockRelId.relId;
362 tag.dbId = lockinfo->lockRelId.dbId;
364 return MultiRelease(MultiTableId, &tag, lockmode, RELN_LEVEL);
368 * MultiRelease -- release a multi-level lock
370 * Returns: TRUE if successful, FALSE otherwise.
373 MultiRelease(LOCKMETHOD lockmethod,
378 LOCKMODE locks[N_LEVELS];
385 * same level scheme as MultiAcquire().
395 locks[0] = lockmode + INTENT;
400 locks[0] = lockmode + INTENT;
401 locks[1] = lockmode + INTENT;
405 elog(ERROR, "MultiRelease: bad lockmode");
409 * again, construct the tag on the fly. This time, however, we
410 * release the locks in the REVERSE order -- from lowest level to
413 * Must zero out the tag to set padding byes to zero and ensure hashing
416 MemSet(tmpTag, 0, sizeof(*tmpTag));
417 tmpTag->relId = tag->relId;
418 tmpTag->dbId = tag->dbId;
420 for (i = (N_LEVELS - 1); i >= 0; i--)
422 if (locks[i] != NO_LOCK)
428 * Set the block # and offset to invalid
431 BlockIdSet(&(tmpTag->tupleId.ip_blkid), InvalidBlockNumber);
432 tmpTag->tupleId.ip_posid = InvalidOffsetNumber;
436 * Copy the block #, set the offset to invalid
439 BlockIdCopy(&(tmpTag->tupleId.ip_blkid),
440 &(tag->tupleId.ip_blkid));
441 tmpTag->tupleId.ip_posid = InvalidOffsetNumber;
444 ItemPointerCopy(&tmpTag->tupleId, &tag->tupleId);
447 status = LockRelease(lockmethod, tmpTag, locks[i]);
449 elog(ERROR, "MultiRelease: couldn't release after error");
452 /* shouldn't reach here */