@@ -45,6 +45,41 @@ uint32 *my_wait_event_info = &local_my_wait_event_info;
4545#define WAIT_EVENT_CLASS_MASK 0xFF000000
4646#define WAIT_EVENT_ID_MASK 0x0000FFFF
4747
48+ /*
49+ * Hash tables for storing custom wait event ids and their names in
50+ * shared memory.
51+ *
52+ * WaitEventExtensionHashById is used to find the name from a event id.
53+ * Any backend can search it to find custom wait events.
54+ *
55+ * WaitEventExtensionHashByName is used to find the event ID from a name.
56+ * It is used to ensure that no duplicated entries are registered.
57+ *
58+ * The size of the hash table is based on the assumption that
59+ * WAIT_EVENT_EXTENSION_BASH_INIT_SIZE is enough for most cases, and it seems
60+ * unlikely that the number of entries will reach
61+ * WAIT_EVENT_EXTENSION_BASH_MAX_SIZE.
62+ */
63+ static HTAB * WaitEventExtensionHashById ;/* find names from IDs */
64+ static HTAB * WaitEventExtensionHashByName ;/* find IDs from names */
65+
66+ #define WAIT_EVENT_EXTENSION_HASH_INIT_SIZE 16
67+ #define WAIT_EVENT_EXTENSION_HASH_MAX_SIZE 128
68+
69+ /* hash table entries */
70+ typedef struct WaitEventExtensionEntryById
71+ {
72+ uint16 event_id ;/* hash key */
73+ char wait_event_name [NAMEDATALEN ];/* custom wait event name */
74+ }WaitEventExtensionEntryById ;
75+
76+ typedef struct WaitEventExtensionEntryByName
77+ {
78+ char wait_event_name [NAMEDATALEN ];/* hash key */
79+ uint16 event_id ;/* wait event ID */
80+ }WaitEventExtensionEntryByName ;
81+
82+
4883/* dynamic allocation counter for custom wait events in extensions */
4984typedef struct WaitEventExtensionCounterData
5085{
@@ -59,58 +94,118 @@ static WaitEventExtensionCounterData *WaitEventExtensionCounter;
5994#define NUM_BUILTIN_WAIT_EVENT_EXTENSION \
6095(WAIT_EVENT_EXTENSION_FIRST_USER_DEFINED - WAIT_EVENT_EXTENSION)
6196
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 ;
97+ /* wait event info for extensions */
98+ #define WAIT_EVENT_EXTENSION_INFO (eventId )(PG_WAIT_EXTENSION | eventId)
6999
70100static const char * GetWaitEventExtensionIdentifier (uint16 eventId );
71101
72102/*
73- * Return the space for dynamic allocation counter.
103+ * Return the space for dynamicshared hash tables and dynamic allocation counter.
74104 */
75105Size
76106WaitEventExtensionShmemSize (void )
77107{
78- return sizeof (WaitEventExtensionCounterData );
108+ Size sz ;
109+
110+ sz = MAXALIGN (sizeof (WaitEventExtensionCounterData ));
111+ sz = add_size (sz ,hash_estimate_size (WAIT_EVENT_EXTENSION_HASH_MAX_SIZE ,
112+ sizeof (WaitEventExtensionEntryById )));
113+ sz = add_size (sz ,hash_estimate_size (WAIT_EVENT_EXTENSION_HASH_MAX_SIZE ,
114+ sizeof (WaitEventExtensionEntryByName )));
115+ return sz ;
79116}
80117
81118/*
82- * Allocate shmem space for dynamic allocation counter.
119+ * Allocate shmem space for dynamicshared hash and dynamic allocation counter.
83120 */
84121void
85122WaitEventExtensionShmemInit (void )
86123{
87124bool found ;
125+ HASHCTL info ;
88126
89127WaitEventExtensionCounter = (WaitEventExtensionCounterData * )
90128ShmemInitStruct ("WaitEventExtensionCounterData" ,
91- WaitEventExtensionShmemSize ( ),& found );
129+ sizeof ( WaitEventExtensionCounterData ),& found );
92130
93131if (!found )
94132{
95133/* initialize the allocation counter and its spinlock. */
96134WaitEventExtensionCounter -> nextId = NUM_BUILTIN_WAIT_EVENT_EXTENSION ;
97135SpinLockInit (& WaitEventExtensionCounter -> mutex );
98136}
137+
138+ /* initialize or attach the hash tables to store custom wait events */
139+ info .keysize = sizeof (uint16 );
140+ info .entrysize = sizeof (WaitEventExtensionEntryById );
141+ WaitEventExtensionHashById = ShmemInitHash ("WaitEventExtension hash by id" ,
142+ WAIT_EVENT_EXTENSION_HASH_INIT_SIZE ,
143+ WAIT_EVENT_EXTENSION_HASH_MAX_SIZE ,
144+ & info ,
145+ HASH_ELEM |HASH_BLOBS );
146+
147+ /* key is a NULL-terminated string */
148+ info .keysize = sizeof (char [NAMEDATALEN ]);
149+ info .entrysize = sizeof (WaitEventExtensionEntryByName );
150+ WaitEventExtensionHashByName = ShmemInitHash ("WaitEventExtension hash by name" ,
151+ WAIT_EVENT_EXTENSION_HASH_INIT_SIZE ,
152+ WAIT_EVENT_EXTENSION_HASH_MAX_SIZE ,
153+ & info ,
154+ HASH_ELEM |HASH_STRINGS );
99155}
100156
101157/*
102- * Allocate a new event ID and return the wait event.
158+ * Allocate a new event ID and return the wait event info.
159+ *
160+ * If the wait event name is already defined, this does not allocate a new
161+ * entry; it returns the wait event information associated to the name.
103162 */
104163uint32
105- WaitEventExtensionNew (void )
164+ WaitEventExtensionNew (const char * wait_event_name )
106165{
107166uint16 eventId ;
167+ bool found ;
168+ WaitEventExtensionEntryByName * entry_by_name ;
169+ WaitEventExtensionEntryById * entry_by_id ;
170+
171+ /* Check the limit of the length of the event name */
172+ if (strlen (wait_event_name ) >=NAMEDATALEN )
173+ elog (ERROR ,
174+ "cannot use custom wait event string longer than %u characters" ,
175+ NAMEDATALEN - 1 );
176+
177+ /*
178+ * Check if the wait event info associated to the name is already defined,
179+ * and return it if so.
180+ */
181+ LWLockAcquire (WaitEventExtensionLock ,LW_SHARED );
182+ entry_by_name = (WaitEventExtensionEntryByName * )
183+ hash_search (WaitEventExtensionHashByName ,wait_event_name ,
184+ HASH_FIND ,& found );
185+ LWLockRelease (WaitEventExtensionLock );
186+ if (found )
187+ return WAIT_EVENT_EXTENSION_INFO (entry_by_name -> event_id );
108188
109- Assert (LWLockHeldByMeInMode (AddinShmemInitLock ,LW_EXCLUSIVE ));
189+ /*
190+ * Allocate and register a new wait event. Recheck if the event name
191+ * exists, as it could be possible that a concurrent process has inserted
192+ * one with the same name since the LWLock acquired again here was
193+ * previously released.
194+ */
195+ LWLockAcquire (WaitEventExtensionLock ,LW_EXCLUSIVE );
196+ entry_by_name = (WaitEventExtensionEntryByName * )
197+ hash_search (WaitEventExtensionHashByName ,wait_event_name ,
198+ HASH_FIND ,& found );
199+ if (found )
200+ {
201+ LWLockRelease (WaitEventExtensionLock );
202+ return WAIT_EVENT_EXTENSION_INFO (entry_by_name -> event_id );
203+ }
110204
205+ /* Allocate a new event Id */
111206SpinLockAcquire (& WaitEventExtensionCounter -> mutex );
112207
113- if (WaitEventExtensionCounter -> nextId > PG_UINT16_MAX )
208+ if (WaitEventExtensionCounter -> nextId >= WAIT_EVENT_EXTENSION_HASH_MAX_SIZE )
114209{
115210SpinLockRelease (& WaitEventExtensionCounter -> mutex );
116211ereport (ERROR ,
@@ -122,64 +217,23 @@ WaitEventExtensionNew(void)
122217
123218SpinLockRelease (& WaitEventExtensionCounter -> mutex );
124219
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 ));
220+ /* Register the new wait event */
221+ entry_by_id = (WaitEventExtensionEntryById * )
222+ hash_search (WaitEventExtensionHashById ,& eventId ,
223+ HASH_ENTER ,& found );
224+ Assert (!found );
225+ strlcpy (entry_by_id -> wait_event_name ,wait_event_name ,
226+ sizeof (entry_by_id -> wait_event_name ));
160227
161- /* Convert to array index. */
162- eventId -= NUM_BUILTIN_WAIT_EVENT_EXTENSION ;
228+ entry_by_name = (WaitEventExtensionEntryByName * )
229+ hash_search (WaitEventExtensionHashByName ,wait_event_name ,
230+ HASH_ENTER ,& found );
231+ Assert (!found );
232+ entry_by_name -> event_id = eventId ;
163233
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- }
234+ LWLockRelease (WaitEventExtensionLock );
181235
182- WaitEventExtensionNames [ eventId ] = wait_event_name ;
236+ return WAIT_EVENT_EXTENSION_INFO ( eventId ) ;
183237}
184238
185239/*
@@ -188,23 +242,25 @@ WaitEventExtensionRegisterName(uint32 wait_event_info,
188242static const char *
189243GetWaitEventExtensionIdentifier (uint16 eventId )
190244{
245+ bool found ;
246+ WaitEventExtensionEntryById * entry ;
247+
191248/* Built-in event? */
192249if (eventId < NUM_BUILTIN_WAIT_EVENT_EXTENSION )
193250return "Extension" ;
194251
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 ;
252+ /* It is a user-defined wait event, so lookup hash table. */
253+ LWLockAcquire (WaitEventExtensionLock ,LW_SHARED );
254+ entry = (WaitEventExtensionEntryById * )
255+ hash_search (WaitEventExtensionHashById ,& eventId ,
256+ HASH_FIND ,& found );
257+ LWLockRelease (WaitEventExtensionLock );
202258
203- if (eventId >= WaitEventExtensionNamesAllocated ||
204- WaitEventExtensionNames [ eventId ] == NULL )
205- return "extension" ;
259+ if (! entry )
260+ elog ( ERROR , "could not find custom wait event name for ID %u" ,
261+ eventId ) ;
206262
207- return WaitEventExtensionNames [ eventId ] ;
263+ return entry -> wait_event_name ;
208264}
209265
210266