Relax lock level for setting PGPROC->statusFlags

We don't actually need a lock to set PGPROC->statusFlags itself; what we
do need is a shared lock on either XidGenLock or ProcArrayLock in order to
ensure MyProc->pgxactoff keeps still while we modify the mirror array in
ProcGlobal->statusFlags.  Some places were using an exclusive lock for
that, which is excessive.  Relax those to use shared lock only.

procarray.c has a couple of places with somewhat brittle assumptions
about PGPROC changes: ProcArrayEndTransaction uses only shared lock, so
it's permissible to change MyProc only.  On the other hand,
ProcArrayEndTransactionInternal also changes other procs, so it must
hold exclusive lock.  Add asserts to ensure those assumptions continue
to hold.

Author: Álvaro Herrera <alvherre@alvh.no-ip.org>
Reviewed-by: Michael Paquier <michael@paquier.xyz>
Discussion: https://postgr.es/m/20201117155501.GA13805@alvherre.pgsql
This commit is contained in:
Alvaro Herrera 2020-11-18 13:24:22 -03:00
parent 2cccb627f1
commit 27838981be
No known key found for this signature in database
GPG Key ID: 1C20ACB9D5C564AE
6 changed files with 16 additions and 5 deletions

View File

@ -1741,7 +1741,7 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams *params)
* MyProc->xid/xmin, otherwise GetOldestNonRemovableTransactionId()
* might appear to go backwards, which is probably Not Good.
*/
LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE);
LWLockAcquire(ProcArrayLock, LW_SHARED);
MyProc->statusFlags |= PROC_IN_VACUUM;
if (params->is_wraparound)
MyProc->statusFlags |= PROC_VACUUM_FOR_WRAPAROUND;

View File

@ -181,7 +181,7 @@ StartupDecodingContext(List *output_plugin_options,
*/
if (!IsTransactionOrTransactionBlock())
{
LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE);
LWLockAcquire(ProcArrayLock, LW_SHARED);
MyProc->statusFlags |= PROC_IN_LOGICAL_DECODING;
ProcGlobal->statusFlags[MyProc->pgxactoff] = MyProc->statusFlags;
LWLockRelease(ProcArrayLock);

View File

@ -527,7 +527,7 @@ ReplicationSlotRelease(void)
MyReplicationSlot = NULL;
/* might not have been set when we've been a plain slot */
LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE);
LWLockAcquire(ProcArrayLock, LW_SHARED);
MyProc->statusFlags &= ~PROC_IN_LOGICAL_DECODING;
ProcGlobal->statusFlags[MyProc->pgxactoff] = MyProc->statusFlags;
LWLockRelease(ProcArrayLock);

View File

@ -662,6 +662,8 @@ ProcArrayEndTransaction(PGPROC *proc, TransactionId latestXid)
/* avoid unnecessarily dirtying shared cachelines */
if (proc->statusFlags & PROC_VACUUM_STATE_MASK)
{
/* Only safe to change my own flags with just share lock */
Assert(proc == MyProc);
Assert(!LWLockHeldByMe(ProcArrayLock));
LWLockAcquire(ProcArrayLock, LW_SHARED);
Assert(proc->statusFlags == ProcGlobal->statusFlags[proc->pgxactoff]);
@ -682,7 +684,11 @@ ProcArrayEndTransactionInternal(PGPROC *proc, TransactionId latestXid)
{
size_t pgxactoff = proc->pgxactoff;
Assert(LWLockHeldByMe(ProcArrayLock));
/*
* Note: we need exclusive lock here because we're going to
* change other processes' PGPROC entries.
*/
Assert(LWLockHeldByMeInMode(ProcArrayLock, LW_EXCLUSIVE));
Assert(TransactionIdIsValid(ProcGlobal->xids[pgxactoff]));
Assert(ProcGlobal->xids[pgxactoff] == proc->xid);

View File

@ -623,7 +623,7 @@ FindLockCycleRecurseMember(PGPROC *checkProc,
* because that flag is set at process start and never
* reset. There is logic elsewhere to avoid canceling an
* autovacuum that is working to prevent XID wraparound
* problems (which needs to read a different vacuumFlag
* problems (which needs to read a different statusFlags
* bit), but we don't do that here to avoid grabbing
* ProcArrayLock.
*/

View File

@ -98,6 +98,11 @@ typedef enum
* The semaphore and lock-activity fields in a prepared-xact PGPROC are unused,
* but its myProcLocks[] lists are valid.
*
* We allow many fields of this struct to be accessed without locks, such as
* statusFlags or delayChkpt. However, keep in mind that writing mirrored ones
* (see below) requires holding ProcArrayLock or XidGenLock in at least shared
* mode, so that pgxactoff does not change concurrently.
*
* Mirrored fields:
*
* Some fields in PGPROC (see "mirrored in ..." comment) are mirrored into an