PGPROC *MyProc = NULL;
PGXACT *MyPgXact = NULL;
+/*
+ * If we're not in a lock group, LockGroupLeader should be set to the
+ * same value as MyProc, if this backend intends to do any heavyweight locking.
+ * This also occurs when we are a lock group leader. When we are a lock
+ * group following, LockGroupLeader points to the group leader's PGPROC.
+ */
+PGPROC *LockGroupLeader = NULL;
+
/*
* This spinlock protects the freelist of recycled PGPROC structures.
* We cannot use an LWLock because the LWLock manager depends on already
/* Mark this volatile because it can be changed by signal handler */
static volatile DeadLockState deadlock_state = DS_NOT_YET_CHECKED;
+/* Are we a lock group leader? */
+static bool IsLockGroupLeader = false;
+
static void RemoveProcFromArray(int code, Datum arg);
static void ProcKill(int code, Datum arg);
InitSharedLatch(&(procs[i].procLatch));
procs[i].backendLock = LWLockAssign();
}
+
procs[i].pgprocno = i;
+ procs[i].lockGroupLeader = &procs[i];
/*
* Newly created PGPROCs for normal backends, autovacuum and bgworkers
errmsg("sorry, too many clients already")));
}
MyPgXact = &ProcGlobal->allPgXact[MyProc->pgprocno];
+ LockGroupLeader = MyProc;
/*
* Cross-check that the PGPROC is of the type we expect; if this were
MyProc->lwWaitLink = NULL;
MyProc->waitLock = NULL;
MyProc->waitProcLock = NULL;
+ Assert(MyProc->lockGroupLeader == MyProc);
#ifdef USE_ASSERT_CHECKING
{
int i;
MyProc->lwWaitLink = NULL;
MyProc->waitLock = NULL;
MyProc->waitProcLock = NULL;
+ Assert(MyProc->lockGroupLeader == MyProc);
#ifdef USE_ASSERT_CHECKING
{
int i;
/* use volatile pointer to prevent code rearrangement */
volatile PROC_HDR *procglobal = ProcGlobal;
PGPROC *proc;
- PGPROC * volatile * procgloballist;
Assert(MyProc != NULL);
/* Make sure we're out of the sync rep lists */
SyncRepCleanupAtProcExit();
-#ifdef USE_ASSERT_CHECKING
- {
- int i;
-
- /* Last process should have released all locks. */
- for (i = 0; i < NUM_LOCK_PARTITIONS; i++)
- Assert(SHMQueueEmpty(&(MyProc->myProcLocks[i])));
- }
-#endif
-
/*
* Release any LW locks I am holding. There really shouldn't be any, but
* it's cheap to check again before we cut the knees off the LWLock
if (MyReplicationSlot != NULL)
ReplicationSlotRelease();
+ /* If we're a lock group member, detach from the lock group. */
+ if (LockGroupLeader != MyProc)
+ {
+ int members;
+
+ LWLockAcquire(LockGroupLeader->backendLock, LW_EXCLUSIVE);
+ members = --LockGroupLeader->lockGroupMembers;
+ LWLockRelease(LockGroupLeader->backendLock);
+
+ LWLockAcquire(MyProc->backendLock, LW_EXCLUSIVE);
+ MyProc->lockGroupLeader = MyProc;
+ LWLockRelease(MyProc->backendLock);
+
+ /* If we're the last member of the lock group, detach the PGPROC. */
+ if (members == 0)
+ {
+ PGPROC * volatile * procgloballist;
+
+#ifdef USE_ASSERT_CHECKING
+ {
+ int i;
+
+ /* Last process should have released all locks. */
+ for (i = 0; i < NUM_LOCK_PARTITIONS; i++)
+ Assert(SHMQueueEmpty(&(LockGroupLeader->myProcLocks[i])));
+ }
+#endif
+
+ procgloballist = LockGroupLeader->procgloballist;
+
+ SpinLockAcquire(ProcStructLock);
+
+ /* Return PGPROC structure to appropriate freelist */
+ LockGroupLeader->links.next = (SHM_QUEUE *) *procgloballist;
+ *procgloballist = LockGroupLeader;
+
+ /* Update shared estimate of spins_per_delay */
+ procglobal->spins_per_delay =
+ update_spins_per_delay(procglobal->spins_per_delay);
+
+ SpinLockRelease(ProcStructLock);
+ }
+ }
+
/*
* Clear MyProc first; then disown the process latch. This is so that
* signal handlers won't try to clear the process latch after it's no
MyProc = NULL;
DisownLatch(&proc->procLatch);
- procgloballist = proc->procgloballist;
- SpinLockAcquire(ProcStructLock);
+ /*
+ * If we are a lock group leader, we need to check whether any other
+ * group members are active. If not, we can declare ourselves to no longer
+ * be a lock group leader, allowing our PGPROC to be recycled
+ * immediately.
+ */
+ if (IsLockGroupLeader)
+ {
+ int members;
- /* Return PGPROC structure (and semaphore) to appropriate freelist */
- proc->links.next = (SHM_QUEUE *) *procgloballist;
- *procgloballist = proc;
+ LWLockAcquire(proc->backendLock, LW_EXCLUSIVE);
+ members = --proc->lockGroupMembers;
+ LWLockRelease(proc->backendLock);
- /* Update shared estimate of spins_per_delay */
- procglobal->spins_per_delay = update_spins_per_delay(procglobal->spins_per_delay);
+ if (members == 0)
+ IsLockGroupLeader = false;
+ }
- SpinLockRelease(ProcStructLock);
+ /*
+ * If we were never a lock group leader or have managed to give up that
+ * designation, then we can immediately release our PGPROC. If not, then
+ * the last group member will do that on exit.
+ */
+ if (!IsLockGroupLeader)
+ {
+ PGPROC * volatile * procgloballist;
+
+#ifdef USE_ASSERT_CHECKING
+ {
+ int i;
+
+ /* Last process should have released all locks. */
+ for (i = 0; i < NUM_LOCK_PARTITIONS; i++)
+ Assert(SHMQueueEmpty(&(proc->myProcLocks[i])));
+ }
+#endif
+
+ procgloballist = proc->procgloballist;
+ SpinLockAcquire(ProcStructLock);
+
+ /* Return PGPROC structure (and semaphore) to appropriate freelist */
+ proc->links.next = (SHM_QUEUE *) *procgloballist;
+ *procgloballist = proc;
+
+ /* Update shared estimate of spins_per_delay */
+ procglobal->spins_per_delay =
+ update_spins_per_delay(procglobal->spins_per_delay);
+
+ SpinLockRelease(ProcStructLock);
+ }
/*
* This process is no longer present in shared memory in any meaningful
LWLockRelease(LockHashPartitionLockByIndex(i));
}
+/*
+ * BecomeLockGroupLeader - designate process as lock group leader
+ *
+ * Once this function has returned, other processes can join the lock group
+ * by calling BecomLockGroupFollower.
+ */
+void
+BecomeLockGroupLeader(void)
+{
+ /* Can't be leader and follower. */
+ Assert(LockGroupLeader == MyProc);
+
+ /* This can be called more than once; but we must not redo the work. */
+ if (!IsLockGroupLeader)
+ {
+ LWLockAcquire(MyProc->backendLock, LW_EXCLUSIVE);
+ Assert(MyProc->lockGroupMembers == 0);
+ Assert(MyProc->lockGroupLeader == MyProc);
+ MyProc->lockGroupLeaderIdentifier = MyProcPid;
+ MyProc->lockGroupMembers = 1;
+ LWLockRelease(MyProc->backendLock);
+ IsLockGroupLeader = true;
+ }
+}
+
+/*
+ * BecomeLockGroupFollower - designate process as lock group follower
+ *
+ * This is pretty straightforward except for the possibility that the leader
+ * whose group we're trying to join might exit before we manage to do so;
+ * and the PGPROC might get recycled for an unrelated process. To avoid
+ * that, we require the caller to pass the PID of the intended PGPROC as
+ * an interlock. Returns true if we successfully join the intended lock
+ * group, and false if not.
+ */
+bool
+BecomeLockGroupFollower(PGPROC *leader, int pid)
+{
+ bool ok = false;
+
+ /* Can't be leader and follower. */
+ Assert(!IsLockGroupLeader);
+
+ /* Can't become a follower if we already are one. */
+ Assert(MyProc == LockGroupLeader);
+
+ /* Can't follow ourselves. */
+ Assert(MyProc != leader);
+
+ /* Try to join the group. */
+ LWLockAcquire(leader->backendLock, LW_EXCLUSIVE);
+ if (leader->lockGroupMembers > 0 &&
+ leader->lockGroupLeaderIdentifier == pid)
+ {
+ ok = true;
+ leader->lockGroupMembers++;
+ LockGroupLeader = leader;
+ }
+ LWLockRelease(leader->backendLock);
+
+ /* Advertise our new leader. */
+ LWLockAcquire(MyProc->backendLock, LW_EXCLUSIVE);
+ MyProc->lockGroupLeader = leader;
+ LWLockRelease(MyProc->backendLock);
+
+ return ok;
+}
+
/*
* ProcWaitForSignal - wait for a signal from another backend.