]> granicus.if.org Git - postgresql/blob - src/backend/access/transam/varsup.c
Improve documentation about reasoning behind the order of operations
[postgresql] / src / backend / access / transam / varsup.c
1 /*-------------------------------------------------------------------------
2  *
3  * varsup.c
4  *        postgres OID & XID variables support routines
5  *
6  * Copyright (c) 2000, PostgreSQL Global Development Group
7  *
8  * IDENTIFICATION
9  *        $Header: /cvsroot/pgsql/src/backend/access/transam/varsup.c,v 1.42 2001/07/16 22:43:33 tgl Exp $
10  *
11  *-------------------------------------------------------------------------
12  */
13
14 #include "postgres.h"
15
16 #include "access/transam.h"
17 #include "access/xlog.h"
18 #include "storage/proc.h"
19
20
21 /* Number of OIDs to prefetch (preallocate) per XLOG write */
22 #define VAR_OID_PREFETCH                8192
23
24 /* Spinlocks for serializing generation of XIDs and OIDs, respectively */
25 SPINLOCK        XidGenLockId;
26 SPINLOCK        OidGenLockId;
27
28 /* pointer to "variable cache" in shared memory (set up by shmem.c) */
29 VariableCache ShmemVariableCache = NULL;
30
31
32 /*
33  * Allocate the next XID for my new transaction.
34  */
35 void
36 GetNewTransactionId(TransactionId *xid)
37 {
38         /*
39          * During bootstrap initialization, we return the special bootstrap
40          * transaction id.
41          */
42         if (AMI_OVERRIDE)
43         {
44                 *xid = AmiTransactionId;
45                 return;
46         }
47
48         SpinAcquire(XidGenLockId);
49
50         *xid = ShmemVariableCache->nextXid;
51
52         (ShmemVariableCache->nextXid)++;
53
54         /*
55          * Must set MyProc->xid before releasing XidGenLock.  This ensures that
56          * when GetSnapshotData calls ReadNewTransactionId, all active XIDs
57          * before the returned value of nextXid are already present in the shared
58          * PROC array.  Else we have a race condition.
59          *
60          * XXX by storing xid into MyProc without acquiring SInvalLock, we are
61          * relying on fetch/store of an xid to be atomic, else other backends
62          * might see a partially-set xid here.  But holding both locks at once
63          * would be a nasty concurrency hit (and in fact could cause a deadlock
64          * against GetSnapshotData).  So for now, assume atomicity.  Note that
65          * readers of PROC xid field should be careful to fetch the value only
66          * once, rather than assume they can read it multiple times and get the
67          * same answer each time.
68          *
69          * A solution to the atomic-store problem would be to give each PROC
70          * its own spinlock used only for fetching/storing that PROC's xid.
71          * (SInvalLock would then mean primarily that PROCs couldn't be added/
72          * removed while holding the lock.)
73          */
74         if (MyProc != (PROC *) NULL)
75                 MyProc->xid = *xid;
76
77         SpinRelease(XidGenLockId);
78 }
79
80 /*
81  * Read nextXid but don't allocate it.
82  */
83 void
84 ReadNewTransactionId(TransactionId *xid)
85 {
86         /*
87          * During bootstrap initialization, we return the special bootstrap
88          * transaction id.
89          */
90         if (AMI_OVERRIDE)
91         {
92                 *xid = AmiTransactionId;
93                 return;
94         }
95
96         SpinAcquire(XidGenLockId);
97         *xid = ShmemVariableCache->nextXid;
98         SpinRelease(XidGenLockId);
99 }
100
101 /* ----------------------------------------------------------------
102  *                                      object id generation support
103  * ----------------------------------------------------------------
104  */
105
106 static Oid      lastSeenOid = InvalidOid;
107
108 void
109 GetNewObjectId(Oid *oid_return)
110 {
111         SpinAcquire(OidGenLockId);
112
113         /* If we run out of logged for use oids then we must log more */
114         if (ShmemVariableCache->oidCount == 0)
115         {
116                 XLogPutNextOid(ShmemVariableCache->nextOid + VAR_OID_PREFETCH);
117                 ShmemVariableCache->oidCount = VAR_OID_PREFETCH;
118         }
119
120         if (PointerIsValid(oid_return))
121                 lastSeenOid = (*oid_return) = ShmemVariableCache->nextOid;
122
123         (ShmemVariableCache->nextOid)++;
124         (ShmemVariableCache->oidCount)--;
125
126         SpinRelease(OidGenLockId);
127 }
128
129 void
130 CheckMaxObjectId(Oid assigned_oid)
131 {
132         if (lastSeenOid != InvalidOid && assigned_oid < lastSeenOid)
133                 return;
134
135         SpinAcquire(OidGenLockId);
136
137         if (assigned_oid < ShmemVariableCache->nextOid)
138         {
139                 lastSeenOid = ShmemVariableCache->nextOid - 1;
140                 SpinRelease(OidGenLockId);
141                 return;
142         }
143
144         /* If we are in the logged oid range, just bump nextOid up */
145         if (assigned_oid <= ShmemVariableCache->nextOid +
146                 ShmemVariableCache->oidCount - 1)
147         {
148                 ShmemVariableCache->oidCount -=
149                         assigned_oid - ShmemVariableCache->nextOid + 1;
150                 ShmemVariableCache->nextOid = assigned_oid + 1;
151                 SpinRelease(OidGenLockId);
152                 return;
153         }
154
155         /*
156          * We have exceeded the logged oid range. We should lock the database
157          * and kill all other backends but we are loading oid's that we can
158          * not guarantee are unique anyway, so we must rely on the user.
159          */
160
161         XLogPutNextOid(assigned_oid + VAR_OID_PREFETCH);
162         ShmemVariableCache->oidCount = VAR_OID_PREFETCH - 1;
163         ShmemVariableCache->nextOid = assigned_oid + 1;
164
165         SpinRelease(OidGenLockId);
166 }