Sorry to bother you, now I know that there is no problem here.
The model for reading and writing of PGXACT::xid and
ShmemVariableCache->latestCompletedXid can be simplified as follows:
backend A backend B
backend C
wlock(XidGenLock); wlock(XidGenLock);
rlock(ProcArrayLock);
write APgXact->xid; write BPgXact->xid; read
latestCompletedXid;
unlock(XidGenLock); unlock(XidGenLock); read
APgXact->xid;
...
read BPgXact->xid;
wlock(ProcArrayLock);
unlock(ProcArrayLock);
write latestCompletedXid;
unlock(ProcArrayLock);
My previous problem was that C might not be able to see the value of
APgXact->xid written by A because there was no obvious acquire-release
operation during this. But now I find that there are already some
acquire-release operations here. Because of the `unlock(XidGenLock)` in A and
`wlock(XidGenLock)` in B and the rules introduced in [Inter-thread
happens-before](https://en.cppreference.com/w/cpp/atomic/memory_order), we can
know that the `write APgXact->xid` in A inter-thread happens before `write
BPgXact->xid` in B. And `write BPgXact->xid` is sequenced before `write
latestCompletedXid` in B according to rules introduced in [Sequenced-before
rules](https://en.cppreference.com/w/cpp/language/eval_order). And similarly
`write latestCompletedXid` in B inter-thread happens before `read
latestCompletedXid` in C. So the `write APgXact->xid` in A inter-thread happens
before `read APgXact->xid` in C. So C can see the value of APgXact->xid written
by A.