1 /*-------------------------------------------------------------------------
4 * POSTGRES lock manager code
6 * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
11 * $PostgreSQL: pgsql/src/backend/storage/lmgr/lmgr.c,v 1.63 2004/05/28 05:13:04 tgl Exp $
13 *-------------------------------------------------------------------------
18 #include "access/transam.h"
19 #include "access/xact.h"
20 #include "catalog/catalog.h"
21 #include "miscadmin.h"
22 #include "storage/lmgr.h"
23 #include "utils/inval.h"
26 static LOCKMASK LockConflicts[] = {
30 (1 << AccessExclusiveLock),
33 (1 << ExclusiveLock) | (1 << AccessExclusiveLock),
35 /* RowExclusiveLock */
36 (1 << ShareLock) | (1 << ShareRowExclusiveLock) |
37 (1 << ExclusiveLock) | (1 << AccessExclusiveLock),
39 /* ShareUpdateExclusiveLock */
40 (1 << ShareUpdateExclusiveLock) |
41 (1 << ShareLock) | (1 << ShareRowExclusiveLock) |
42 (1 << ExclusiveLock) | (1 << AccessExclusiveLock),
45 (1 << RowExclusiveLock) | (1 << ShareUpdateExclusiveLock) |
46 (1 << ShareRowExclusiveLock) |
47 (1 << ExclusiveLock) | (1 << AccessExclusiveLock),
49 /* ShareRowExclusiveLock */
50 (1 << RowExclusiveLock) | (1 << ShareUpdateExclusiveLock) |
51 (1 << ShareLock) | (1 << ShareRowExclusiveLock) |
52 (1 << ExclusiveLock) | (1 << AccessExclusiveLock),
56 (1 << RowExclusiveLock) | (1 << ShareUpdateExclusiveLock) |
57 (1 << ShareLock) | (1 << ShareRowExclusiveLock) |
58 (1 << ExclusiveLock) | (1 << AccessExclusiveLock),
60 /* AccessExclusiveLock */
61 (1 << AccessShareLock) | (1 << RowShareLock) |
62 (1 << RowExclusiveLock) | (1 << ShareUpdateExclusiveLock) |
63 (1 << ShareLock) | (1 << ShareRowExclusiveLock) |
64 (1 << ExclusiveLock) | (1 << AccessExclusiveLock)
68 static LOCKMETHODID LockTableId = INVALID_LOCKMETHOD;
71 * Create the lock table described by LockConflicts
74 InitLockTable(int maxBackends)
76 LOCKMETHODID LongTermTableId;
78 /* there's no zero-th table */
82 * Create the default lock method table
85 /* number of lock modes is lengthof()-1 because of dummy zero */
86 LockTableId = LockMethodTableInit("LockTable",
88 lengthof(LockConflicts) - 1,
90 if (!LockMethodIsValid(LockTableId))
91 elog(ERROR, "could not initialize lock table");
92 Assert(LockTableId == DEFAULT_LOCKMETHOD);
97 * Allocate another tableId for long-term locks
99 LongTermTableId = LockMethodTableRename(LockTableId);
100 if (!LockMethodIsValid(LongTermTableId))
101 elog(ERROR, "could not rename long-term lock table");
102 Assert(LongTermTableId == USER_LOCKMETHOD);
107 * RelationInitLockInfo
108 * Initializes the lock information in a relation descriptor.
110 * relcache.c must call this during creation of any reldesc.
113 RelationInitLockInfo(Relation relation)
115 Assert(RelationIsValid(relation));
116 Assert(OidIsValid(RelationGetRelid(relation)));
118 relation->rd_lockInfo.lockRelId.relId = RelationGetRelid(relation);
120 if (relation->rd_rel->relisshared)
121 relation->rd_lockInfo.lockRelId.dbId = InvalidOid;
123 relation->rd_lockInfo.lockRelId.dbId = MyDatabaseId;
130 LockRelation(Relation relation, LOCKMODE lockmode)
134 MemSet(&tag, 0, sizeof(tag));
135 tag.relId = relation->rd_lockInfo.lockRelId.relId;
136 tag.dbId = relation->rd_lockInfo.lockRelId.dbId;
137 tag.objId.blkno = InvalidBlockNumber;
139 if (!LockAcquire(LockTableId, &tag, GetCurrentTransactionId(),
141 elog(ERROR, "LockAcquire failed");
144 * Check to see if the relcache entry has been invalidated while we
145 * were waiting to lock it. If so, rebuild it, or ereport() trying.
146 * Increment the refcount to ensure that RelationFlushRelation will
147 * rebuild it and not just delete it.
149 RelationIncrementReferenceCount(relation);
150 AcceptInvalidationMessages();
151 RelationDecrementReferenceCount(relation);
155 * ConditionalLockRelation
157 * As above, but only lock if we can get the lock without blocking.
158 * Returns TRUE iff the lock was acquired.
160 * NOTE: we do not currently need conditional versions of all the
161 * LockXXX routines in this file, but they could easily be added if needed.
164 ConditionalLockRelation(Relation relation, LOCKMODE lockmode)
168 MemSet(&tag, 0, sizeof(tag));
169 tag.relId = relation->rd_lockInfo.lockRelId.relId;
170 tag.dbId = relation->rd_lockInfo.lockRelId.dbId;
171 tag.objId.blkno = InvalidBlockNumber;
173 if (!LockAcquire(LockTableId, &tag, GetCurrentTransactionId(),
178 * Check to see if the relcache entry has been invalidated while we
179 * were waiting to lock it. If so, rebuild it, or ereport() trying.
180 * Increment the refcount to ensure that RelationFlushRelation will
181 * rebuild it and not just delete it.
183 RelationIncrementReferenceCount(relation);
184 AcceptInvalidationMessages();
185 RelationDecrementReferenceCount(relation);
194 UnlockRelation(Relation relation, LOCKMODE lockmode)
198 MemSet(&tag, 0, sizeof(tag));
199 tag.relId = relation->rd_lockInfo.lockRelId.relId;
200 tag.dbId = relation->rd_lockInfo.lockRelId.dbId;
201 tag.objId.blkno = InvalidBlockNumber;
203 LockRelease(LockTableId, &tag, GetCurrentTransactionId(), lockmode);
207 * LockRelationForSession
209 * This routine grabs a session-level lock on the target relation. The
210 * session lock persists across transaction boundaries. It will be removed
211 * when UnlockRelationForSession() is called, or if an ereport(ERROR) occurs,
212 * or if the backend exits.
214 * Note that one should also grab a transaction-level lock on the rel
215 * in any transaction that actually uses the rel, to ensure that the
216 * relcache entry is up to date.
219 LockRelationForSession(LockRelId *relid, LOCKMODE lockmode)
223 MemSet(&tag, 0, sizeof(tag));
224 tag.relId = relid->relId;
225 tag.dbId = relid->dbId;
226 tag.objId.blkno = InvalidBlockNumber;
228 if (!LockAcquire(LockTableId, &tag, InvalidTransactionId,
230 elog(ERROR, "LockAcquire failed");
234 * UnlockRelationForSession
237 UnlockRelationForSession(LockRelId *relid, LOCKMODE lockmode)
241 MemSet(&tag, 0, sizeof(tag));
242 tag.relId = relid->relId;
243 tag.dbId = relid->dbId;
244 tag.objId.blkno = InvalidBlockNumber;
246 LockRelease(LockTableId, &tag, InvalidTransactionId, lockmode);
252 * Obtain a page-level lock. This is currently used by some index access
253 * methods to lock index pages. For heap relations, it is used only with
254 * blkno == 0 to signify locking the relation for extension.
257 LockPage(Relation relation, BlockNumber blkno, LOCKMODE lockmode)
261 MemSet(&tag, 0, sizeof(tag));
262 tag.relId = relation->rd_lockInfo.lockRelId.relId;
263 tag.dbId = relation->rd_lockInfo.lockRelId.dbId;
264 tag.objId.blkno = blkno;
266 if (!LockAcquire(LockTableId, &tag, GetCurrentTransactionId(),
268 elog(ERROR, "LockAcquire failed");
272 * ConditionalLockPage
274 * As above, but only lock if we can get the lock without blocking.
275 * Returns TRUE iff the lock was acquired.
278 ConditionalLockPage(Relation relation, BlockNumber blkno, LOCKMODE lockmode)
282 MemSet(&tag, 0, sizeof(tag));
283 tag.relId = relation->rd_lockInfo.lockRelId.relId;
284 tag.dbId = relation->rd_lockInfo.lockRelId.dbId;
285 tag.objId.blkno = blkno;
287 return LockAcquire(LockTableId, &tag, GetCurrentTransactionId(),
295 UnlockPage(Relation relation, BlockNumber blkno, LOCKMODE lockmode)
299 MemSet(&tag, 0, sizeof(tag));
300 tag.relId = relation->rd_lockInfo.lockRelId.relId;
301 tag.dbId = relation->rd_lockInfo.lockRelId.dbId;
302 tag.objId.blkno = blkno;
304 LockRelease(LockTableId, &tag, GetCurrentTransactionId(), lockmode);
308 * XactLockTableInsert
310 * Insert a lock showing that the given transaction ID is running ---
311 * this is done during xact startup. The lock can then be used to wait
312 * for the transaction to finish.
314 * We need no corresponding unlock function, since the lock will always
315 * be released implicitly at transaction commit/abort, never any other way.
318 XactLockTableInsert(TransactionId xid)
322 MemSet(&tag, 0, sizeof(tag));
323 tag.relId = XactLockTableId;
324 tag.dbId = InvalidOid; /* xids are globally unique */
327 if (!LockAcquire(LockTableId, &tag, xid,
328 ExclusiveLock, false))
329 elog(ERROR, "LockAcquire failed");
335 * Wait for the specified transaction to commit or abort.
338 XactLockTableWait(TransactionId xid)
341 TransactionId myxid = GetCurrentTransactionId();
343 Assert(!TransactionIdEquals(xid, myxid));
345 MemSet(&tag, 0, sizeof(tag));
346 tag.relId = XactLockTableId;
347 tag.dbId = InvalidOid;
350 if (!LockAcquire(LockTableId, &tag, myxid,
352 elog(ERROR, "LockAcquire failed");
354 LockRelease(LockTableId, &tag, myxid, ShareLock);
357 * Transaction was committed/aborted/crashed - we have to update
358 * pg_clog if transaction is still marked as running.
360 if (!TransactionIdDidCommit(xid) && !TransactionIdDidAbort(xid))
361 TransactionIdAbort(xid);