Use a long lived WaitEventSet for WaitLatch().
authorThomas Munro <tmunro@postgresql.org>
Thu, 30 Jul 2020 05:08:11 +0000 (17:08 +1200)
committerThomas Munro <tmunro@postgresql.org>
Thu, 30 Jul 2020 05:40:00 +0000 (17:40 +1200)
Create LatchWaitSet at backend startup time, and use it to implement
WaitLatch().  This avoids repeated epoll/kqueue setup and teardown
system calls.

Reorder SubPostmasterMain() slightly so that we restore the postmaster
pipe and Windows signal emulation before we reach InitPostmasterChild(),
to make this work in EXEC_BACKEND builds.

Reviewed-by: Kyotaro Horiguchi <horikyota.ntt@gmail.com>
Discussion: https://postgr.es/m/CA%2BhUKGJAC4Oqao%3DqforhNey20J8CiG2R%3DoBPqvfR0vOJrFysGw%40mail.gmail.com

src/backend/postmaster/postmaster.c
src/backend/storage/ipc/latch.c
src/backend/utils/init/miscinit.c
src/include/storage/latch.h

index 1db6a3d29d01e935781c7a55ab6171bcd6deeb9c..5b5fc97c72dae75a967153d13eb75d5132b101c6 100644 (file)
@@ -4896,9 +4896,6 @@ SubPostmasterMain(int argc, char *argv[])
    IsPostmasterEnvironment = true;
    whereToSendOutput = DestNone;
 
-   /* Setup as postmaster child */
-   InitPostmasterChild();
-
    /* Setup essential subsystems (to ensure elog() behaves sanely) */
    InitializeGUCOptions();
 
@@ -4913,6 +4910,18 @@ SubPostmasterMain(int argc, char *argv[])
    /* Close the postmaster's sockets (as soon as we know them) */
    ClosePostmasterPorts(strcmp(argv[1], "--forklog") == 0);
 
+   /*
+    * Start our win32 signal implementation. This has to be done after we
+    * read the backend variables, because we need to pick up the signal pipe
+    * from the parent process.
+    */
+#ifdef WIN32
+   pgwin32_signal_initialize();
+#endif
+
+   /* Setup as postmaster child */
+   InitPostmasterChild();
+
    /*
     * Set up memory area for GSS information. Mirrors the code in ConnCreate
     * for the non-exec case.
@@ -4956,15 +4965,6 @@ SubPostmasterMain(int argc, char *argv[])
    if (strcmp(argv[1], "--forkavworker") == 0)
        AutovacuumWorkerIAm();
 
-   /*
-    * Start our win32 signal implementation. This has to be done after we
-    * read the backend variables, because we need to pick up the signal pipe
-    * from the parent process.
-    */
-#ifdef WIN32
-   pgwin32_signal_initialize();
-#endif
-
    /* In EXEC_BACKEND case we will not have inherited these settings */
    pqinitmask();
    PG_SETMASK(&BlockSig);
index 91fa4b619b8c5c96cb99e7d3407d98686dd44ca1..4153cc85579f7ab6372aacb0b3b13e68c6feab16 100644 (file)
@@ -56,6 +56,7 @@
 #include "storage/latch.h"
 #include "storage/pmsignal.h"
 #include "storage/shmem.h"
+#include "utils/memutils.h"
 
 /*
  * Select the fd readiness primitive to use. Normally the "most modern"
@@ -129,6 +130,12 @@ struct WaitEventSet
 #endif
 };
 
+/* A common WaitEventSet used to implement WatchLatch() */
+static WaitEventSet *LatchWaitSet;
+
+/* The position of the latch in LatchWaitSet. */
+#define LatchWaitSetLatchPos 0
+
 #ifndef WIN32
 /* Are we currently in WaitLatch? The signal handler would like to know. */
 static volatile sig_atomic_t waiting = false;
@@ -242,6 +249,24 @@ InitializeLatchSupport(void)
 #endif
 }
 
+void
+InitializeLatchWaitSet(void)
+{
+   int         latch_pos PG_USED_FOR_ASSERTS_ONLY;
+
+   Assert(LatchWaitSet == NULL);
+
+   /* Set up the WaitEventSet used by WaitLatch(). */
+   LatchWaitSet = CreateWaitEventSet(TopMemoryContext, 2);
+   latch_pos = AddWaitEventToSet(LatchWaitSet, WL_LATCH_SET, PGINVALID_SOCKET,
+                                 MyLatch, NULL);
+   if (IsUnderPostmaster)
+       AddWaitEventToSet(LatchWaitSet, WL_EXIT_ON_PM_DEATH,
+                         PGINVALID_SOCKET, NULL, NULL);
+
+   Assert(latch_pos == LatchWaitSetLatchPos);
+}
+
 /*
  * Initialize a process-local latch.
  */
@@ -365,8 +390,31 @@ int
 WaitLatch(Latch *latch, int wakeEvents, long timeout,
          uint32 wait_event_info)
 {
-   return WaitLatchOrSocket(latch, wakeEvents, PGINVALID_SOCKET, timeout,
-                            wait_event_info);
+   WaitEvent   event;
+
+   /* Postmaster-managed callers must handle postmaster death somehow. */
+   Assert(!IsUnderPostmaster ||
+          (wakeEvents & WL_EXIT_ON_PM_DEATH) ||
+          (wakeEvents & WL_POSTMASTER_DEATH));
+
+   /*
+    * Some callers may have a latch other than MyLatch, or no latch at all,
+    * or want to handle postmaster death differently.  It's cheap to assign
+    * those, so just do it every time.
+    */
+   if (!(wakeEvents & WL_LATCH_SET))
+       latch = NULL;
+   ModifyWaitEvent(LatchWaitSet, LatchWaitSetLatchPos, WL_LATCH_SET, latch);
+   LatchWaitSet->exit_on_postmaster_death =
+       ((wakeEvents & WL_EXIT_ON_PM_DEATH) != 0);
+
+   if (WaitEventSetWait(LatchWaitSet,
+                        (wakeEvents & WL_TIMEOUT) ? timeout : -1,
+                        &event, 1,
+                        wait_event_info) == 0)
+       return WL_TIMEOUT;
+   else
+       return event.events;
 }
 
 /*
@@ -830,7 +878,8 @@ AddWaitEventToSet(WaitEventSet *set, uint32 events, pgsocket fd, Latch *latch,
 
 /*
  * Change the event mask and, in the WL_LATCH_SET case, the latch associated
- * with the WaitEvent.
+ * with the WaitEvent.  The latch may be changed to NULL to disable the latch
+ * temporarily, and then set back to a latch later.
  *
  * 'pos' is the id returned by AddWaitEventToSet.
  */
@@ -862,7 +911,6 @@ ModifyWaitEvent(WaitEventSet *set, int pos, uint32 events, Latch *latch)
    if (event->events & WL_LATCH_SET &&
        events != event->events)
    {
-       /* we could allow to disable latch events for a while */
        elog(ERROR, "cannot modify latch event");
    }
 
index cca9704d2d7afb2c1012c2a107a899d09033ed0f..cf8f9579c345f2d674d3d3b10aa05265725bbd65 100644 (file)
@@ -120,6 +120,7 @@ InitPostmasterChild(void)
    InitializeLatchSupport();
    MyLatch = &LocalLatchData;
    InitLatch(MyLatch);
+   InitializeLatchWaitSet();
 
    /*
     * If possible, make this process a group leader, so that the postmaster
@@ -152,6 +153,7 @@ InitStandaloneProcess(const char *argv0)
    InitializeLatchSupport();
    MyLatch = &LocalLatchData;
    InitLatch(MyLatch);
+   InitializeLatchWaitSet();
 
    /* Compute paths, no postmaster to inherit from */
    if (my_exec_path[0] == '\0')
index 46ae56cae3f1e8fe46dce948c66a89170db72d31..7c742021fb1b14977e10b0e38ca2574b0577fb45 100644 (file)
@@ -176,6 +176,7 @@ extern int  WaitLatch(Latch *latch, int wakeEvents, long timeout,
                      uint32 wait_event_info);
 extern int WaitLatchOrSocket(Latch *latch, int wakeEvents,
                              pgsocket sock, long timeout, uint32 wait_event_info);
+extern void InitializeLatchWaitSet(void);
 
 /*
  * Unix implementation uses SIGUSR1 for inter-process signaling.