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);
......
This diff is collapsed.
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