@@ -45,6 +45,41 @@ uint32 *my_wait_event_info = &local_my_wait_event_info;
45
45
#define WAIT_EVENT_CLASS_MASK 0xFF000000
46
46
#define WAIT_EVENT_ID_MASK 0x0000FFFF
47
47
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
+
48
83
/* dynamic allocation counter for custom wait events in extensions */
49
84
typedef struct WaitEventExtensionCounterData
50
85
{
@@ -59,58 +94,118 @@ static WaitEventExtensionCounterData *WaitEventExtensionCounter;
59
94
#define NUM_BUILTIN_WAIT_EVENT_EXTENSION \
60
95
(WAIT_EVENT_EXTENSION_FIRST_USER_DEFINED - WAIT_EVENT_EXTENSION)
61
96
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)
69
99
70
100
static const char * GetWaitEventExtensionIdentifier (uint16 eventId );
71
101
72
102
/*
73
- * Return the space for dynamic allocation counter.
103
+ * Return the space for dynamic shared hash tables and dynamic allocation counter.
74
104
*/
75
105
Size
76
106
WaitEventExtensionShmemSize (void )
77
107
{
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 ;
79
116
}
80
117
81
118
/*
82
- * Allocate shmem space for dynamic allocation counter.
119
+ * Allocate shmem space for dynamic shared hash and dynamic allocation counter.
83
120
*/
84
121
void
85
122
WaitEventExtensionShmemInit (void )
86
123
{
87
124
bool found ;
125
+ HASHCTL info ;
88
126
89
127
WaitEventExtensionCounter = (WaitEventExtensionCounterData * )
90
128
ShmemInitStruct ("WaitEventExtensionCounterData" ,
91
- WaitEventExtensionShmemSize ( ), & found );
129
+ sizeof ( WaitEventExtensionCounterData ), & found );
92
130
93
131
if (!found )
94
132
{
95
133
/* initialize the allocation counter and its spinlock. */
96
134
WaitEventExtensionCounter -> nextId = NUM_BUILTIN_WAIT_EVENT_EXTENSION ;
97
135
SpinLockInit (& WaitEventExtensionCounter -> mutex );
98
136
}
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 );
99
155
}
100
156
101
157
/*
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.
103
162
*/
104
163
uint32
105
- WaitEventExtensionNew (void )
164
+ WaitEventExtensionNew (const char * wait_event_name )
106
165
{
107
166
uint16 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 );
108
188
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
+ }
110
204
205
+ /* Allocate a new event Id */
111
206
SpinLockAcquire (& WaitEventExtensionCounter -> mutex );
112
207
113
- if (WaitEventExtensionCounter -> nextId > PG_UINT16_MAX )
208
+ if (WaitEventExtensionCounter -> nextId >= WAIT_EVENT_EXTENSION_HASH_MAX_SIZE )
114
209
{
115
210
SpinLockRelease (& WaitEventExtensionCounter -> mutex );
116
211
ereport (ERROR ,
@@ -122,64 +217,23 @@ WaitEventExtensionNew(void)
122
217
123
218
SpinLockRelease (& WaitEventExtensionCounter -> mutex );
124
219
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 ));
160
227
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 ;
163
233
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 );
181
235
182
- WaitEventExtensionNames [ eventId ] = wait_event_name ;
236
+ return WAIT_EVENT_EXTENSION_INFO ( eventId ) ;
183
237
}
184
238
185
239
/*
@@ -188,23 +242,25 @@ WaitEventExtensionRegisterName(uint32 wait_event_info,
188
242
static const char *
189
243
GetWaitEventExtensionIdentifier (uint16 eventId )
190
244
{
245
+ bool found ;
246
+ WaitEventExtensionEntryById * entry ;
247
+
191
248
/* Built-in event? */
192
249
if (eventId < NUM_BUILTIN_WAIT_EVENT_EXTENSION )
193
250
return "Extension" ;
194
251
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 );
202
258
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 ) ;
206
262
207
- return WaitEventExtensionNames [ eventId ] ;
263
+ return entry -> wait_event_name ;
208
264
}
209
265
210
266
0 commit comments