aboutsummaryrefslogtreecommitdiff
path: root/src/backend/utils/activity/wait_event.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/utils/activity/wait_event.c')
-rw-r--r--src/backend/utils/activity/wait_event.c178
1 files changed, 170 insertions, 8 deletions
diff --git a/src/backend/utils/activity/wait_event.c b/src/backend/utils/activity/wait_event.c
index 59177de7a04..b3596ece80d 100644
--- a/src/backend/utils/activity/wait_event.c
+++ b/src/backend/utils/activity/wait_event.c
@@ -22,15 +22,18 @@
*/
#include "postgres.h"
+#include "miscadmin.h"
+#include "port/pg_bitutils.h"
#include "storage/lmgr.h" /* for GetLockNameFromTagType */
#include "storage/lwlock.h" /* for GetLWLockIdentifier */
+#include "storage/spin.h"
+#include "utils/memutils.h"
#include "utils/wait_event.h"
static const char *pgstat_get_wait_activity(WaitEventActivity w);
static const char *pgstat_get_wait_bufferpin(WaitEventBufferPin w);
static const char *pgstat_get_wait_client(WaitEventClient w);
-static const char *pgstat_get_wait_extension(WaitEventExtension w);
static const char *pgstat_get_wait_ipc(WaitEventIPC w);
static const char *pgstat_get_wait_timeout(WaitEventTimeout w);
static const char *pgstat_get_wait_io(WaitEventIO w);
@@ -42,6 +45,169 @@ uint32 *my_wait_event_info = &local_my_wait_event_info;
#define WAIT_EVENT_CLASS_MASK 0xFF000000
#define WAIT_EVENT_ID_MASK 0x0000FFFF
+/* dynamic allocation counter for custom wait events in extensions */
+typedef struct WaitEventExtensionCounterData
+{
+ int nextId; /* next ID to assign */
+ slock_t mutex; /* protects the counter */
+} WaitEventExtensionCounterData;
+
+/* pointer to the shared memory */
+static WaitEventExtensionCounterData *WaitEventExtensionCounter;
+
+/* first event ID of custom wait events for extensions */
+#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;
+
+static const char *GetWaitEventExtensionIdentifier(uint16 eventId);
+
+/*
+ * Return the space for dynamic allocation counter.
+ */
+Size
+WaitEventExtensionShmemSize(void)
+{
+ return sizeof(WaitEventExtensionCounterData);
+}
+
+/*
+ * Allocate shmem space for dynamic allocation counter.
+ */
+void
+WaitEventExtensionShmemInit(void)
+{
+ bool found;
+
+ WaitEventExtensionCounter = (WaitEventExtensionCounterData *)
+ ShmemInitStruct("WaitEventExtensionCounterData",
+ WaitEventExtensionShmemSize(), &found);
+
+ if (!found)
+ {
+ /* initialize the allocation counter and its spinlock. */
+ WaitEventExtensionCounter->nextId = NUM_BUILTIN_WAIT_EVENT_EXTENSION;
+ SpinLockInit(&WaitEventExtensionCounter->mutex);
+ }
+}
+
+/*
+ * Allocate a new event ID and return the wait event.
+ */
+uint32
+WaitEventExtensionNew(void)
+{
+ uint16 eventId;
+
+ Assert(LWLockHeldByMeInMode(AddinShmemInitLock, LW_EXCLUSIVE));
+
+ SpinLockAcquire(&WaitEventExtensionCounter->mutex);
+
+ if (WaitEventExtensionCounter->nextId > PG_UINT16_MAX)
+ {
+ SpinLockRelease(&WaitEventExtensionCounter->mutex);
+ ereport(ERROR,
+ errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("too many wait events for extensions"));
+ }
+
+ eventId = WaitEventExtensionCounter->nextId++;
+
+ 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));
+
+ /* Convert to array index. */
+ eventId -= NUM_BUILTIN_WAIT_EVENT_EXTENSION;
+
+ /* 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;
+ }
+
+ WaitEventExtensionNames[eventId] = wait_event_name;
+}
+
+/*
+ * Return the name of an wait event ID for extension.
+ */
+static const char *
+GetWaitEventExtensionIdentifier(uint16 eventId)
+{
+ /* 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;
+
+ if (eventId >= WaitEventExtensionNamesAllocated ||
+ WaitEventExtensionNames[eventId] == NULL)
+ return "extension";
+
+ return WaitEventExtensionNames[eventId];
+}
+
+
/*
* Configure wait event reporting to report wait events to *wait_event_info.
* *wait_event_info needs to be valid until pgstat_reset_wait_event_storage()
@@ -151,6 +317,9 @@ pgstat_get_wait_event(uint32 wait_event_info)
case PG_WAIT_LOCK:
event_name = GetLockNameFromTagType(eventId);
break;
+ case PG_WAIT_EXTENSION:
+ event_name = GetWaitEventExtensionIdentifier(eventId);
+ break;
case PG_WAIT_BUFFERPIN:
{
WaitEventBufferPin w = (WaitEventBufferPin) wait_event_info;
@@ -172,13 +341,6 @@ pgstat_get_wait_event(uint32 wait_event_info)
event_name = pgstat_get_wait_client(w);
break;
}
- case PG_WAIT_EXTENSION:
- {
- WaitEventExtension w = (WaitEventExtension) wait_event_info;
-
- event_name = pgstat_get_wait_extension(w);
- break;
- }
case PG_WAIT_IPC:
{
WaitEventIPC w = (WaitEventIPC) wait_event_info;