@@ -80,13 +80,15 @@ PGPROC *PreparedXactProcs = NULL;
80
80
/* If we are waiting for a lock, this points to the associated LOCALLOCK */
81
81
static LOCALLOCK * lockAwaited = NULL ;
82
82
83
- /* Mark this volatile because it can be changed by signal handler */
84
- static volatile DeadLockState deadlock_state = DS_NOT_YET_CHECKED ;
83
+ static DeadLockState deadlock_state = DS_NOT_YET_CHECKED ;
85
84
85
+ /* Is a deadlock check pending? */
86
+ static volatile sig_atomic_t got_deadlock_timeout ;
86
87
87
88
static void RemoveProcFromArray (int code , Datum arg );
88
89
static void ProcKill (int code , Datum arg );
89
90
static void AuxiliaryProcKill (int code , Datum arg );
91
+ static void CheckDeadLock (void );
90
92
91
93
92
94
/*
@@ -705,16 +707,6 @@ LockErrorCleanup(void)
705
707
706
708
LWLockRelease (partitionLock );
707
709
708
- /*
709
- * We used to do PGSemaphoreReset() here to ensure that our proc's wait
710
- * semaphore is reset to zero. This prevented a leftover wakeup signal
711
- * from remaining in the semaphore if someone else had granted us the lock
712
- * we wanted before we were able to remove ourselves from the wait-list.
713
- * However, now that ProcSleep loops until waitStatus changes, a leftover
714
- * wakeup signal isn't harmful, and it seems not worth expending cycles to
715
- * get rid of a signal that most likely isn't there.
716
- */
717
-
718
710
RESUME_INTERRUPTS ();
719
711
}
720
712
@@ -942,9 +934,6 @@ ProcQueueInit(PROC_QUEUE *queue)
942
934
* we release the partition lock.
943
935
*
944
936
* NOTES: The process queue is now a priority queue for locking.
945
- *
946
- * P() on the semaphore should put us to sleep. The process
947
- * semaphore is normally zero, so when we try to acquire it, we sleep.
948
937
*/
949
938
int
950
939
ProcSleep (LOCALLOCK * locallock , LockMethod lockMethodTable )
@@ -1084,6 +1073,7 @@ ProcSleep(LOCALLOCK *locallock, LockMethod lockMethodTable)
1084
1073
1085
1074
/* Reset deadlock_state before enabling the timeout handler */
1086
1075
deadlock_state = DS_NOT_YET_CHECKED ;
1076
+ got_deadlock_timeout = false;
1087
1077
1088
1078
/*
1089
1079
* Set timer so we can wake up after awhile and check for a deadlock. If a
@@ -1113,32 +1103,37 @@ ProcSleep(LOCALLOCK *locallock, LockMethod lockMethodTable)
1113
1103
enable_timeout_after (DEADLOCK_TIMEOUT , DeadlockTimeout );
1114
1104
1115
1105
/*
1116
- * If someone wakes us between LWLockRelease and PGSemaphoreLock,
1117
- * PGSemaphoreLock will not block. The wakeup is "saved" by the semaphore
1118
- * implementation. While this is normally good, there are cases where a
1119
- * saved wakeup might be leftover from a previous operation (for example,
1120
- * we aborted ProcWaitForSignal just before someone did ProcSendSignal).
1121
- * So, loop to wait again if the waitStatus shows we haven't been granted
1122
- * nor denied the lock yet.
1106
+ * If somebody wakes us between LWLockRelease and WaitLatch, the latch
1107
+ * will not wait. But a set latch does not necessarily mean that the lock
1108
+ * is free now, as there are many other sources for latch sets than
1109
+ * somebody releasing the lock.
1123
1110
*
1124
- * We pass interruptOK = true, which eliminates a window in which
1125
- * cancel/die interrupts would be held off undesirably. This is a promise
1126
- * that we don't mind losing control to a cancel/die interrupt here. We
1127
- * don't, because we have no shared-state-change work to do after being
1128
- * granted the lock (the grantor did it all). We do have to worry about
1129
- * canceling the deadlock timeout and updating the locallock table, but if
1130
- * we lose control to an error, LockErrorCleanup will fix that up.
1111
+ * We process interrupts whenever the latch has been set, so cancel/die
1112
+ * interrupts are processed quickly. This means we must not mind losing
1113
+ * control to a cancel/die interrupt here. We don't, because we have no
1114
+ * shared-state-change work to do after being granted the lock (the
1115
+ * grantor did it all). We do have to worry about canceling the deadlock
1116
+ * timeout and updating the locallock table, but if we lose control to an
1117
+ * error, LockErrorCleanup will fix that up.
1131
1118
*/
1132
1119
do
1133
1120
{
1134
- PGSemaphoreLock (& MyProc -> sem , true);
1121
+ WaitLatch (MyLatch , WL_LATCH_SET , 0 );
1122
+ ResetLatch (MyLatch );
1123
+ /* check for deadlocks first, as that's probably log-worthy */
1124
+ if (got_deadlock_timeout )
1125
+ {
1126
+ CheckDeadLock ();
1127
+ got_deadlock_timeout = false;
1128
+ }
1129
+ CHECK_FOR_INTERRUPTS ();
1135
1130
1136
1131
/*
1137
1132
* waitStatus could change from STATUS_WAITING to something else
1138
1133
* asynchronously. Read it just once per loop to prevent surprising
1139
1134
* behavior (such as missing log messages).
1140
1135
*/
1141
- myWaitStatus = MyProc -> waitStatus ;
1136
+ myWaitStatus = * (( volatile int * ) & MyProc -> waitStatus ) ;
1142
1137
1143
1138
/*
1144
1139
* If we are not deadlocked, but are waiting on an autovacuum-induced
@@ -1435,7 +1430,7 @@ ProcWakeup(PGPROC *proc, int waitStatus)
1435
1430
proc -> waitStatus = waitStatus ;
1436
1431
1437
1432
/* And awaken it */
1438
- PGSemaphoreUnlock (& proc -> sem );
1433
+ SetLatch (& proc -> procLatch );
1439
1434
1440
1435
return retProc ;
1441
1436
}
@@ -1502,17 +1497,13 @@ ProcLockWakeup(LockMethod lockMethodTable, LOCK *lock)
1502
1497
/*
1503
1498
* CheckDeadLock
1504
1499
*
1505
- * We only get to this routine if the DEADLOCK_TIMEOUT fired
1506
- * while waiting for a lock to be released by some other process. Look
1507
- * to see if there's a deadlock; if not, just return and continue waiting.
1508
- * (But signal ProcSleep to log a message, if log_lock_waits is true.)
1509
- * If we have a real deadlock, remove ourselves from the lock's wait queue
1510
- * and signal an error to ProcSleep.
1511
- *
1512
- * NB: this is run inside a signal handler, so be very wary about what is done
1513
- * here or in called routines.
1500
+ * We only get to this routine, if DEADLOCK_TIMEOUT fired while waiting for a
1501
+ * lock to be released by some other process. Check if there's a deadlock; if
1502
+ * not, just return. (But signal ProcSleep to log a message, if
1503
+ * log_lock_waits is true.) If we have a real deadlock, remove ourselves from
1504
+ * the lock's wait queue and signal an error to ProcSleep.
1514
1505
*/
1515
- void
1506
+ static void
1516
1507
CheckDeadLock (void )
1517
1508
{
1518
1509
int i ;
@@ -1570,12 +1561,6 @@ CheckDeadLock(void)
1570
1561
Assert (MyProc -> waitLock != NULL );
1571
1562
RemoveFromWaitQueue (MyProc , LockTagHashCode (& (MyProc -> waitLock -> tag )));
1572
1563
1573
- /*
1574
- * Unlock my semaphore so that the interrupted ProcSleep() call can
1575
- * finish.
1576
- */
1577
- PGSemaphoreUnlock (& MyProc -> sem );
1578
-
1579
1564
/*
1580
1565
* We're done here. Transaction abort caused by the error that
1581
1566
* ProcSleep will raise will cause any other locks we hold to be
@@ -1587,19 +1572,6 @@ CheckDeadLock(void)
1587
1572
* RemoveFromWaitQueue took care of waking up any such processes.
1588
1573
*/
1589
1574
}
1590
- else if (log_lock_waits || deadlock_state == DS_BLOCKED_BY_AUTOVACUUM )
1591
- {
1592
- /*
1593
- * Unlock my semaphore so that the interrupted ProcSleep() call can
1594
- * print the log message (we daren't do it here because we are inside
1595
- * a signal handler). It will then sleep again until someone releases
1596
- * the lock.
1597
- *
1598
- * If blocked by autovacuum, this wakeup will enable ProcSleep to send
1599
- * the canceling signal to the autovacuum worker.
1600
- */
1601
- PGSemaphoreUnlock (& MyProc -> sem );
1602
- }
1603
1575
1604
1576
/*
1605
1577
* And release locks. We do this in reverse order for two reasons: (1)
@@ -1613,22 +1585,39 @@ CheckDeadLock(void)
1613
1585
LWLockRelease (LockHashPartitionLockByIndex (i ));
1614
1586
}
1615
1587
1588
+ /*
1589
+ * CheckDeadLockAlert - Handle the expiry of deadlock_timeout.
1590
+ *
1591
+ * NB: Runs inside a signal handler, be careful.
1592
+ */
1593
+ void
1594
+ CheckDeadLockAlert (void )
1595
+ {
1596
+ int save_errno = errno ;
1597
+
1598
+ got_deadlock_timeout = true;
1599
+ /*
1600
+ * Have to set the latch again, even if handle_sig_alarm already did. Back
1601
+ * then got_deadlock_timeout wasn't yet set... It's unlikely that this
1602
+ * ever would be a problem, but setting a set latch again is cheap.
1603
+ */
1604
+ SetLatch (MyLatch );
1605
+ errno = save_errno ;
1606
+ }
1616
1607
1617
1608
/*
1618
1609
* ProcWaitForSignal - wait for a signal from another backend.
1619
1610
*
1620
- * This can share the semaphore normally used for waiting for locks,
1621
- * since a backend could never be waiting for a lock and a signal at
1622
- * the same time. As with locks, it's OK if the signal arrives just
1623
- * before we actually reach the waiting state. Also as with locks,
1624
- * it's necessary that the caller be robust against bogus wakeups:
1625
- * always check that the desired state has occurred, and wait again
1626
- * if not. This copes with possible "leftover" wakeups.
1611
+ * As this uses the generic process latch the caller has to be robust against
1612
+ * unrelated wakeups: Always check that the desired state has occurred, and
1613
+ * wait again if not.
1627
1614
*/
1628
1615
void
1629
1616
ProcWaitForSignal (void )
1630
1617
{
1631
- PGSemaphoreLock (& MyProc -> sem , true);
1618
+ WaitLatch (MyLatch , WL_LATCH_SET , 0 );
1619
+ ResetLatch (MyLatch );
1620
+ CHECK_FOR_INTERRUPTS ();
1632
1621
}
1633
1622
1634
1623
/*
@@ -1664,5 +1653,7 @@ ProcSendSignal(int pid)
1664
1653
proc = BackendPidGetProc (pid );
1665
1654
1666
1655
if (proc != NULL )
1667
- PGSemaphoreUnlock (& proc -> sem );
1656
+ {
1657
+ SetLatch (& proc -> procLatch );
1658
+ }
1668
1659
}
0 commit comments