2222 */
2323#include "postgres.h"
2424
25+ #include "miscadmin.h"
26+ #include "port/pg_bitutils.h"
2527#include "storage/lmgr.h" /* for GetLockNameFromTagType */
2628#include "storage/lwlock.h" /* for GetLWLockIdentifier */
29+ #include "storage/spin.h"
30+ #include "utils/memutils.h"
2731#include "utils/wait_event.h"
2832
2933
3034static const char * pgstat_get_wait_activity (WaitEventActivity w );
3135static const char * pgstat_get_wait_bufferpin (WaitEventBufferPin w );
3236static const char * pgstat_get_wait_client (WaitEventClient w );
33- static const char * pgstat_get_wait_extension (WaitEventExtension w );
3437static const char * pgstat_get_wait_ipc (WaitEventIPC w );
3538static const char * pgstat_get_wait_timeout (WaitEventTimeout w );
3639static const char * pgstat_get_wait_io (WaitEventIO w );
@@ -42,6 +45,169 @@ uint32 *my_wait_event_info = &local_my_wait_event_info;
4245#define WAIT_EVENT_CLASS_MASK 0xFF000000
4346#define WAIT_EVENT_ID_MASK 0x0000FFFF
4447
48+ /* dynamic allocation counter for custom wait events in extensions */
49+ typedef struct WaitEventExtensionCounterData
50+ {
51+ int nextId ; /* next ID to assign */
52+ slock_t mutex ; /* protects the counter */
53+ } WaitEventExtensionCounterData ;
54+
55+ /* pointer to the shared memory */
56+ static WaitEventExtensionCounterData * WaitEventExtensionCounter ;
57+
58+ /* first event ID of custom wait events for extensions */
59+ #define NUM_BUILTIN_WAIT_EVENT_EXTENSION \
60+ (WAIT_EVENT_EXTENSION_FIRST_USER_DEFINED - WAIT_EVENT_EXTENSION)
61+
62+ /*
63+ * This is indexed by event ID minus NUM_BUILTIN_WAIT_EVENT_EXTENSION, and
64+ * stores the names of all dynamically-created event IDs known to the current
65+ * process. Any unused entries in the array will contain NULL.
66+ */
67+ static const char * * WaitEventExtensionNames = NULL ;
68+ static int WaitEventExtensionNamesAllocated = 0 ;
69+
70+ static const char * GetWaitEventExtensionIdentifier (uint16 eventId );
71+
72+ /*
73+ * Return the space for dynamic allocation counter.
74+ */
75+ Size
76+ WaitEventExtensionShmemSize (void )
77+ {
78+ return sizeof (WaitEventExtensionCounterData );
79+ }
80+
81+ /*
82+ * Allocate shmem space for dynamic allocation counter.
83+ */
84+ void
85+ WaitEventExtensionShmemInit (void )
86+ {
87+ bool found ;
88+
89+ WaitEventExtensionCounter = (WaitEventExtensionCounterData * )
90+ ShmemInitStruct ("WaitEventExtensionCounterData" ,
91+ WaitEventExtensionShmemSize (), & found );
92+
93+ if (!found )
94+ {
95+ /* initialize the allocation counter and its spinlock. */
96+ WaitEventExtensionCounter -> nextId = NUM_BUILTIN_WAIT_EVENT_EXTENSION ;
97+ SpinLockInit (& WaitEventExtensionCounter -> mutex );
98+ }
99+ }
100+
101+ /*
102+ * Allocate a new event ID and return the wait event.
103+ */
104+ uint32
105+ WaitEventExtensionNew (void )
106+ {
107+ uint16 eventId ;
108+
109+ Assert (LWLockHeldByMeInMode (AddinShmemInitLock , LW_EXCLUSIVE ));
110+
111+ SpinLockAcquire (& WaitEventExtensionCounter -> mutex );
112+
113+ if (WaitEventExtensionCounter -> nextId > PG_UINT16_MAX )
114+ {
115+ SpinLockRelease (& WaitEventExtensionCounter -> mutex );
116+ ereport (ERROR ,
117+ errcode (ERRCODE_PROGRAM_LIMIT_EXCEEDED ),
118+ errmsg ("too many wait events for extensions" ));
119+ }
120+
121+ eventId = WaitEventExtensionCounter -> nextId ++ ;
122+
123+ SpinLockRelease (& WaitEventExtensionCounter -> mutex );
124+
125+ return PG_WAIT_EXTENSION | eventId ;
126+ }
127+
128+ /*
129+ * Register a dynamic wait event name for extension in the lookup table
130+ * of the current process.
131+ *
132+ * This routine will save a pointer to the wait event name passed as an argument,
133+ * so the name should be allocated in a backend-lifetime context
134+ * (shared memory, TopMemoryContext, static constant, or similar).
135+ *
136+ * The "wait_event_name" will be user-visible as a wait event name, so try to
137+ * use a name that fits the style for those.
138+ */
139+ void
140+ WaitEventExtensionRegisterName (uint32 wait_event_info ,
141+ const char * wait_event_name )
142+ {
143+ uint32 classId ;
144+ uint16 eventId ;
145+
146+ classId = wait_event_info & WAIT_EVENT_CLASS_MASK ;
147+ eventId = wait_event_info & WAIT_EVENT_ID_MASK ;
148+
149+ /* Check the wait event class. */
150+ if (classId != PG_WAIT_EXTENSION )
151+ ereport (ERROR ,
152+ errcode (ERRCODE_INVALID_PARAMETER_VALUE ),
153+ errmsg ("invalid wait event class %u" , classId ));
154+
155+ /* This should only be called for user-defined wait event. */
156+ if (eventId < NUM_BUILTIN_WAIT_EVENT_EXTENSION )
157+ ereport (ERROR ,
158+ errcode (ERRCODE_INVALID_PARAMETER_VALUE ),
159+ errmsg ("invalid wait event ID %u" , eventId ));
160+
161+ /* Convert to array index. */
162+ eventId -= NUM_BUILTIN_WAIT_EVENT_EXTENSION ;
163+
164+ /* If necessary, create or enlarge array. */
165+ if (eventId >= WaitEventExtensionNamesAllocated )
166+ {
167+ uint32 newalloc ;
168+
169+ newalloc = pg_nextpower2_32 (Max (8 , eventId + 1 ));
170+
171+ if (WaitEventExtensionNames == NULL )
172+ WaitEventExtensionNames = (const char * * )
173+ MemoryContextAllocZero (TopMemoryContext ,
174+ newalloc * sizeof (char * ));
175+ else
176+ WaitEventExtensionNames =
177+ repalloc0_array (WaitEventExtensionNames , const char * ,
178+ WaitEventExtensionNamesAllocated , newalloc );
179+ WaitEventExtensionNamesAllocated = newalloc ;
180+ }
181+
182+ WaitEventExtensionNames [eventId ] = wait_event_name ;
183+ }
184+
185+ /*
186+ * Return the name of an wait event ID for extension.
187+ */
188+ static const char *
189+ GetWaitEventExtensionIdentifier (uint16 eventId )
190+ {
191+ /* Built-in event? */
192+ if (eventId < NUM_BUILTIN_WAIT_EVENT_EXTENSION )
193+ return "Extension" ;
194+
195+ /*
196+ * It is a user-defined wait event, so look at WaitEventExtensionNames[].
197+ * However, it is possible that the name has never been registered by
198+ * calling WaitEventExtensionRegisterName() in the current process, in
199+ * which case give up and return "extension".
200+ */
201+ eventId -= NUM_BUILTIN_WAIT_EVENT_EXTENSION ;
202+
203+ if (eventId >= WaitEventExtensionNamesAllocated ||
204+ WaitEventExtensionNames [eventId ] == NULL )
205+ return "extension" ;
206+
207+ return WaitEventExtensionNames [eventId ];
208+ }
209+
210+
45211/*
46212 * Configure wait event reporting to report wait events to *wait_event_info.
47213 * *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)
151317 case PG_WAIT_LOCK :
152318 event_name = GetLockNameFromTagType (eventId );
153319 break ;
320+ case PG_WAIT_EXTENSION :
321+ event_name = GetWaitEventExtensionIdentifier (eventId );
322+ break ;
154323 case PG_WAIT_BUFFERPIN :
155324 {
156325 WaitEventBufferPin w = (WaitEventBufferPin ) wait_event_info ;
@@ -172,13 +341,6 @@ pgstat_get_wait_event(uint32 wait_event_info)
172341 event_name = pgstat_get_wait_client (w );
173342 break ;
174343 }
175- case PG_WAIT_EXTENSION :
176- {
177- WaitEventExtension w = (WaitEventExtension ) wait_event_info ;
178-
179- event_name = pgstat_get_wait_extension (w );
180- break ;
181- }
182344 case PG_WAIT_IPC :
183345 {
184346 WaitEventIPC w = (WaitEventIPC ) wait_event_info ;
0 commit comments