From e4b15b94993555c79d30f0386250973e6b0a2904 Mon Sep 17 00:00:00 2001
From: Shihao <zhong950419@gmail.com>
Date: Sun, 27 Sep 2026 08:41:43 -0700
Subject: [PATCH v3] Reset waitStart when a lock wait ends

PGPROC->waitStart could keep the start time of a lock wait that had
already ended, in two ways.

RemoveFromWaitQueue() did not clear it, unlike ProcWakeup(). It ends
the wait on lock_timeout, query cancel and deadlock.

A waiter also releases the partition lock before it stores waitStart,
in ProcSleep() or, for the startup process, in
ResolveRecoveryConflictWithLock(). If ProcWakeup() grants the lock in
between, it clears waitStart too early and the waiter's value stays.

A regular backend overwrites the value at its next lock wait, so
pg_locks shows the old time only at the start of that wait. The startup
process stores waitStart only when it reads zero, so there pg_locks
shows the old time for the whole next wait.

Fix by clearing waitStart in RemoveFromWaitQueue(), at the end of
ProcSleep() and in LockErrorCleanup(). The last one covers a cancel
that arrives after the grant, where ProcSleep() errors out before its
end.

Oversight in 46d6e5f5679.

Reported-by: Alex Shapalov <shapalov@gmail.com>
Reported-by: Fujii Masao <masao.fujii@gmail.com>
Discussion: https://postgr.es/m/CAPrb+Q+XN=sNusXiUeWmMo2H7Qgq3Y4uPekSSLkHcnCyf7GhXg@mail.gmail.com
Discussion: https://postgr.es/m/CAGRkXqQLxZBr-ouVrtaX2utMggi4+TiVMbgH04b_0JLgKryxbA@mail.gmail.com
Backpatch-through: 14
---
 src/backend/storage/lmgr/lock.c |  1 +
 src/backend/storage/lmgr/proc.c | 10 ++++++++++
 2 files changed, 11 insertions(+)

diff --git a/src/backend/storage/lmgr/lock.c b/src/backend/storage/lmgr/lock.c
index 00978168bbf..52fdb656ae8 100644
--- a/src/backend/storage/lmgr/lock.c
+++ b/src/backend/storage/lmgr/lock.c
@@ -2105,6 +2105,7 @@ RemoveFromWaitQueue(PGPROC *proc, uint32 hashcode)
 	proc->waitLock = NULL;
 	proc->waitProcLock = NULL;
 	proc->waitStatus = PROC_WAIT_STATUS_ERROR;
+	pg_atomic_write_u64(&proc->waitStart, 0);
 
 	/*
 	 * Delete the proclock immediately if it represents no already-held locks.
diff --git a/src/backend/storage/lmgr/proc.c b/src/backend/storage/lmgr/proc.c
index 91fe2766640..f93203916c1 100644
--- a/src/backend/storage/lmgr/proc.c
+++ b/src/backend/storage/lmgr/proc.c
@@ -871,6 +871,9 @@ LockErrorCleanup(void)
 			GrantAwaitedLock();
 	}
 
+	/* Clear waitStart, for the same reason as at the end of ProcSleep() */
+	pg_atomic_write_u64(&MyProc->waitStart, 0);
+
 	ResetAwaitedLock();
 
 	LWLockRelease(partitionLock);
@@ -1744,6 +1747,13 @@ ProcSleep(LOCALLOCK *locallock)
 		}
 	} while (myWaitStatus == PROC_WAIT_STATUS_WAITING);
 
+	/*
+	 * The wait is over, so clear waitStart.  ProcWakeup() clears it too, but
+	 * it can run before we set waitStart, and then our value would be left
+	 * behind.
+	 */
+	pg_atomic_write_u64(&MyProc->waitStart, 0);
+
 	/*
 	 * Disable the timers, if they are still running.  As in LockErrorCleanup,
 	 * we must preserve the LOCK_TIMEOUT indicator flag: if a lock timeout has
-- 
2.37.1 (Apple Git-137.1)

