Change custom wait events to use dynamic shared hash tables
authorMichael Paquier <[email protected]>
Mon, 14 Aug 2023 05:47:27 +0000 (14:47 +0900)
committerMichael Paquier <[email protected]>
Mon, 14 Aug 2023 05:47:27 +0000 (14:47 +0900)
Currently, the names of the custom wait event must be registered for
each backend, requiring all these to link to the shared memory area of
an extension, even if these are not loaded with
shared_preload_libraries.

This  relaxes the constraints related to this infrastructure by
storing the wait events and their names in two dynamic hash tables in
shared memory.  This has the advantage to simplify the registration of
custom wait events to a single routine call that returns an event ID
ready for consumption:
uint32 WaitEventExtensionNew(const char *wait_event_name);

The caller of this routine can then cache locally the ID returned, to be
used for pgstat_report_wait_start(), WaitLatch() or a similar routine.

The implementation uses two hash tables: one with a key based on the
event name to avoid duplicates and a second using the event ID as key
for event lookups, like on pg_stat_activity.  These tables can hold a
minimum of 16 entries, and a maximum of 128 entries, which should be plenty
enough.

The code changes done in worker_spi show how things are simplified (most
of the code removed in this commit comes from there):
- worker_spi_init() is gone.
- No more shared memory hooks required (size requested and
initialization).
- The custom wait event ID is cached in the process that needs to set
it, with one single call to WaitEventExtensionNew() to retrieve it.

Per suggestion from Andres Freund.

Author: Masahiro Ikeda, with a few tweaks from me.
Discussion: https://postgr.es/m/20230801032349[email protected]

doc/src/sgml/monitoring.sgml
doc/src/sgml/xfunc.sgml
src/backend/storage/lmgr/lwlocknames.txt
src/backend/utils/activity/wait_event.c
src/backend/utils/activity/wait_event_names.txt
src/include/utils/wait_event.h
src/test/modules/worker_spi/t/001_worker_spi.pl
src/test/modules/worker_spi/worker_spi--1.0.sql
src/test/modules/worker_spi/worker_spi.c
src/tools/pgindent/typedefs.list

index f4fc5d814fb7fbdb90954e5d623c0a805cc9a657..70511a2388e1d978cfaabf3764c0f4e50bcbf796 100644 (file)
@@ -1121,9 +1121,8 @@ SELECT pid, wait_event_type, wait_event FROM pg_stat_activity WHERE wait_event i
      <literal>LWLock</literal> types
      to the list shown in <xref linkend="wait-event-extension-table"/> and
      <xref linkend="wait-event-lwlock-table"/>. In some cases, the name
-     assigned by an extension will not be available in all server processes;
-     so an <literal>Extension</literal> or <literal>LWLock</literal> wait
-     event might be reported as just
+     of <literal>LWLock</literal> assigned by an extension will not be
+     available in all server processes; It might be reported as just
      <quote><literal>extension</literal></quote> rather than the
      extension-assigned name.
     </para>
index d6345a775b6de2f685c5873225d176db5ef97f59..281c178b0e8a35a42a252b05dd43962500cb2b94 100644 (file)
@@ -3454,33 +3454,15 @@ if (!ptr)
    </sect2>
 
    <sect2 id="xfunc-addin-wait-events">
-    <title>Shared Memory and Custom Wait Events</title>
+    <title>Custom Wait Events</title>
 
     <para>
      Add-ins can define custom wait events under the wait event type
-     <literal>Extension</literal>. The add-in's shared library must be
-     preloaded by specifying it in <literal>shared_preload_libraries</literal>,
-     and register a <literal>shmem_request_hook</literal> and a
-     <literal>shmem_startup_hook</literal> in its
-     <function>_PG_init</function> function.
-     <literal>shmem_request_hook</literal> can request a shared memory size
-     to be later used at startup by calling:
+     <literal>Extension</literal> by calling:
 <programlisting>
-void RequestAddinShmemSpace(int size)
-</programlisting>
-    </para>
-    <para>
-     <literal>shmem_startup_hook</literal> can allocate in shared memory
-     custom wait events by calling while holding the LWLock
-     <function>AddinShmemInitLock</function> to avoid any race conditions:
-<programlisting>
-uint32 WaitEventExtensionNew(void)
-</programlisting>
-     Next, each process needs to associate the wait event allocated previously
-     to a user-facing custom string, which is something done by calling:
-<programlisting>
-void WaitEventExtensionRegisterName(uint32 wait_event_info, const char *wait_event_name)
+uint32 WaitEventExtensionNew(const char *wait_event_name)
 </programlisting>
+     The wait event is associated to a user-facing custom string.
      An example can be found in <filename>src/test/modules/worker_spi</filename>
      in the PostgreSQL source tree.
     </para>
index b34b6afecde9738206b81554052e16a1678c0e78..811ad94742026cdde32c125af213639f6bd76f5d 100644 (file)
@@ -53,3 +53,4 @@ XactTruncationLock                    44
 # 45 was XactTruncationLock until removal of BackendRandomLock
 WrapLimitsVacuumLock               46
 NotifyQueueTailLock                    47
+WaitEventExtensionLock             48
index b3596ece80d303da5d587e6808f428b39b52726d..4b9b5c01cbea5e6234aae21f8834c445e7438cab 100644 (file)
@@ -45,6 +45,41 @@ uint32      *my_wait_event_info = &local_my_wait_event_info;
 #define WAIT_EVENT_CLASS_MASK  0xFF000000
 #define WAIT_EVENT_ID_MASK     0x0000FFFF
 
+/*
+ * Hash tables for storing custom wait event ids and their names in
+ * shared memory.
+ *
+ * WaitEventExtensionHashById is used to find the name from a event id.
+ * Any backend can search it to find custom wait events.
+ *
+ * WaitEventExtensionHashByName is used to find the event ID from a name.
+ * It is used to ensure that no duplicated entries are registered.
+ *
+ * The size of the hash table is based on the assumption that
+ * WAIT_EVENT_EXTENSION_BASH_INIT_SIZE is enough for most cases, and it seems
+ * unlikely that the number of entries will reach
+ * WAIT_EVENT_EXTENSION_BASH_MAX_SIZE.
+ */
+static HTAB *WaitEventExtensionHashById;   /* find names from IDs */
+static HTAB *WaitEventExtensionHashByName; /* find IDs from names */
+
+#define WAIT_EVENT_EXTENSION_HASH_INIT_SIZE    16
+#define WAIT_EVENT_EXTENSION_HASH_MAX_SIZE 128
+
+/* hash table entries */
+typedef struct WaitEventExtensionEntryById
+{
+   uint16      event_id;       /* hash key */
+   char        wait_event_name[NAMEDATALEN];   /* custom wait event name */
+} WaitEventExtensionEntryById;
+
+typedef struct WaitEventExtensionEntryByName
+{
+   char        wait_event_name[NAMEDATALEN];   /* hash key */
+   uint16      event_id;       /* wait event ID */
+} WaitEventExtensionEntryByName;
+
+
 /* dynamic allocation counter for custom wait events in extensions */
 typedef struct WaitEventExtensionCounterData
 {
@@ -59,36 +94,39 @@ static WaitEventExtensionCounterData *WaitEventExtensionCounter;
 #define NUM_BUILTIN_WAIT_EVENT_EXTENSION   \
    (WAIT_EVENT_EXTENSION_FIRST_USER_DEFINED - WAIT_EVENT_EXTENSION)
 
-/*
- * This is indexed by event ID minus NUM_BUILTIN_WAIT_EVENT_EXTENSION, and
- * stores the names of all dynamically-created event IDs known to the current
- * process.  Any unused entries in the array will contain NULL.
- */
-static const char **WaitEventExtensionNames = NULL;
-static int WaitEventExtensionNamesAllocated = 0;
+/* wait event info for extensions */
+#define WAIT_EVENT_EXTENSION_INFO(eventId) (PG_WAIT_EXTENSION | eventId)
 
 static const char *GetWaitEventExtensionIdentifier(uint16 eventId);
 
 /*
- *  Return the space for dynamic allocation counter.
+ *  Return the space for dynamic shared hash tables and dynamic allocation counter.
  */
 Size
 WaitEventExtensionShmemSize(void)
 {
-   return sizeof(WaitEventExtensionCounterData);
+   Size        sz;
+
+   sz = MAXALIGN(sizeof(WaitEventExtensionCounterData));
+   sz = add_size(sz, hash_estimate_size(WAIT_EVENT_EXTENSION_HASH_MAX_SIZE,
+                                        sizeof(WaitEventExtensionEntryById)));
+   sz = add_size(sz, hash_estimate_size(WAIT_EVENT_EXTENSION_HASH_MAX_SIZE,
+                                        sizeof(WaitEventExtensionEntryByName)));
+   return sz;
 }
 
 /*
- * Allocate shmem space for dynamic allocation counter.
+ * Allocate shmem space for dynamic shared hash and dynamic allocation counter.
  */
 void
 WaitEventExtensionShmemInit(void)
 {
    bool        found;
+   HASHCTL     info;
 
    WaitEventExtensionCounter = (WaitEventExtensionCounterData *)
        ShmemInitStruct("WaitEventExtensionCounterData",
-                       WaitEventExtensionShmemSize(), &found);
+                       sizeof(WaitEventExtensionCounterData), &found);
 
    if (!found)
    {
@@ -96,21 +134,78 @@ WaitEventExtensionShmemInit(void)
        WaitEventExtensionCounter->nextId = NUM_BUILTIN_WAIT_EVENT_EXTENSION;
        SpinLockInit(&WaitEventExtensionCounter->mutex);
    }
+
+   /* initialize or attach the hash tables to store custom wait events */
+   info.keysize = sizeof(uint16);
+   info.entrysize = sizeof(WaitEventExtensionEntryById);
+   WaitEventExtensionHashById = ShmemInitHash("WaitEventExtension hash by id",
+                                              WAIT_EVENT_EXTENSION_HASH_INIT_SIZE,
+                                              WAIT_EVENT_EXTENSION_HASH_MAX_SIZE,
+                                              &info,
+                                              HASH_ELEM | HASH_BLOBS);
+
+   /* key is a NULL-terminated string */
+   info.keysize = sizeof(char[NAMEDATALEN]);
+   info.entrysize = sizeof(WaitEventExtensionEntryByName);
+   WaitEventExtensionHashByName = ShmemInitHash("WaitEventExtension hash by name",
+                                                WAIT_EVENT_EXTENSION_HASH_INIT_SIZE,
+                                                WAIT_EVENT_EXTENSION_HASH_MAX_SIZE,
+                                                &info,
+                                                HASH_ELEM | HASH_STRINGS);
 }
 
 /*
- * Allocate a new event ID and return the wait event.
+ * Allocate a new event ID and return the wait event info.
+ *
+ * If the wait event name is already defined, this does not allocate a new
+ * entry; it returns the wait event information associated to the name.
  */
 uint32
-WaitEventExtensionNew(void)
+WaitEventExtensionNew(const char *wait_event_name)
 {
    uint16      eventId;
+   bool        found;
+   WaitEventExtensionEntryByName *entry_by_name;
+   WaitEventExtensionEntryById *entry_by_id;
+
+   /* Check the limit of the length of the event name */
+   if (strlen(wait_event_name) >= NAMEDATALEN)
+       elog(ERROR,
+            "cannot use custom wait event string longer than %u characters",
+            NAMEDATALEN - 1);
+
+   /*
+    * Check if the wait event info associated to the name is already defined,
+    * and return it if so.
+    */
+   LWLockAcquire(WaitEventExtensionLock, LW_SHARED);
+   entry_by_name = (WaitEventExtensionEntryByName *)
+       hash_search(WaitEventExtensionHashByName, wait_event_name,
+                   HASH_FIND, &found);
+   LWLockRelease(WaitEventExtensionLock);
+   if (found)
+       return WAIT_EVENT_EXTENSION_INFO(entry_by_name->event_id);
 
-   Assert(LWLockHeldByMeInMode(AddinShmemInitLock, LW_EXCLUSIVE));
+   /*
+    * Allocate and register a new wait event.  Recheck if the event name
+    * exists, as it could be possible that a concurrent process has inserted
+    * one with the same name since the LWLock acquired again here was
+    * previously released.
+    */
+   LWLockAcquire(WaitEventExtensionLock, LW_EXCLUSIVE);
+   entry_by_name = (WaitEventExtensionEntryByName *)
+       hash_search(WaitEventExtensionHashByName, wait_event_name,
+                   HASH_FIND, &found);
+   if (found)
+   {
+       LWLockRelease(WaitEventExtensionLock);
+       return WAIT_EVENT_EXTENSION_INFO(entry_by_name->event_id);
+   }
 
+   /* Allocate a new event Id */
    SpinLockAcquire(&WaitEventExtensionCounter->mutex);
 
-   if (WaitEventExtensionCounter->nextId > PG_UINT16_MAX)
+   if (WaitEventExtensionCounter->nextId >= WAIT_EVENT_EXTENSION_HASH_MAX_SIZE)
    {
        SpinLockRelease(&WaitEventExtensionCounter->mutex);
        ereport(ERROR,
@@ -122,64 +217,23 @@ WaitEventExtensionNew(void)
 
    SpinLockRelease(&WaitEventExtensionCounter->mutex);
 
-   return PG_WAIT_EXTENSION | eventId;
-}
-
-/*
- * Register a dynamic wait event name for extension in the lookup table
- * of the current process.
- *
- * This routine will save a pointer to the wait event name passed as an argument,
- * so the name should be allocated in a backend-lifetime context
- * (shared memory, TopMemoryContext, static constant, or similar).
- *
- * The "wait_event_name" will be user-visible as a wait event name, so try to
- * use a name that fits the style for those.
- */
-void
-WaitEventExtensionRegisterName(uint32 wait_event_info,
-                              const char *wait_event_name)
-{
-   uint32      classId;
-   uint16      eventId;
-
-   classId = wait_event_info & WAIT_EVENT_CLASS_MASK;
-   eventId = wait_event_info & WAIT_EVENT_ID_MASK;
-
-   /* Check the wait event class. */
-   if (classId != PG_WAIT_EXTENSION)
-       ereport(ERROR,
-               errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-               errmsg("invalid wait event class %u", classId));
-
-   /* This should only be called for user-defined wait event. */
-   if (eventId < NUM_BUILTIN_WAIT_EVENT_EXTENSION)
-       ereport(ERROR,
-               errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-               errmsg("invalid wait event ID %u", eventId));
+   /* Register the new wait event */
+   entry_by_id = (WaitEventExtensionEntryById *)
+       hash_search(WaitEventExtensionHashById, &eventId,
+                   HASH_ENTER, &found);
+   Assert(!found);
+   strlcpy(entry_by_id->wait_event_name, wait_event_name,
+           sizeof(entry_by_id->wait_event_name));
 
-   /* Convert to array index. */
-   eventId -= NUM_BUILTIN_WAIT_EVENT_EXTENSION;
+   entry_by_name = (WaitEventExtensionEntryByName *)
+       hash_search(WaitEventExtensionHashByName, wait_event_name,
+                   HASH_ENTER, &found);
+   Assert(!found);
+   entry_by_name->event_id = eventId;
 
-   /* If necessary, create or enlarge array. */
-   if (eventId >= WaitEventExtensionNamesAllocated)
-   {
-       uint32      newalloc;
-
-       newalloc = pg_nextpower2_32(Max(8, eventId + 1));
-
-       if (WaitEventExtensionNames == NULL)
-           WaitEventExtensionNames = (const char **)
-               MemoryContextAllocZero(TopMemoryContext,
-                                      newalloc * sizeof(char *));
-       else
-           WaitEventExtensionNames =
-               repalloc0_array(WaitEventExtensionNames, const char *,
-                               WaitEventExtensionNamesAllocated, newalloc);
-       WaitEventExtensionNamesAllocated = newalloc;
-   }
+   LWLockRelease(WaitEventExtensionLock);
 
-   WaitEventExtensionNames[eventId] = wait_event_name;
+   return WAIT_EVENT_EXTENSION_INFO(eventId);
 }
 
 /*
@@ -188,23 +242,25 @@ WaitEventExtensionRegisterName(uint32 wait_event_info,
 static const char *
 GetWaitEventExtensionIdentifier(uint16 eventId)
 {
+   bool        found;
+   WaitEventExtensionEntryById *entry;
+
    /* Built-in event? */
    if (eventId < NUM_BUILTIN_WAIT_EVENT_EXTENSION)
        return "Extension";
 
-   /*
-    * It is a user-defined wait event, so look at WaitEventExtensionNames[].
-    * However, it is possible that the name has never been registered by
-    * calling WaitEventExtensionRegisterName() in the current process, in
-    * which case give up and return "extension".
-    */
-   eventId -= NUM_BUILTIN_WAIT_EVENT_EXTENSION;
+   /* It is a user-defined wait event, so lookup hash table. */
+   LWLockAcquire(WaitEventExtensionLock, LW_SHARED);
+   entry = (WaitEventExtensionEntryById *)
+       hash_search(WaitEventExtensionHashById, &eventId,
+                   HASH_FIND, &found);
+   LWLockRelease(WaitEventExtensionLock);
 
-   if (eventId >= WaitEventExtensionNamesAllocated ||
-       WaitEventExtensionNames[eventId] == NULL)
-       return "extension";
+   if (!entry)
+       elog(ERROR, "could not find custom wait event name for ID %u",
+            eventId);
 
-   return WaitEventExtensionNames[eventId];
+   return entry->wait_event_name;
 }
 
 
index fcd9d2c63cdbc375251a919f7e7ed19c3ec6586f..f9e01e33b13401879a36d766d398063062cd3a04 100644 (file)
@@ -317,6 +317,7 @@ WAIT_EVENT_DOCONLY  LogicalRepWorker    "Waiting to read or update the state of logi
 WAIT_EVENT_DOCONLY XactTruncation  "Waiting to execute <function>pg_xact_status</function> or update the oldest transaction ID available to it."
 WAIT_EVENT_DOCONLY WrapLimitsVacuum    "Waiting to update limits on transaction id and multixact consumption."
 WAIT_EVENT_DOCONLY NotifyQueueTail "Waiting to update limit on <command>NOTIFY</command> message storage."
+WAIT_EVENT_DOCONLY WaitEventExtension  "Waiting to read or update custom wait events information for extensions."
 
 WAIT_EVENT_DOCONLY XactBuffer  "Waiting for I/O on a transaction status SLRU buffer."
 WAIT_EVENT_DOCONLY CommitTsBuffer  "Waiting for I/O on a commit timestamp SLRU buffer."
index aad8bc08fa0301a1469b55579af167b4d04909a1..3eebdfad38b8d9fc1e16721765532c800aa7f57e 100644 (file)
@@ -44,12 +44,14 @@ extern PGDLLIMPORT uint32 *my_wait_event_info;
  * Use this category when the server process is waiting for some condition
  * defined by an extension module.
  *
- * Extensions can define their own wait events in this category.  First,
- * they should call WaitEventExtensionNew() to get one or more wait event
- * IDs that are allocated from a shared counter.  These can be used directly
- * with pgstat_report_wait_start() or equivalent.  Next, each individual
- * process should call WaitEventExtensionRegisterName() to associate a wait
- * event string to the number allocated previously.
+ * Extensions can define their own wait events in this category.  They should
+ * call WaitEventExtensionNew() with a wait event string.  If the wait event
+ * associated to a string is already allocated, it returns the wait event
+ * information to use.  If not, it gets one wait event ID allocated from
+ * a shared counter, associates the string to the ID in the shared dynamic
+ * hash and returns the wait event information.
+ *
+ * The ID retrieved can be used with pgstat_report_wait_start() or equivalent.
  */
 typedef enum
 {
@@ -60,9 +62,7 @@ typedef enum
 extern void WaitEventExtensionShmemInit(void);
 extern Size WaitEventExtensionShmemSize(void);
 
-extern uint32 WaitEventExtensionNew(void);
-extern void WaitEventExtensionRegisterName(uint32 wait_event_info,
-                                          const char *wait_event_name);
+extern uint32 WaitEventExtensionNew(const char *wait_event_name);
 
 /* ----------
  * pgstat_report_wait_start() -
index c3e7f5fbe6589f1b547875f4df05358b4328af39..26b8a49becab8d93af9a6cd3a1ae9b0d1a49a595 100644 (file)
@@ -39,25 +39,11 @@ $node->poll_query_until('postgres',
 $result = $node->safe_psql('postgres', 'SELECT * FROM schema4.counted;');
 is($result, qq(total|1), 'dynamic bgworker correctly consumed tuple data');
 
-# Check the wait event used by the dynamic bgworker.  For a session without
-# the state in shared memory known, the default of "extension" is the value
-# waited on.
+# Check the wait event used by the dynamic bgworker.
 $result = $node->poll_query_until(
    'postgres',
    qq[SELECT wait_event FROM pg_stat_activity WHERE backend_type ~ 'worker_spi';],
-   'extension');
-is($result, 1, 'dynamic bgworker has reported "extension" as wait event');
-
-# If the shared memory state is loaded (here with worker_spi_init within
-# the same connection as the one querying pg_stat_activity), the wait
-# event is the custom one.
-# The expected result is a special pattern here with a newline coming from the
-# first query where the shared memory state is set.
-$result = $node->poll_query_until(
-   'postgres',
-   qq[SELECT worker_spi_init(); SELECT wait_event FROM pg_stat_activity WHERE backend_type ~ 'worker_spi';],
-   qq[
-worker_spi_main]);
+   qq[worker_spi_main]);
 is($result, 1,
    'dynamic bgworker has reported "worker_spi_main" as wait event');
 
index f13f7e0f98d94c11b09d21244a45d095b1171635..e9d5b07373a8bdaeaab083758b693e7b93792c08 100644 (file)
@@ -7,8 +7,3 @@ CREATE FUNCTION worker_spi_launch(pg_catalog.int4)
 RETURNS pg_catalog.int4 STRICT
 AS 'MODULE_PATHNAME'
 LANGUAGE C;
-
-CREATE FUNCTION worker_spi_init()
-RETURNS VOID STRICT
-AS 'MODULE_PATHNAME'
-LANGUAGE C;
index c4317351cedd1f1adc6d01e2e67a9160b5fdda43..98f8d4194b21b4e994d70124aed818f519dcdce9 100644 (file)
 
 PG_MODULE_MAGIC;
 
-PG_FUNCTION_INFO_V1(worker_spi_init);
 PG_FUNCTION_INFO_V1(worker_spi_launch);
 
 PGDLLEXPORT void worker_spi_main(Datum main_arg) pg_attribute_noreturn();
 
-/* Shared memory state */
-typedef struct worker_spi_state
-{
-   /* the wait event defined during initialization phase */
-   uint32      wait_event;
-} worker_spi_state;
-
-static worker_spi_state *wsstate = NULL;   /* pointer to shared memory */
-
-static shmem_request_hook_type prev_shmem_request_hook = NULL;
-static shmem_request_hook_type prev_shmem_startup_hook = NULL;
-
-static void worker_spi_shmem_request(void);
-static void worker_spi_shmem_startup(void);
-static void worker_spi_shmem_init(void);
-static Size worker_spi_memsize(void);
-
 /* GUC variables */
 static int worker_spi_naptime = 10;
 static int worker_spi_total_workers = 2;
 static char *worker_spi_database = NULL;
 
+/* value cached, fetched from shared memory */
+static uint32 worker_spi_wait_event_main = 0;
 
 typedef struct worktable
 {
@@ -78,63 +62,6 @@ typedef struct worktable
    const char *name;
 } worktable;
 
-static void
-worker_spi_shmem_request(void)
-{
-   if (prev_shmem_request_hook)
-       prev_shmem_request_hook();
-
-   RequestAddinShmemSpace(worker_spi_memsize());
-}
-
-static void
-worker_spi_shmem_startup(void)
-{
-   if (prev_shmem_startup_hook)
-       prev_shmem_startup_hook();
-
-   worker_spi_shmem_init();
-}
-
-static Size
-worker_spi_memsize(void)
-{
-   return MAXALIGN(sizeof(worker_spi_state));
-}
-
-/*
- * Initialize the shared memory state of worker_spi.
- *
- * This routine allocates a new wait event when called the first time.
- * On follow-up calls, the name of the wait event associated with the
- * existing shared memory state is registered.
- */
-static void
-worker_spi_shmem_init(void)
-{
-   bool        found;
-
-   wsstate = NULL;
-
-   /* Create or attach to the shared memory state */
-   LWLockAcquire(AddinShmemInitLock, LW_EXCLUSIVE);
-   wsstate = ShmemInitStruct("worker_spi State",
-                             sizeof(worker_spi_state),
-                             &found);
-
-   /* Define a new wait event */
-   if (!found)
-       wsstate->wait_event = WaitEventExtensionNew();
-
-   LWLockRelease(AddinShmemInitLock);
-
-   /*
-    * Register the wait event in the lookup table of the current process.
-    */
-   WaitEventExtensionRegisterName(wsstate->wait_event, "worker_spi_main");
-   return;
-}
-
 /*
  * Initialize workspace for a worker process: create the schema if it doesn't
  * already exist.
@@ -224,9 +151,6 @@ worker_spi_main(Datum main_arg)
    /* We're now ready to receive signals */
    BackgroundWorkerUnblockSignals();
 
-   /* Create (if necessary) and attach to our shared memory area. */
-   worker_spi_shmem_init();
-
    /* Connect to our database */
    BackgroundWorkerInitializeConnection(worker_spi_database, NULL, 0);
 
@@ -268,6 +192,10 @@ worker_spi_main(Datum main_arg)
    {
        int         ret;
 
+       /* First time, allocate or get the custom wait event */
+       if (worker_spi_wait_event_main == 0)
+           worker_spi_wait_event_main = WaitEventExtensionNew("worker_spi_main");
+
        /*
         * Background workers mustn't call usleep() or any direct equivalent:
         * instead, they may wait on their process latch, which sleeps as
@@ -277,7 +205,7 @@ worker_spi_main(Datum main_arg)
        (void) WaitLatch(MyLatch,
                         WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
                         worker_spi_naptime * 1000L,
-                        wsstate->wait_event);
+                        worker_spi_wait_event_main);
        ResetLatch(MyLatch);
 
        CHECK_FOR_INTERRUPTS();
@@ -406,11 +334,6 @@ _PG_init(void)
 
    MarkGUCPrefixReserved("worker_spi");
 
-   prev_shmem_request_hook = shmem_request_hook;
-   shmem_request_hook = worker_spi_shmem_request;
-   prev_shmem_startup_hook = shmem_startup_hook;
-   shmem_startup_hook = worker_spi_shmem_startup;
-
    /* set up common data for all our workers */
    memset(&worker, 0, sizeof(worker));
    worker.bgw_flags = BGWORKER_SHMEM_ACCESS |
@@ -434,21 +357,6 @@ _PG_init(void)
    }
 }
 
-/*
- * Wrapper to initialize a session with the shared memory state
- * used by this module.  This is a convenience routine to be able to
- * see the custom wait event stored in shared memory without loading
- * through shared_preload_libraries.
- */
-Datum
-worker_spi_init(PG_FUNCTION_ARGS)
-{
-   /* Create (if necessary) and attach to our shared memory area. */
-   worker_spi_shmem_init();
-
-   PG_RETURN_VOID();
-}
-
 /*
  * Dynamically launch an SPI worker.
  */
@@ -461,9 +369,6 @@ worker_spi_launch(PG_FUNCTION_ARGS)
    BgwHandleStatus status;
    pid_t       pid;
 
-   /* Create (if necessary) and attach to our shared memory area. */
-   worker_spi_shmem_init();
-
    memset(&worker, 0, sizeof(worker));
    worker.bgw_flags = BGWORKER_SHMEM_ACCESS |
        BGWORKER_BACKEND_DATABASE_CONNECTION;
index 52a8789cc4d64ce7635c91d6624c4746bba3acc7..51b7951ad847bd988e5ac9efa1ee0164db35e649 100644 (file)
@@ -2993,6 +2993,8 @@ WaitEventBufferPin
 WaitEventClient
 WaitEventExtension
 WaitEventExtensionCounterData
+WaitEventExtensionEntryById
+WaitEventExtensionEntryByName
 WaitEventIO
 WaitEventIPC
 WaitEventSet