]> granicus.if.org Git - postgresql/blob - src/backend/storage/lmgr/lmgr.c
e4e52b16abf33b283c1be267d5b93d84017a90ee
[postgresql] / src / backend / storage / lmgr / lmgr.c
1 /*-------------------------------------------------------------------------
2  *
3  * lmgr.c
4  *        POSTGRES lock manager code
5  *
6  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  *
10  * IDENTIFICATION
11  *        $PostgreSQL: pgsql/src/backend/storage/lmgr/lmgr.c,v 1.63 2004/05/28 05:13:04 tgl Exp $
12  *
13  *-------------------------------------------------------------------------
14  */
15
16 #include "postgres.h"
17
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"
24
25
26 static LOCKMASK LockConflicts[] = {
27         0,
28
29         /* AccessShareLock */
30         (1 << AccessExclusiveLock),
31
32         /* RowShareLock */
33         (1 << ExclusiveLock) | (1 << AccessExclusiveLock),
34
35         /* RowExclusiveLock */
36         (1 << ShareLock) | (1 << ShareRowExclusiveLock) |
37         (1 << ExclusiveLock) | (1 << AccessExclusiveLock),
38
39         /* ShareUpdateExclusiveLock */
40         (1 << ShareUpdateExclusiveLock) |
41         (1 << ShareLock) | (1 << ShareRowExclusiveLock) |
42         (1 << ExclusiveLock) | (1 << AccessExclusiveLock),
43
44         /* ShareLock */
45         (1 << RowExclusiveLock) | (1 << ShareUpdateExclusiveLock) |
46         (1 << ShareRowExclusiveLock) |
47         (1 << ExclusiveLock) | (1 << AccessExclusiveLock),
48
49         /* ShareRowExclusiveLock */
50         (1 << RowExclusiveLock) | (1 << ShareUpdateExclusiveLock) |
51         (1 << ShareLock) | (1 << ShareRowExclusiveLock) |
52         (1 << ExclusiveLock) | (1 << AccessExclusiveLock),
53
54         /* ExclusiveLock */
55         (1 << RowShareLock) |
56         (1 << RowExclusiveLock) | (1 << ShareUpdateExclusiveLock) |
57         (1 << ShareLock) | (1 << ShareRowExclusiveLock) |
58         (1 << ExclusiveLock) | (1 << AccessExclusiveLock),
59
60         /* AccessExclusiveLock */
61         (1 << AccessShareLock) | (1 << RowShareLock) |
62         (1 << RowExclusiveLock) | (1 << ShareUpdateExclusiveLock) |
63         (1 << ShareLock) | (1 << ShareRowExclusiveLock) |
64         (1 << ExclusiveLock) | (1 << AccessExclusiveLock)
65
66 };
67
68 static  LOCKMETHODID    LockTableId = INVALID_LOCKMETHOD;
69
70 /*
71  * Create the lock table described by LockConflicts
72  */
73 void
74 InitLockTable(int maxBackends)
75 {
76         LOCKMETHODID    LongTermTableId;
77
78         /* there's no zero-th table */
79         NumLockMethods = 1;
80
81         /*
82          * Create the default lock method table
83          */
84
85         /* number of lock modes is lengthof()-1 because of dummy zero */
86         LockTableId = LockMethodTableInit("LockTable",
87                                                                           LockConflicts,
88                                                                           lengthof(LockConflicts) - 1,
89                                                                           maxBackends);
90         if (!LockMethodIsValid(LockTableId))
91                 elog(ERROR, "could not initialize lock table");
92         Assert(LockTableId == DEFAULT_LOCKMETHOD);
93
94 #ifdef USER_LOCKS
95
96         /*
97          * Allocate another tableId for long-term locks
98          */
99         LongTermTableId = LockMethodTableRename(LockTableId);
100         if (!LockMethodIsValid(LongTermTableId))
101                 elog(ERROR, "could not rename long-term lock table");
102         Assert(LongTermTableId == USER_LOCKMETHOD);
103 #endif
104 }
105
106 /*
107  * RelationInitLockInfo
108  *              Initializes the lock information in a relation descriptor.
109  *
110  *              relcache.c must call this during creation of any reldesc.
111  */
112 void
113 RelationInitLockInfo(Relation relation)
114 {
115         Assert(RelationIsValid(relation));
116         Assert(OidIsValid(RelationGetRelid(relation)));
117
118         relation->rd_lockInfo.lockRelId.relId = RelationGetRelid(relation);
119
120         if (relation->rd_rel->relisshared)
121                 relation->rd_lockInfo.lockRelId.dbId = InvalidOid;
122         else
123                 relation->rd_lockInfo.lockRelId.dbId = MyDatabaseId;
124 }
125
126 /*
127  *              LockRelation
128  */
129 void
130 LockRelation(Relation relation, LOCKMODE lockmode)
131 {
132         LOCKTAG         tag;
133
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;
138
139         if (!LockAcquire(LockTableId, &tag, GetCurrentTransactionId(),
140                                          lockmode, false))
141                 elog(ERROR, "LockAcquire failed");
142
143         /*
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.
148          */
149         RelationIncrementReferenceCount(relation);
150         AcceptInvalidationMessages();
151         RelationDecrementReferenceCount(relation);
152 }
153
154 /*
155  *              ConditionalLockRelation
156  *
157  * As above, but only lock if we can get the lock without blocking.
158  * Returns TRUE iff the lock was acquired.
159  *
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.
162  */
163 bool
164 ConditionalLockRelation(Relation relation, LOCKMODE lockmode)
165 {
166         LOCKTAG         tag;
167
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;
172
173         if (!LockAcquire(LockTableId, &tag, GetCurrentTransactionId(),
174                                          lockmode, true))
175                 return false;
176
177         /*
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.
182          */
183         RelationIncrementReferenceCount(relation);
184         AcceptInvalidationMessages();
185         RelationDecrementReferenceCount(relation);
186
187         return true;
188 }
189
190 /*
191  *              UnlockRelation
192  */
193 void
194 UnlockRelation(Relation relation, LOCKMODE lockmode)
195 {
196         LOCKTAG         tag;
197
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;
202
203         LockRelease(LockTableId, &tag, GetCurrentTransactionId(), lockmode);
204 }
205
206 /*
207  *              LockRelationForSession
208  *
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.
213  *
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.
217  */
218 void
219 LockRelationForSession(LockRelId *relid, LOCKMODE lockmode)
220 {
221         LOCKTAG         tag;
222
223         MemSet(&tag, 0, sizeof(tag));
224         tag.relId = relid->relId;
225         tag.dbId = relid->dbId;
226         tag.objId.blkno = InvalidBlockNumber;
227
228         if (!LockAcquire(LockTableId, &tag, InvalidTransactionId,
229                                          lockmode, false))
230                 elog(ERROR, "LockAcquire failed");
231 }
232
233 /*
234  *              UnlockRelationForSession
235  */
236 void
237 UnlockRelationForSession(LockRelId *relid, LOCKMODE lockmode)
238 {
239         LOCKTAG         tag;
240
241         MemSet(&tag, 0, sizeof(tag));
242         tag.relId = relid->relId;
243         tag.dbId = relid->dbId;
244         tag.objId.blkno = InvalidBlockNumber;
245
246         LockRelease(LockTableId, &tag, InvalidTransactionId, lockmode);
247 }
248
249 /*
250  *              LockPage
251  *
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.
255  */
256 void
257 LockPage(Relation relation, BlockNumber blkno, LOCKMODE lockmode)
258 {
259         LOCKTAG         tag;
260
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;
265
266         if (!LockAcquire(LockTableId, &tag, GetCurrentTransactionId(),
267                                          lockmode, false))
268                 elog(ERROR, "LockAcquire failed");
269 }
270
271 /*
272  *              ConditionalLockPage
273  *
274  * As above, but only lock if we can get the lock without blocking.
275  * Returns TRUE iff the lock was acquired.
276  */
277 bool
278 ConditionalLockPage(Relation relation, BlockNumber blkno, LOCKMODE lockmode)
279 {
280         LOCKTAG         tag;
281
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;
286
287         return LockAcquire(LockTableId, &tag, GetCurrentTransactionId(),
288                                            lockmode, true);
289 }
290
291 /*
292  *              UnlockPage
293  */
294 void
295 UnlockPage(Relation relation, BlockNumber blkno, LOCKMODE lockmode)
296 {
297         LOCKTAG         tag;
298
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;
303
304         LockRelease(LockTableId, &tag, GetCurrentTransactionId(), lockmode);
305 }
306
307 /*
308  *              XactLockTableInsert
309  *
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.
313  *
314  * We need no corresponding unlock function, since the lock will always
315  * be released implicitly at transaction commit/abort, never any other way.
316  */
317 void
318 XactLockTableInsert(TransactionId xid)
319 {
320         LOCKTAG         tag;
321
322         MemSet(&tag, 0, sizeof(tag));
323         tag.relId = XactLockTableId;
324         tag.dbId = InvalidOid;          /* xids are globally unique */
325         tag.objId.xid = xid;
326
327         if (!LockAcquire(LockTableId, &tag, xid,
328                                          ExclusiveLock, false))
329                 elog(ERROR, "LockAcquire failed");
330 }
331
332 /*
333  *              XactLockTableWait
334  *
335  * Wait for the specified transaction to commit or abort.
336  */
337 void
338 XactLockTableWait(TransactionId xid)
339 {
340         LOCKTAG         tag;
341         TransactionId myxid = GetCurrentTransactionId();
342
343         Assert(!TransactionIdEquals(xid, myxid));
344
345         MemSet(&tag, 0, sizeof(tag));
346         tag.relId = XactLockTableId;
347         tag.dbId = InvalidOid;
348         tag.objId.xid = xid;
349
350         if (!LockAcquire(LockTableId, &tag, myxid,
351                                          ShareLock, false))
352                 elog(ERROR, "LockAcquire failed");
353
354         LockRelease(LockTableId, &tag, myxid, ShareLock);
355
356         /*
357          * Transaction was committed/aborted/crashed - we have to update
358          * pg_clog if transaction is still marked as running.
359          */
360         if (!TransactionIdDidCommit(xid) && !TransactionIdDidAbort(xid))
361                 TransactionIdAbort(xid);
362 }