79
79
* either, but just process the queue directly.
80
80
*
81
81
* 5. Upon receipt of a PROCSIG_NOTIFY_INTERRUPT signal, the signal handler
82
- * can call inbound-notify processing immediately if this backend is idle
83
- * (ie, it is waiting for a frontend command and is not within a transaction
84
- * block). Otherwise the handler may only set a flag, which will cause the
85
- * processing to occur just before we next go idle.
82
+ * sets the process's latch, which triggers the event to be processed
83
+ * immediately if this backend is idle (i.e., it is waiting for a frontend
84
+ * command and is not within a transaction block. C.f.
85
+ * ProcessClientReadInterrupt()). Otherwise the handler may only set a
86
+ * flag, which will cause the processing to occur just before we next go
87
+ * idle.
86
88
*
87
89
* Inbound-notify processing consists of reading all of the notifications
88
90
* that have arrived since scanning last time. We read every notification
126
128
#include "miscadmin.h"
127
129
#include "storage/ipc.h"
128
130
#include "storage/lmgr.h"
131
+ #include "storage/proc.h"
129
132
#include "storage/procarray.h"
130
133
#include "storage/procsignal.h"
131
134
#include "storage/sinval.h"
@@ -334,17 +337,13 @@ static List *pendingNotifies = NIL; /* list of Notifications */
334
337
static List * upperPendingNotifies = NIL ; /* list of upper-xact lists */
335
338
336
339
/*
337
- * State for inbound notifications consists of two flags: one saying whether
338
- * the signal handler is currently allowed to call ProcessIncomingNotify
339
- * directly, and one saying whether the signal has occurred but the handler
340
- * was not allowed to call ProcessIncomingNotify at the time.
341
- *
342
- * NB: the "volatile" on these declarations is critical! If your compiler
343
- * does not grok "volatile", you'd be best advised to compile this file
344
- * with all optimization turned off.
340
+ * Inbound notifications are initially processed by HandleNotifyInterrupt(),
341
+ * called from inside a signal handler. That just sets the
342
+ * notifyInterruptPending flag and sets the process
343
+ * latch. ProcessNotifyInterrupt() will then be called whenever it's safe to
344
+ * actually deal with the interrupt.
345
345
*/
346
- static volatile sig_atomic_t notifyInterruptEnabled = 0 ;
347
- static volatile sig_atomic_t notifyInterruptOccurred = 0 ;
346
+ volatile sig_atomic_t notifyInterruptPending = false;
348
347
349
348
/* True if we've registered an on_shmem_exit cleanup */
350
349
static bool unlistenExitRegistered = false;
@@ -1625,164 +1624,45 @@ AtSubAbort_Notify(void)
1625
1624
/*
1626
1625
* HandleNotifyInterrupt
1627
1626
*
1628
- * This is called when PROCSIG_NOTIFY_INTERRUPT is received.
1629
- *
1630
- * If we are idle (notifyInterruptEnabled is set), we can safely invoke
1631
- * ProcessIncomingNotify directly. Otherwise, just set a flag
1632
- * to do it later.
1627
+ * Signal handler portion of interrupt handling. Let the backend know
1628
+ * that there's a pending notify interrupt. If we're currently reading
1629
+ * from the client, this will interrupt the read and
1630
+ * ProcessClientReadInterrupt() will call ProcessNotifyInterrupt().
1633
1631
*/
1634
1632
void
1635
1633
HandleNotifyInterrupt (void )
1636
1634
{
1637
1635
/*
1638
1636
* Note: this is called by a SIGNAL HANDLER. You must be very wary what
1639
- * you do here. Some helpful soul had this routine sprinkled with
1640
- * TPRINTFs, which would likely lead to corruption of stdio buffers if
1641
- * they were ever turned on.
1637
+ * you do here.
1642
1638
*/
1643
1639
1644
- /* Don't joggle the elbow of proc_exit */
1645
- if (proc_exit_inprogress )
1646
- return ;
1647
-
1648
- if (notifyInterruptEnabled )
1649
- {
1650
- bool save_ImmediateInterruptOK = ImmediateInterruptOK ;
1651
-
1652
- /*
1653
- * We may be called while ImmediateInterruptOK is true; turn it off
1654
- * while messing with the NOTIFY state. This prevents problems if
1655
- * SIGINT or similar arrives while we're working. Just to be real
1656
- * sure, bump the interrupt holdoff counter as well. That way, even
1657
- * if something inside ProcessIncomingNotify() transiently sets
1658
- * ImmediateInterruptOK (eg while waiting on a lock), we won't get
1659
- * interrupted until we're done with the notify interrupt.
1660
- */
1661
- ImmediateInterruptOK = false;
1662
- HOLD_INTERRUPTS ();
1663
-
1664
- /*
1665
- * I'm not sure whether some flavors of Unix might allow another
1666
- * SIGUSR1 occurrence to recursively interrupt this routine. To cope
1667
- * with the possibility, we do the same sort of dance that
1668
- * EnableNotifyInterrupt must do --- see that routine for comments.
1669
- */
1670
- notifyInterruptEnabled = 0 ; /* disable any recursive signal */
1671
- notifyInterruptOccurred = 1 ; /* do at least one iteration */
1672
- for (;;)
1673
- {
1674
- notifyInterruptEnabled = 1 ;
1675
- if (!notifyInterruptOccurred )
1676
- break ;
1677
- notifyInterruptEnabled = 0 ;
1678
- if (notifyInterruptOccurred )
1679
- {
1680
- /* Here, it is finally safe to do stuff. */
1681
- if (Trace_notify )
1682
- elog (DEBUG1 , "HandleNotifyInterrupt: perform async notify" );
1683
-
1684
- ProcessIncomingNotify ();
1685
-
1686
- if (Trace_notify )
1687
- elog (DEBUG1 , "HandleNotifyInterrupt: done" );
1688
- }
1689
- }
1640
+ /* signal that work needs to be done */
1641
+ notifyInterruptPending = true;
1690
1642
1691
- /*
1692
- * Restore the holdoff level and ImmediateInterruptOK, and check for
1693
- * interrupts if needed.
1694
- */
1695
- RESUME_INTERRUPTS ();
1696
- ImmediateInterruptOK = save_ImmediateInterruptOK ;
1697
- if (save_ImmediateInterruptOK )
1698
- CHECK_FOR_INTERRUPTS ();
1699
- }
1700
- else
1701
- {
1702
- /*
1703
- * In this path it is NOT SAFE to do much of anything, except this:
1704
- */
1705
- notifyInterruptOccurred = 1 ;
1706
- }
1643
+ /* make sure the event is processed in due course */
1644
+ SetLatch (MyLatch );
1707
1645
}
1708
1646
1709
1647
/*
1710
- * EnableNotifyInterrupt
1711
- *
1712
- * This is called by the PostgresMain main loop just before waiting
1713
- * for a frontend command. If we are truly idle (ie, *not* inside
1714
- * a transaction block), then process any pending inbound notifies,
1715
- * and enable the signal handler to process future notifies directly.
1648
+ * ProcessNotifyInterrupt
1716
1649
*
1717
- * NOTE: the signal handler starts out disabled, and stays so until
1718
- * PostgresMain calls this the first time.
1650
+ * This is called just after waiting for a frontend command. If a
1651
+ * interrupt arrives (via HandleNotifyInterrupt()) while reading, the
1652
+ * read will be interrupted via the process's latch, and this routine
1653
+ * will get called. If we are truly idle (ie, *not* inside a transaction
1654
+ * block), process the incoming notifies.
1719
1655
*/
1720
1656
void
1721
- EnableNotifyInterrupt (void )
1657
+ ProcessNotifyInterrupt (void )
1722
1658
{
1723
1659
if (IsTransactionOrTransactionBlock ())
1724
1660
return ; /* not really idle */
1725
1661
1726
- /*
1727
- * This code is tricky because we are communicating with a signal handler
1728
- * that could interrupt us at any point. If we just checked
1729
- * notifyInterruptOccurred and then set notifyInterruptEnabled, we could
1730
- * fail to respond promptly to a signal that happens in between those two
1731
- * steps. (A very small time window, perhaps, but Murphy's Law says you
1732
- * can hit it...) Instead, we first set the enable flag, then test the
1733
- * occurred flag. If we see an unserviced interrupt has occurred, we
1734
- * re-clear the enable flag before going off to do the service work. (That
1735
- * prevents re-entrant invocation of ProcessIncomingNotify() if another
1736
- * interrupt occurs.) If an interrupt comes in between the setting and
1737
- * clearing of notifyInterruptEnabled, then it will have done the service
1738
- * work and left notifyInterruptOccurred zero, so we have to check again
1739
- * after clearing enable. The whole thing has to be in a loop in case
1740
- * another interrupt occurs while we're servicing the first. Once we get
1741
- * out of the loop, enable is set and we know there is no unserviced
1742
- * interrupt.
1743
- *
1744
- * NB: an overenthusiastic optimizing compiler could easily break this
1745
- * code. Hopefully, they all understand what "volatile" means these days.
1746
- */
1747
- for (;;)
1748
- {
1749
- notifyInterruptEnabled = 1 ;
1750
- if (!notifyInterruptOccurred )
1751
- break ;
1752
- notifyInterruptEnabled = 0 ;
1753
- if (notifyInterruptOccurred )
1754
- {
1755
- if (Trace_notify )
1756
- elog (DEBUG1 , "EnableNotifyInterrupt: perform async notify" );
1757
-
1758
- ProcessIncomingNotify ();
1759
-
1760
- if (Trace_notify )
1761
- elog (DEBUG1 , "EnableNotifyInterrupt: done" );
1762
- }
1763
- }
1662
+ while (notifyInterruptPending )
1663
+ ProcessIncomingNotify ();
1764
1664
}
1765
1665
1766
- /*
1767
- * DisableNotifyInterrupt
1768
- *
1769
- * This is called by the PostgresMain main loop just after receiving
1770
- * a frontend command. Signal handler execution of inbound notifies
1771
- * is disabled until the next EnableNotifyInterrupt call.
1772
- *
1773
- * The PROCSIG_CATCHUP_INTERRUPT signal handler also needs to call this,
1774
- * so as to prevent conflicts if one signal interrupts the other. So we
1775
- * must return the previous state of the flag.
1776
- */
1777
- bool
1778
- DisableNotifyInterrupt (void )
1779
- {
1780
- bool result = (notifyInterruptEnabled != 0 );
1781
-
1782
- notifyInterruptEnabled = 0 ;
1783
-
1784
- return result ;
1785
- }
1786
1666
1787
1667
/*
1788
1668
* Read all pending notifications from the queue, and deliver appropriate
@@ -2076,9 +1956,10 @@ asyncQueueAdvanceTail(void)
2076
1956
/*
2077
1957
* ProcessIncomingNotify
2078
1958
*
2079
- * Deal with arriving NOTIFYs from other backends.
2080
- * This is called either directly from the PROCSIG_NOTIFY_INTERRUPT
2081
- * signal handler, or the next time control reaches the outer idle loop.
1959
+ * Deal with arriving NOTIFYs from other backends as soon as it's safe to
1960
+ * do so. This used to be called from the PROCSIG_NOTIFY_INTERRUPT
1961
+ * signal handler, but isn't anymore.
1962
+ *
2082
1963
* Scan the queue for arriving notifications and report them to my front
2083
1964
* end.
2084
1965
*
@@ -2087,18 +1968,13 @@ asyncQueueAdvanceTail(void)
2087
1968
static void
2088
1969
ProcessIncomingNotify (void )
2089
1970
{
2090
- bool catchup_enabled ;
2091
-
2092
1971
/* We *must* reset the flag */
2093
- notifyInterruptOccurred = 0 ;
1972
+ notifyInterruptPending = false ;
2094
1973
2095
1974
/* Do nothing else if we aren't actively listening */
2096
1975
if (listenChannels == NIL )
2097
1976
return ;
2098
1977
2099
- /* Must prevent catchup interrupt while I am running */
2100
- catchup_enabled = DisableCatchupInterrupt ();
2101
-
2102
1978
if (Trace_notify )
2103
1979
elog (DEBUG1 , "ProcessIncomingNotify" );
2104
1980
@@ -2123,9 +1999,6 @@ ProcessIncomingNotify(void)
2123
1999
2124
2000
if (Trace_notify )
2125
2001
elog (DEBUG1 , "ProcessIncomingNotify: done" );
2126
-
2127
- if (catchup_enabled )
2128
- EnableCatchupInterrupt ();
2129
2002
}
2130
2003
2131
2004
/*
0 commit comments