Commit 450c7def authored by Andres Freund's avatar Andres Freund

Remove volatiles from {procarray,volatile}.c and fix memory ordering issue.

The use of volatiles in procarray.c largely originated from the time
when postgres did not have reliable compiler and memory
barriers. That's not the case anymore, so we can do better.

Several of the functions in procarray.c can be bottlenecks, and
removal of volatile yields mildly better code.

The new state, with explicit memory barriers, is also more
correct. The previous use of volatile did not actually deliver
sufficient guarantees on weakly ordered machines, in particular the
logic in GetNewTransactionId() does not look safe.  It seems unlikely
to be a problem in practice, but worth fixing.

Thomas and I independently wrote a patch for this.

Reported-By: Andres Freund and Thomas Munro
Author: Andres Freund, with cherrypicked changes from a patch by Thomas Munro
Discussion:
    https://postgr.es/m/20181005172955.wyjb4fzcdzqtaxjq@alap3.anarazel.de
    https://postgr.es/m/CAEepm=1nff0x=7i3YQO16jLA2qw-F9O39YmUew4oq-xcBQBs0g@mail.gmail.com
parent 69ee2ff9
...@@ -186,20 +186,23 @@ GetNewTransactionId(bool isSubXact) ...@@ -186,20 +186,23 @@ GetNewTransactionId(bool isSubXact)
* latestCompletedXid is present in the ProcArray, which is essential for * latestCompletedXid is present in the ProcArray, which is essential for
* correct OldestXmin tracking; see src/backend/access/transam/README. * correct OldestXmin tracking; see src/backend/access/transam/README.
* *
* XXX by storing xid into MyPgXact without acquiring ProcArrayLock, we
* are relying on fetch/store of an xid to be atomic, else other backends
* might see a partially-set xid here. But holding both locks at once
* would be a nasty concurrency hit. So for now, assume atomicity.
*
* Note that readers of PGXACT xid fields should be careful to fetch the * Note that readers of PGXACT xid fields should be careful to fetch the
* value only once, rather than assume they can read a value multiple * value only once, rather than assume they can read a value multiple
* times and get the same answer each time. * times and get the same answer each time. Note we are assuming that
* TransactionId and int fetch/store are atomic.
* *
* The same comments apply to the subxact xid count and overflow fields. * The same comments apply to the subxact xid count and overflow fields.
* *
* A solution to the atomic-store problem would be to give each PGXACT its * Use of a write barrier prevents dangerous code rearrangement in this
* own spinlock used only for fetching/storing that PGXACT's xid and * function; other backends could otherwise e.g. be examining my subxids
* related fields. * info concurrently, and we don't want them to see an invalid
* intermediate state, such as an incremented nxids before the array entry
* is filled.
*
* Other processes that read nxids should do so before reading xids
* elements with a pg_read_barrier() in between, so that they can be sure
* not to read an uninitialized array element; see
* src/backend/storage/lmgr/README.barrier.
* *
* If there's no room to fit a subtransaction XID into PGPROC, set the * If there's no room to fit a subtransaction XID into PGPROC, set the
* cache-overflowed flag instead. This forces readers to look in * cache-overflowed flag instead. This forces readers to look in
...@@ -211,31 +214,20 @@ GetNewTransactionId(bool isSubXact) ...@@ -211,31 +214,20 @@ GetNewTransactionId(bool isSubXact)
* window *will* include the parent XID, so they will deliver the correct * window *will* include the parent XID, so they will deliver the correct
* answer later on when someone does have a reason to inquire.) * answer later on when someone does have a reason to inquire.)
*/ */
if (!isSubXact)
MyPgXact->xid = xid; /* LWLockRelease acts as barrier */
else
{ {
/* int nxids = MyPgXact->nxids;
* Use volatile pointer to prevent code rearrangement; other backends
* could be examining my subxids info concurrently, and we don't want
* them to see an invalid intermediate state, such as incrementing
* nxids before filling the array entry. Note we are assuming that
* TransactionId and int fetch/store are atomic.
*/
volatile PGPROC *myproc = MyProc;
volatile PGXACT *mypgxact = MyPgXact;
if (!isSubXact) if (nxids < PGPROC_MAX_CACHED_SUBXIDS)
mypgxact->xid = xid;
else
{ {
int nxids = mypgxact->nxids; MyProc->subxids.xids[nxids] = xid;
pg_write_barrier();
if (nxids < PGPROC_MAX_CACHED_SUBXIDS) MyPgXact->nxids = nxids + 1;
{
myproc->subxids.xids[nxids] = xid;
mypgxact->nxids = nxids + 1;
}
else
mypgxact->overflowed = true;
} }
else
MyPgXact->overflowed = true;
} }
LWLockRelease(XidGenLock); LWLockRelease(XidGenLock);
......
...@@ -61,6 +61,7 @@ ...@@ -61,6 +61,7 @@
#include "utils/rel.h" #include "utils/rel.h"
#include "utils/snapmgr.h" #include "utils/snapmgr.h"
#define UINT32_ACCESS_ONCE(var) ((uint32)(*((volatile uint32 *)&(var))))
/* Our shared memory area */ /* Our shared memory area */
typedef struct ProcArrayStruct typedef struct ProcArrayStruct
...@@ -483,7 +484,7 @@ ProcArrayEndTransactionInternal(PGPROC *proc, PGXACT *pgxact, ...@@ -483,7 +484,7 @@ ProcArrayEndTransactionInternal(PGPROC *proc, PGXACT *pgxact,
static void static void
ProcArrayGroupClearXid(PGPROC *proc, TransactionId latestXid) ProcArrayGroupClearXid(PGPROC *proc, TransactionId latestXid)
{ {
volatile PROC_HDR *procglobal = ProcGlobal; PROC_HDR *procglobal = ProcGlobal;
uint32 nextidx; uint32 nextidx;
uint32 wakeidx; uint32 wakeidx;
...@@ -1077,16 +1078,17 @@ TransactionIdIsInProgress(TransactionId xid) ...@@ -1077,16 +1078,17 @@ TransactionIdIsInProgress(TransactionId xid)
for (i = 0; i < arrayP->numProcs; i++) for (i = 0; i < arrayP->numProcs; i++)
{ {
int pgprocno = arrayP->pgprocnos[i]; int pgprocno = arrayP->pgprocnos[i];
volatile PGPROC *proc = &allProcs[pgprocno]; PGPROC *proc = &allProcs[pgprocno];
volatile PGXACT *pgxact = &allPgXact[pgprocno]; PGXACT *pgxact = &allPgXact[pgprocno];
TransactionId pxid; TransactionId pxid;
int pxids;
/* Ignore my own proc --- dealt with it above */ /* Ignore my own proc --- dealt with it above */
if (proc == MyProc) if (proc == MyProc)
continue; continue;
/* Fetch xid just once - see GetNewTransactionId */ /* Fetch xid just once - see GetNewTransactionId */
pxid = pgxact->xid; pxid = UINT32_ACCESS_ONCE(pgxact->xid);
if (!TransactionIdIsValid(pxid)) if (!TransactionIdIsValid(pxid))
continue; continue;
...@@ -1111,10 +1113,12 @@ TransactionIdIsInProgress(TransactionId xid) ...@@ -1111,10 +1113,12 @@ TransactionIdIsInProgress(TransactionId xid)
/* /*
* Step 2: check the cached child-Xids arrays * Step 2: check the cached child-Xids arrays
*/ */
for (j = pgxact->nxids - 1; j >= 0; j--) pxids = pgxact->nxids;
pg_read_barrier(); /* pairs with barrier in GetNewTransactionId() */
for (j = pxids - 1; j >= 0; j--)
{ {
/* Fetch xid just once - see GetNewTransactionId */ /* Fetch xid just once - see GetNewTransactionId */
TransactionId cxid = proc->subxids.xids[j]; TransactionId cxid = UINT32_ACCESS_ONCE(proc->subxids.xids[j]);
if (TransactionIdEquals(cxid, xid)) if (TransactionIdEquals(cxid, xid))
{ {
...@@ -1233,12 +1237,12 @@ TransactionIdIsActive(TransactionId xid) ...@@ -1233,12 +1237,12 @@ TransactionIdIsActive(TransactionId xid)
for (i = 0; i < arrayP->numProcs; i++) for (i = 0; i < arrayP->numProcs; i++)
{ {
int pgprocno = arrayP->pgprocnos[i]; int pgprocno = arrayP->pgprocnos[i];
volatile PGPROC *proc = &allProcs[pgprocno]; PGPROC *proc = &allProcs[pgprocno];
volatile PGXACT *pgxact = &allPgXact[pgprocno]; PGXACT *pgxact = &allPgXact[pgprocno];
TransactionId pxid; TransactionId pxid;
/* Fetch xid just once - see GetNewTransactionId */ /* Fetch xid just once - see GetNewTransactionId */
pxid = pgxact->xid; pxid = UINT32_ACCESS_ONCE(pgxact->xid);
if (!TransactionIdIsValid(pxid)) if (!TransactionIdIsValid(pxid))
continue; continue;
...@@ -1321,8 +1325,8 @@ GetOldestXmin(Relation rel, int flags) ...@@ -1321,8 +1325,8 @@ GetOldestXmin(Relation rel, int flags)
int index; int index;
bool allDbs; bool allDbs;
volatile TransactionId replication_slot_xmin = InvalidTransactionId; TransactionId replication_slot_xmin = InvalidTransactionId;
volatile TransactionId replication_slot_catalog_xmin = InvalidTransactionId; TransactionId replication_slot_catalog_xmin = InvalidTransactionId;
/* /*
* If we're not computing a relation specific limit, or if a shared * If we're not computing a relation specific limit, or if a shared
...@@ -1349,8 +1353,8 @@ GetOldestXmin(Relation rel, int flags) ...@@ -1349,8 +1353,8 @@ GetOldestXmin(Relation rel, int flags)
for (index = 0; index < arrayP->numProcs; index++) for (index = 0; index < arrayP->numProcs; index++)
{ {
int pgprocno = arrayP->pgprocnos[index]; int pgprocno = arrayP->pgprocnos[index];
volatile PGPROC *proc = &allProcs[pgprocno]; PGPROC *proc = &allProcs[pgprocno];
volatile PGXACT *pgxact = &allPgXact[pgprocno]; PGXACT *pgxact = &allPgXact[pgprocno];
if (pgxact->vacuumFlags & (flags & PROCARRAY_PROC_FLAGS_MASK)) if (pgxact->vacuumFlags & (flags & PROCARRAY_PROC_FLAGS_MASK))
continue; continue;
...@@ -1360,7 +1364,7 @@ GetOldestXmin(Relation rel, int flags) ...@@ -1360,7 +1364,7 @@ GetOldestXmin(Relation rel, int flags)
proc->databaseId == 0) /* always include WalSender */ proc->databaseId == 0) /* always include WalSender */
{ {
/* Fetch xid just once - see GetNewTransactionId */ /* Fetch xid just once - see GetNewTransactionId */
TransactionId xid = pgxact->xid; TransactionId xid = UINT32_ACCESS_ONCE(pgxact->xid);
/* First consider the transaction's own Xid, if any */ /* First consider the transaction's own Xid, if any */
if (TransactionIdIsNormal(xid) && if (TransactionIdIsNormal(xid) &&
...@@ -1374,14 +1378,18 @@ GetOldestXmin(Relation rel, int flags) ...@@ -1374,14 +1378,18 @@ GetOldestXmin(Relation rel, int flags)
* have an Xmin but not (yet) an Xid; conversely, if it has an * have an Xmin but not (yet) an Xid; conversely, if it has an
* Xid, that could determine some not-yet-set Xmin. * Xid, that could determine some not-yet-set Xmin.
*/ */
xid = pgxact->xmin; /* Fetch just once */ xid = UINT32_ACCESS_ONCE(pgxact->xmin);
if (TransactionIdIsNormal(xid) && if (TransactionIdIsNormal(xid) &&
TransactionIdPrecedes(xid, result)) TransactionIdPrecedes(xid, result))
result = xid; result = xid;
} }
} }
/* fetch into volatile var while ProcArrayLock is held */ /*
* Fetch into local variable while ProcArrayLock is held - the
* LWLockRelease below is a barrier, ensuring this happens inside the
* lock.
*/
replication_slot_xmin = procArray->replication_slot_xmin; replication_slot_xmin = procArray->replication_slot_xmin;
replication_slot_catalog_xmin = procArray->replication_slot_catalog_xmin; replication_slot_catalog_xmin = procArray->replication_slot_catalog_xmin;
...@@ -1518,8 +1526,8 @@ GetSnapshotData(Snapshot snapshot) ...@@ -1518,8 +1526,8 @@ GetSnapshotData(Snapshot snapshot)
int count = 0; int count = 0;
int subcount = 0; int subcount = 0;
bool suboverflowed = false; bool suboverflowed = false;
volatile TransactionId replication_slot_xmin = InvalidTransactionId; TransactionId replication_slot_xmin = InvalidTransactionId;
volatile TransactionId replication_slot_catalog_xmin = InvalidTransactionId; TransactionId replication_slot_catalog_xmin = InvalidTransactionId;
Assert(snapshot != NULL); Assert(snapshot != NULL);
...@@ -1585,7 +1593,7 @@ GetSnapshotData(Snapshot snapshot) ...@@ -1585,7 +1593,7 @@ GetSnapshotData(Snapshot snapshot)
for (index = 0; index < numProcs; index++) for (index = 0; index < numProcs; index++)
{ {
int pgprocno = pgprocnos[index]; int pgprocno = pgprocnos[index];
volatile PGXACT *pgxact = &allPgXact[pgprocno]; PGXACT *pgxact = &allPgXact[pgprocno];
TransactionId xid; TransactionId xid;
/* /*
...@@ -1597,13 +1605,13 @@ GetSnapshotData(Snapshot snapshot) ...@@ -1597,13 +1605,13 @@ GetSnapshotData(Snapshot snapshot)
continue; continue;
/* Update globalxmin to be the smallest valid xmin */ /* Update globalxmin to be the smallest valid xmin */
xid = pgxact->xmin; /* fetch just once */ xid = UINT32_ACCESS_ONCE(pgxact->xmin);
if (TransactionIdIsNormal(xid) && if (TransactionIdIsNormal(xid) &&
NormalTransactionIdPrecedes(xid, globalxmin)) NormalTransactionIdPrecedes(xid, globalxmin))
globalxmin = xid; globalxmin = xid;
/* Fetch xid just once - see GetNewTransactionId */ /* Fetch xid just once - see GetNewTransactionId */
xid = pgxact->xid; xid = UINT32_ACCESS_ONCE(pgxact->xid);
/* /*
* If the transaction has no XID assigned, we can skip it; it * If the transaction has no XID assigned, we can skip it; it
...@@ -1652,7 +1660,9 @@ GetSnapshotData(Snapshot snapshot) ...@@ -1652,7 +1660,9 @@ GetSnapshotData(Snapshot snapshot)
if (nxids > 0) if (nxids > 0)
{ {
volatile PGPROC *proc = &allProcs[pgprocno]; PGPROC *proc = &allProcs[pgprocno];
pg_read_barrier(); /* pairs with GetNewTransactionId */
memcpy(snapshot->subxip + subcount, memcpy(snapshot->subxip + subcount,
(void *) proc->subxids.xids, (void *) proc->subxids.xids,
...@@ -1702,7 +1712,11 @@ GetSnapshotData(Snapshot snapshot) ...@@ -1702,7 +1712,11 @@ GetSnapshotData(Snapshot snapshot)
} }
/* fetch into volatile var while ProcArrayLock is held */ /*
* Fetch into local variable while ProcArrayLock is held - the
* LWLockRelease below is a barrier, ensuring this happens inside the
* lock.
*/
replication_slot_xmin = procArray->replication_slot_xmin; replication_slot_xmin = procArray->replication_slot_xmin;
replication_slot_catalog_xmin = procArray->replication_slot_catalog_xmin; replication_slot_catalog_xmin = procArray->replication_slot_catalog_xmin;
...@@ -1810,8 +1824,8 @@ ProcArrayInstallImportedXmin(TransactionId xmin, ...@@ -1810,8 +1824,8 @@ ProcArrayInstallImportedXmin(TransactionId xmin,
for (index = 0; index < arrayP->numProcs; index++) for (index = 0; index < arrayP->numProcs; index++)
{ {
int pgprocno = arrayP->pgprocnos[index]; int pgprocno = arrayP->pgprocnos[index];
volatile PGPROC *proc = &allProcs[pgprocno]; PGPROC *proc = &allProcs[pgprocno];
volatile PGXACT *pgxact = &allPgXact[pgprocno]; PGXACT *pgxact = &allPgXact[pgprocno];
TransactionId xid; TransactionId xid;
/* Ignore procs running LAZY VACUUM */ /* Ignore procs running LAZY VACUUM */
...@@ -1836,7 +1850,7 @@ ProcArrayInstallImportedXmin(TransactionId xmin, ...@@ -1836,7 +1850,7 @@ ProcArrayInstallImportedXmin(TransactionId xmin,
/* /*
* Likewise, let's just make real sure its xmin does cover us. * Likewise, let's just make real sure its xmin does cover us.
*/ */
xid = pgxact->xmin; /* fetch just once */ xid = UINT32_ACCESS_ONCE(pgxact->xmin);
if (!TransactionIdIsNormal(xid) || if (!TransactionIdIsNormal(xid) ||
!TransactionIdPrecedesOrEquals(xid, xmin)) !TransactionIdPrecedesOrEquals(xid, xmin))
continue; continue;
...@@ -1872,7 +1886,7 @@ ProcArrayInstallRestoredXmin(TransactionId xmin, PGPROC *proc) ...@@ -1872,7 +1886,7 @@ ProcArrayInstallRestoredXmin(TransactionId xmin, PGPROC *proc)
{ {
bool result = false; bool result = false;
TransactionId xid; TransactionId xid;
volatile PGXACT *pgxact; PGXACT *pgxact;
Assert(TransactionIdIsNormal(xmin)); Assert(TransactionIdIsNormal(xmin));
Assert(proc != NULL); Assert(proc != NULL);
...@@ -1888,7 +1902,7 @@ ProcArrayInstallRestoredXmin(TransactionId xmin, PGPROC *proc) ...@@ -1888,7 +1902,7 @@ ProcArrayInstallRestoredXmin(TransactionId xmin, PGPROC *proc)
* can't go backwards. Also, make sure it's running in the same database, * can't go backwards. Also, make sure it's running in the same database,
* so that the per-database xmin cannot go backwards. * so that the per-database xmin cannot go backwards.
*/ */
xid = pgxact->xmin; /* fetch just once */ xid = UINT32_ACCESS_ONCE(pgxact->xmin);
if (proc->databaseId == MyDatabaseId && if (proc->databaseId == MyDatabaseId &&
TransactionIdIsNormal(xid) && TransactionIdIsNormal(xid) &&
TransactionIdPrecedesOrEquals(xid, xmin)) TransactionIdPrecedesOrEquals(xid, xmin))
...@@ -1995,11 +2009,11 @@ GetRunningTransactionData(void) ...@@ -1995,11 +2009,11 @@ GetRunningTransactionData(void)
for (index = 0; index < arrayP->numProcs; index++) for (index = 0; index < arrayP->numProcs; index++)
{ {
int pgprocno = arrayP->pgprocnos[index]; int pgprocno = arrayP->pgprocnos[index];
volatile PGXACT *pgxact = &allPgXact[pgprocno]; PGXACT *pgxact = &allPgXact[pgprocno];
TransactionId xid; TransactionId xid;
/* Fetch xid just once - see GetNewTransactionId */ /* Fetch xid just once - see GetNewTransactionId */
xid = pgxact->xid; xid = UINT32_ACCESS_ONCE(pgxact->xid);
/* /*
* We don't need to store transactions that don't have a TransactionId * We don't need to store transactions that don't have a TransactionId
...@@ -2039,8 +2053,8 @@ GetRunningTransactionData(void) ...@@ -2039,8 +2053,8 @@ GetRunningTransactionData(void)
for (index = 0; index < arrayP->numProcs; index++) for (index = 0; index < arrayP->numProcs; index++)
{ {
int pgprocno = arrayP->pgprocnos[index]; int pgprocno = arrayP->pgprocnos[index];
volatile PGPROC *proc = &allProcs[pgprocno]; PGPROC *proc = &allProcs[pgprocno];
volatile PGXACT *pgxact = &allPgXact[pgprocno]; PGXACT *pgxact = &allPgXact[pgprocno];
int nxids; int nxids;
/* /*
...@@ -2050,6 +2064,9 @@ GetRunningTransactionData(void) ...@@ -2050,6 +2064,9 @@ GetRunningTransactionData(void)
nxids = pgxact->nxids; nxids = pgxact->nxids;
if (nxids > 0) if (nxids > 0)
{ {
/* barrier not really required, as XidGenLock is held, but ... */
pg_read_barrier(); /* pairs with GetNewTransactionId */
memcpy(&xids[count], (void *) proc->subxids.xids, memcpy(&xids[count], (void *) proc->subxids.xids,
nxids * sizeof(TransactionId)); nxids * sizeof(TransactionId));
count += nxids; count += nxids;
...@@ -2131,11 +2148,11 @@ GetOldestActiveTransactionId(void) ...@@ -2131,11 +2148,11 @@ GetOldestActiveTransactionId(void)
for (index = 0; index < arrayP->numProcs; index++) for (index = 0; index < arrayP->numProcs; index++)
{ {
int pgprocno = arrayP->pgprocnos[index]; int pgprocno = arrayP->pgprocnos[index];
volatile PGXACT *pgxact = &allPgXact[pgprocno]; PGXACT *pgxact = &allPgXact[pgprocno];
TransactionId xid; TransactionId xid;
/* Fetch xid just once - see GetNewTransactionId */ /* Fetch xid just once - see GetNewTransactionId */
xid = pgxact->xid; xid = UINT32_ACCESS_ONCE(pgxact->xid);
if (!TransactionIdIsNormal(xid)) if (!TransactionIdIsNormal(xid))
continue; continue;
...@@ -2229,11 +2246,11 @@ GetOldestSafeDecodingTransactionId(bool catalogOnly) ...@@ -2229,11 +2246,11 @@ GetOldestSafeDecodingTransactionId(bool catalogOnly)
for (index = 0; index < arrayP->numProcs; index++) for (index = 0; index < arrayP->numProcs; index++)
{ {
int pgprocno = arrayP->pgprocnos[index]; int pgprocno = arrayP->pgprocnos[index];
volatile PGXACT *pgxact = &allPgXact[pgprocno]; PGXACT *pgxact = &allPgXact[pgprocno];
TransactionId xid; TransactionId xid;
/* Fetch xid just once - see GetNewTransactionId */ /* Fetch xid just once - see GetNewTransactionId */
xid = pgxact->xid; xid = UINT32_ACCESS_ONCE(pgxact->xid);
if (!TransactionIdIsNormal(xid)) if (!TransactionIdIsNormal(xid))
continue; continue;
...@@ -2283,8 +2300,8 @@ GetVirtualXIDsDelayingChkpt(int *nvxids) ...@@ -2283,8 +2300,8 @@ GetVirtualXIDsDelayingChkpt(int *nvxids)
for (index = 0; index < arrayP->numProcs; index++) for (index = 0; index < arrayP->numProcs; index++)
{ {
int pgprocno = arrayP->pgprocnos[index]; int pgprocno = arrayP->pgprocnos[index];
volatile PGPROC *proc = &allProcs[pgprocno]; PGPROC *proc = &allProcs[pgprocno];
volatile PGXACT *pgxact = &allPgXact[pgprocno]; PGXACT *pgxact = &allPgXact[pgprocno];
if (pgxact->delayChkpt) if (pgxact->delayChkpt)
{ {
...@@ -2323,8 +2340,8 @@ HaveVirtualXIDsDelayingChkpt(VirtualTransactionId *vxids, int nvxids) ...@@ -2323,8 +2340,8 @@ HaveVirtualXIDsDelayingChkpt(VirtualTransactionId *vxids, int nvxids)
for (index = 0; index < arrayP->numProcs; index++) for (index = 0; index < arrayP->numProcs; index++)
{ {
int pgprocno = arrayP->pgprocnos[index]; int pgprocno = arrayP->pgprocnos[index];
volatile PGPROC *proc = &allProcs[pgprocno]; PGPROC *proc = &allProcs[pgprocno];
volatile PGXACT *pgxact = &allPgXact[pgprocno]; PGXACT *pgxact = &allPgXact[pgprocno];
VirtualTransactionId vxid; VirtualTransactionId vxid;
GET_VXID_FROM_PGPROC(vxid, *proc); GET_VXID_FROM_PGPROC(vxid, *proc);
...@@ -2433,8 +2450,8 @@ BackendXidGetPid(TransactionId xid) ...@@ -2433,8 +2450,8 @@ BackendXidGetPid(TransactionId xid)
for (index = 0; index < arrayP->numProcs; index++) for (index = 0; index < arrayP->numProcs; index++)
{ {
int pgprocno = arrayP->pgprocnos[index]; int pgprocno = arrayP->pgprocnos[index];
volatile PGPROC *proc = &allProcs[pgprocno]; PGPROC *proc = &allProcs[pgprocno];
volatile PGXACT *pgxact = &allPgXact[pgprocno]; PGXACT *pgxact = &allPgXact[pgprocno];
if (pgxact->xid == xid) if (pgxact->xid == xid)
{ {
...@@ -2505,8 +2522,8 @@ GetCurrentVirtualXIDs(TransactionId limitXmin, bool excludeXmin0, ...@@ -2505,8 +2522,8 @@ GetCurrentVirtualXIDs(TransactionId limitXmin, bool excludeXmin0,
for (index = 0; index < arrayP->numProcs; index++) for (index = 0; index < arrayP->numProcs; index++)
{ {
int pgprocno = arrayP->pgprocnos[index]; int pgprocno = arrayP->pgprocnos[index];
volatile PGPROC *proc = &allProcs[pgprocno]; PGPROC *proc = &allProcs[pgprocno];
volatile PGXACT *pgxact = &allPgXact[pgprocno]; PGXACT *pgxact = &allPgXact[pgprocno];
if (proc == MyProc) if (proc == MyProc)
continue; continue;
...@@ -2517,7 +2534,7 @@ GetCurrentVirtualXIDs(TransactionId limitXmin, bool excludeXmin0, ...@@ -2517,7 +2534,7 @@ GetCurrentVirtualXIDs(TransactionId limitXmin, bool excludeXmin0,
if (allDbs || proc->databaseId == MyDatabaseId) if (allDbs || proc->databaseId == MyDatabaseId)
{ {
/* Fetch xmin just once - might change on us */ /* Fetch xmin just once - might change on us */
TransactionId pxmin = pgxact->xmin; TransactionId pxmin = UINT32_ACCESS_ONCE(pgxact->xmin);
if (excludeXmin0 && !TransactionIdIsValid(pxmin)) if (excludeXmin0 && !TransactionIdIsValid(pxmin))
continue; continue;
...@@ -2602,8 +2619,8 @@ GetConflictingVirtualXIDs(TransactionId limitXmin, Oid dbOid) ...@@ -2602,8 +2619,8 @@ GetConflictingVirtualXIDs(TransactionId limitXmin, Oid dbOid)
for (index = 0; index < arrayP->numProcs; index++) for (index = 0; index < arrayP->numProcs; index++)
{ {
int pgprocno = arrayP->pgprocnos[index]; int pgprocno = arrayP->pgprocnos[index];
volatile PGPROC *proc = &allProcs[pgprocno]; PGPROC *proc = &allProcs[pgprocno];
volatile PGXACT *pgxact = &allPgXact[pgprocno]; PGXACT *pgxact = &allPgXact[pgprocno];
/* Exclude prepared transactions */ /* Exclude prepared transactions */
if (proc->pid == 0) if (proc->pid == 0)
...@@ -2613,7 +2630,7 @@ GetConflictingVirtualXIDs(TransactionId limitXmin, Oid dbOid) ...@@ -2613,7 +2630,7 @@ GetConflictingVirtualXIDs(TransactionId limitXmin, Oid dbOid)
proc->databaseId == dbOid) proc->databaseId == dbOid)
{ {
/* Fetch xmin just once - can't change on us, but good coding */ /* Fetch xmin just once - can't change on us, but good coding */
TransactionId pxmin = pgxact->xmin; TransactionId pxmin = UINT32_ACCESS_ONCE(pgxact->xmin);
/* /*
* We ignore an invalid pxmin because this means that backend has * We ignore an invalid pxmin because this means that backend has
...@@ -2661,7 +2678,7 @@ CancelVirtualTransaction(VirtualTransactionId vxid, ProcSignalReason sigmode) ...@@ -2661,7 +2678,7 @@ CancelVirtualTransaction(VirtualTransactionId vxid, ProcSignalReason sigmode)
for (index = 0; index < arrayP->numProcs; index++) for (index = 0; index < arrayP->numProcs; index++)
{ {
int pgprocno = arrayP->pgprocnos[index]; int pgprocno = arrayP->pgprocnos[index];
volatile PGPROC *proc = &allProcs[pgprocno]; PGPROC *proc = &allProcs[pgprocno];
VirtualTransactionId procvxid; VirtualTransactionId procvxid;
GET_VXID_FROM_PGPROC(procvxid, *proc); GET_VXID_FROM_PGPROC(procvxid, *proc);
...@@ -2716,8 +2733,8 @@ MinimumActiveBackends(int min) ...@@ -2716,8 +2733,8 @@ MinimumActiveBackends(int min)
for (index = 0; index < arrayP->numProcs; index++) for (index = 0; index < arrayP->numProcs; index++)
{ {
int pgprocno = arrayP->pgprocnos[index]; int pgprocno = arrayP->pgprocnos[index];
volatile PGPROC *proc = &allProcs[pgprocno]; PGPROC *proc = &allProcs[pgprocno];
volatile PGXACT *pgxact = &allPgXact[pgprocno]; PGXACT *pgxact = &allPgXact[pgprocno];
/* /*
* Since we're not holding a lock, need to be prepared to deal with * Since we're not holding a lock, need to be prepared to deal with
...@@ -2763,7 +2780,7 @@ CountDBBackends(Oid databaseid) ...@@ -2763,7 +2780,7 @@ CountDBBackends(Oid databaseid)
for (index = 0; index < arrayP->numProcs; index++) for (index = 0; index < arrayP->numProcs; index++)
{ {
int pgprocno = arrayP->pgprocnos[index]; int pgprocno = arrayP->pgprocnos[index];
volatile PGPROC *proc = &allProcs[pgprocno]; PGPROC *proc = &allProcs[pgprocno];
if (proc->pid == 0) if (proc->pid == 0)
continue; /* do not count prepared xacts */ continue; /* do not count prepared xacts */
...@@ -2793,7 +2810,7 @@ CountDBConnections(Oid databaseid) ...@@ -2793,7 +2810,7 @@ CountDBConnections(Oid databaseid)
for (index = 0; index < arrayP->numProcs; index++) for (index = 0; index < arrayP->numProcs; index++)
{ {
int pgprocno = arrayP->pgprocnos[index]; int pgprocno = arrayP->pgprocnos[index];
volatile PGPROC *proc = &allProcs[pgprocno]; PGPROC *proc = &allProcs[pgprocno];
if (proc->pid == 0) if (proc->pid == 0)
continue; /* do not count prepared xacts */ continue; /* do not count prepared xacts */
...@@ -2825,7 +2842,7 @@ CancelDBBackends(Oid databaseid, ProcSignalReason sigmode, bool conflictPending) ...@@ -2825,7 +2842,7 @@ CancelDBBackends(Oid databaseid, ProcSignalReason sigmode, bool conflictPending)
for (index = 0; index < arrayP->numProcs; index++) for (index = 0; index < arrayP->numProcs; index++)
{ {
int pgprocno = arrayP->pgprocnos[index]; int pgprocno = arrayP->pgprocnos[index];
volatile PGPROC *proc = &allProcs[pgprocno]; PGPROC *proc = &allProcs[pgprocno];
if (databaseid == InvalidOid || proc->databaseId == databaseid) if (databaseid == InvalidOid || proc->databaseId == databaseid)
{ {
...@@ -2864,7 +2881,7 @@ CountUserBackends(Oid roleid) ...@@ -2864,7 +2881,7 @@ CountUserBackends(Oid roleid)
for (index = 0; index < arrayP->numProcs; index++) for (index = 0; index < arrayP->numProcs; index++)
{ {
int pgprocno = arrayP->pgprocnos[index]; int pgprocno = arrayP->pgprocnos[index];
volatile PGPROC *proc = &allProcs[pgprocno]; PGPROC *proc = &allProcs[pgprocno];
if (proc->pid == 0) if (proc->pid == 0)
continue; /* do not count prepared xacts */ continue; /* do not count prepared xacts */
...@@ -2927,8 +2944,8 @@ CountOtherDBBackends(Oid databaseId, int *nbackends, int *nprepared) ...@@ -2927,8 +2944,8 @@ CountOtherDBBackends(Oid databaseId, int *nbackends, int *nprepared)
for (index = 0; index < arrayP->numProcs; index++) for (index = 0; index < arrayP->numProcs; index++)
{ {
int pgprocno = arrayP->pgprocnos[index]; int pgprocno = arrayP->pgprocnos[index];
volatile PGPROC *proc = &allProcs[pgprocno]; PGPROC *proc = &allProcs[pgprocno];
volatile PGXACT *pgxact = &allPgXact[pgprocno]; PGXACT *pgxact = &allPgXact[pgprocno];
if (proc->databaseId != databaseId) if (proc->databaseId != databaseId)
continue; continue;
...@@ -3017,6 +3034,7 @@ ProcArrayGetReplicationSlotXmin(TransactionId *xmin, ...@@ -3017,6 +3034,7 @@ ProcArrayGetReplicationSlotXmin(TransactionId *xmin,
#define XidCacheRemove(i) \ #define XidCacheRemove(i) \
do { \ do { \
MyProc->subxids.xids[i] = MyProc->subxids.xids[MyPgXact->nxids - 1]; \ MyProc->subxids.xids[i] = MyProc->subxids.xids[MyPgXact->nxids - 1]; \
pg_write_barrier(); \
MyPgXact->nxids--; \ MyPgXact->nxids--; \
} while (0) } while (0)
...@@ -3044,6 +3062,11 @@ XidCacheRemoveRunningXids(TransactionId xid, ...@@ -3044,6 +3062,11 @@ XidCacheRemoveRunningXids(TransactionId xid,
* possible this could be relaxed since we know this routine is only used * possible this could be relaxed since we know this routine is only used
* to abort subtransactions, but pending closer analysis we'd best be * to abort subtransactions, but pending closer analysis we'd best be
* conservative. * conservative.
*
* Note that we do not have to be careful about memory ordering of our own
* reads wrt. GetNewTransactionId() here - only this process can modify
* relevant fields of MyProc/MyPgXact. But we do have to be careful about
* our own writes being well ordered.
*/ */
LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE); LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE);
...@@ -3401,8 +3424,7 @@ ExpireOldKnownAssignedTransactionIds(TransactionId xid) ...@@ -3401,8 +3424,7 @@ ExpireOldKnownAssignedTransactionIds(TransactionId xid)
static void static void
KnownAssignedXidsCompress(bool force) KnownAssignedXidsCompress(bool force)
{ {
/* use volatile pointer to prevent code rearrangement */ ProcArrayStruct *pArray = procArray;
volatile ProcArrayStruct *pArray = procArray;
int head, int head,
tail; tail;
int compress_index; int compress_index;
...@@ -3465,8 +3487,7 @@ static void ...@@ -3465,8 +3487,7 @@ static void
KnownAssignedXidsAdd(TransactionId from_xid, TransactionId to_xid, KnownAssignedXidsAdd(TransactionId from_xid, TransactionId to_xid,
bool exclusive_lock) bool exclusive_lock)
{ {
/* use volatile pointer to prevent code rearrangement */ ProcArrayStruct *pArray = procArray;
volatile ProcArrayStruct *pArray = procArray;
TransactionId next_xid; TransactionId next_xid;
int head, int head,
tail; tail;
...@@ -3583,8 +3604,7 @@ KnownAssignedXidsAdd(TransactionId from_xid, TransactionId to_xid, ...@@ -3583,8 +3604,7 @@ KnownAssignedXidsAdd(TransactionId from_xid, TransactionId to_xid,
static bool static bool
KnownAssignedXidsSearch(TransactionId xid, bool remove) KnownAssignedXidsSearch(TransactionId xid, bool remove)
{ {
/* use volatile pointer to prevent code rearrangement */ ProcArrayStruct *pArray = procArray;
volatile ProcArrayStruct *pArray = procArray;
int first, int first,
last; last;
int head; int head;
...@@ -3738,8 +3758,7 @@ KnownAssignedXidsRemoveTree(TransactionId xid, int nsubxids, ...@@ -3738,8 +3758,7 @@ KnownAssignedXidsRemoveTree(TransactionId xid, int nsubxids,
static void static void
KnownAssignedXidsRemovePreceding(TransactionId removeXid) KnownAssignedXidsRemovePreceding(TransactionId removeXid)
{ {
/* use volatile pointer to prevent code rearrangement */ ProcArrayStruct *pArray = procArray;
volatile ProcArrayStruct *pArray = procArray;
int count = 0; int count = 0;
int head, int head,
tail, tail,
...@@ -3924,8 +3943,7 @@ KnownAssignedXidsGetOldestXmin(void) ...@@ -3924,8 +3943,7 @@ KnownAssignedXidsGetOldestXmin(void)
static void static void
KnownAssignedXidsDisplay(int trace_level) KnownAssignedXidsDisplay(int trace_level)
{ {
/* use volatile pointer to prevent code rearrangement */ ProcArrayStruct *pArray = procArray;
volatile ProcArrayStruct *pArray = procArray;
StringInfoData buf; StringInfoData buf;
int head, int head,
tail, tail,
...@@ -3963,8 +3981,7 @@ KnownAssignedXidsDisplay(int trace_level) ...@@ -3963,8 +3981,7 @@ KnownAssignedXidsDisplay(int trace_level)
static void static void
KnownAssignedXidsReset(void) KnownAssignedXidsReset(void)
{ {
/* use volatile pointer to prevent code rearrangement */ ProcArrayStruct *pArray = procArray;
volatile ProcArrayStruct *pArray = procArray;
LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE); LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment