15
15
* current backend. This function guarantees that only one backend
16
16
* initializes the segment and that all other backends just attach it.
17
17
*
18
+ * A DSA can be created in or retrieved from the registry by calling
19
+ * GetNamedDSA(). As with GetNamedDSMSegment(), if a DSA with the provided
20
+ * name does not yet exist, it is created. Otherwise, GetNamedDSA()
21
+ * ensures the DSA is attached to the current backend. This function
22
+ * guarantees that only one backend initializes the DSA and that all other
23
+ * backends just attach it.
24
+ *
25
+ * A dshash table can be created in or retrieved from the registry by
26
+ * calling GetNamedDSHash(). As with GetNamedDSMSegment(), if a hash
27
+ * table with the provided name does not yet exist, it is created.
28
+ * Otherwise, GetNamedDSHash() ensures the hash table is attached to the
29
+ * current backend. This function guarantees that only one backend
30
+ * initializes the table and that all other backends just attach it.
31
+ *
18
32
* Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
19
33
* Portions Copyright (c) 1994, Regents of the University of California
20
34
*
32
46
#include "storage/shmem.h"
33
47
#include "utils/memutils.h"
34
48
49
+ #define DSMR_NAME_LEN 128
50
+
51
+ #define DSMR_DSA_TRANCHE_SUFFIX " DSA"
52
+ #define DSMR_DSA_TRANCHE_SUFFIX_LEN (sizeof(DSMR_DSA_TRANCHE_SUFFIX) - 1)
53
+ #define DSMR_DSA_TRANCHE_NAME_LEN (DSMR_NAME_LEN + DSMR_DSA_TRANCHE_SUFFIX_LEN)
54
+
35
55
typedef struct DSMRegistryCtxStruct
36
56
{
37
57
dsa_handle dsah ;
@@ -40,15 +60,48 @@ typedef struct DSMRegistryCtxStruct
40
60
41
61
static DSMRegistryCtxStruct * DSMRegistryCtx ;
42
62
43
- typedef struct DSMRegistryEntry
63
+ typedef struct NamedDSMState
44
64
{
45
- char name [64 ];
46
65
dsm_handle handle ;
47
66
size_t size ;
67
+ } NamedDSMState ;
68
+
69
+ typedef struct NamedDSAState
70
+ {
71
+ dsa_handle handle ;
72
+ int tranche ;
73
+ char tranche_name [DSMR_DSA_TRANCHE_NAME_LEN ];
74
+ } NamedDSAState ;
75
+
76
+ typedef struct NamedDSHState
77
+ {
78
+ NamedDSAState dsa ;
79
+ dshash_table_handle handle ;
80
+ int tranche ;
81
+ char tranche_name [DSMR_NAME_LEN ];
82
+ } NamedDSHState ;
83
+
84
+ typedef enum DSMREntryType
85
+ {
86
+ DSMR_ENTRY_TYPE_DSM ,
87
+ DSMR_ENTRY_TYPE_DSA ,
88
+ DSMR_ENTRY_TYPE_DSH ,
89
+ } DSMREntryType ;
90
+
91
+ typedef struct DSMRegistryEntry
92
+ {
93
+ char name [DSMR_NAME_LEN ];
94
+ DSMREntryType type ;
95
+ union
96
+ {
97
+ NamedDSMState dsm ;
98
+ NamedDSAState dsa ;
99
+ NamedDSHState dsh ;
100
+ } data ;
48
101
} DSMRegistryEntry ;
49
102
50
103
static const dshash_parameters dsh_params = {
51
- offsetof(DSMRegistryEntry , handle ),
104
+ offsetof(DSMRegistryEntry , type ),
52
105
sizeof (DSMRegistryEntry ),
53
106
dshash_strcmp ,
54
107
dshash_strhash ,
@@ -141,7 +194,7 @@ GetNamedDSMSegment(const char *name, size_t size,
141
194
ereport (ERROR ,
142
195
(errmsg ("DSM segment name cannot be empty" )));
143
196
144
- if (strlen (name ) >= offsetof(DSMRegistryEntry , handle ))
197
+ if (strlen (name ) >= offsetof(DSMRegistryEntry , type ))
145
198
ereport (ERROR ,
146
199
(errmsg ("DSM segment name too long" )));
147
200
@@ -158,32 +211,39 @@ GetNamedDSMSegment(const char *name, size_t size,
158
211
entry = dshash_find_or_insert (dsm_registry_table , name , found );
159
212
if (!(* found ))
160
213
{
214
+ NamedDSMState * state = & entry -> data .dsm ;
215
+ dsm_segment * seg ;
216
+
217
+ entry -> type = DSMR_ENTRY_TYPE_DSM ;
218
+
161
219
/* Initialize the segment. */
162
- dsm_segment * seg = dsm_create (size , 0 );
220
+ seg = dsm_create (size , 0 );
163
221
164
222
dsm_pin_segment (seg );
165
223
dsm_pin_mapping (seg );
166
- entry -> handle = dsm_segment_handle (seg );
167
- entry -> size = size ;
224
+ state -> handle = dsm_segment_handle (seg );
225
+ state -> size = size ;
168
226
ret = dsm_segment_address (seg );
169
227
170
228
if (init_callback )
171
229
(* init_callback ) (ret );
172
230
}
173
- else if (entry -> size != size )
174
- {
231
+ else if (entry -> type != DSMR_ENTRY_TYPE_DSM )
175
232
ereport (ERROR ,
176
- (errmsg ("requested DSM segment size does not match size of "
177
- "existing segment" )));
178
- }
233
+ (errmsg ("requested DSM segment does not match type of existing entry" )));
234
+ else if (entry -> data .dsm .size != size )
235
+ ereport (ERROR ,
236
+ (errmsg ("requested DSM segment size does not match size of existing segment" )));
179
237
else
180
238
{
181
- dsm_segment * seg = dsm_find_mapping (entry -> handle );
239
+ NamedDSMState * state = & entry -> data .dsm ;
240
+ dsm_segment * seg ;
182
241
183
242
/* If the existing segment is not already attached, attach it now. */
243
+ seg = dsm_find_mapping (state -> handle );
184
244
if (seg == NULL )
185
245
{
186
- seg = dsm_attach (entry -> handle );
246
+ seg = dsm_attach (state -> handle );
187
247
if (seg == NULL )
188
248
elog (ERROR , "could not map dynamic shared memory segment" );
189
249
@@ -198,3 +258,180 @@ GetNamedDSMSegment(const char *name, size_t size,
198
258
199
259
return ret ;
200
260
}
261
+
262
+ /*
263
+ * Initialize or attach a named DSA.
264
+ *
265
+ * This routine returns a pointer to the DSA. A new LWLock tranche ID will be
266
+ * generated if needed. Note that the lock tranche will be registered with the
267
+ * provided name. Also note that this should be called at most once for a
268
+ * given DSA in each backend.
269
+ */
270
+ dsa_area *
271
+ GetNamedDSA (const char * name , bool * found )
272
+ {
273
+ DSMRegistryEntry * entry ;
274
+ MemoryContext oldcontext ;
275
+ dsa_area * ret ;
276
+
277
+ Assert (found );
278
+
279
+ if (!name || * name == '\0' )
280
+ ereport (ERROR ,
281
+ (errmsg ("DSA name cannot be empty" )));
282
+
283
+ if (strlen (name ) >= offsetof(DSMRegistryEntry , type ))
284
+ ereport (ERROR ,
285
+ (errmsg ("DSA name too long" )));
286
+
287
+ /* Be sure any local memory allocated by DSM/DSA routines is persistent. */
288
+ oldcontext = MemoryContextSwitchTo (TopMemoryContext );
289
+
290
+ /* Connect to the registry. */
291
+ init_dsm_registry ();
292
+
293
+ entry = dshash_find_or_insert (dsm_registry_table , name , found );
294
+ if (!(* found ))
295
+ {
296
+ NamedDSAState * state = & entry -> data .dsa ;
297
+
298
+ entry -> type = DSMR_ENTRY_TYPE_DSA ;
299
+
300
+ /* Initialize the LWLock tranche for the DSA. */
301
+ state -> tranche = LWLockNewTrancheId ();
302
+ strcpy (state -> tranche_name , name );
303
+ LWLockRegisterTranche (state -> tranche , state -> tranche_name );
304
+
305
+ /* Initialize the DSA. */
306
+ ret = dsa_create (state -> tranche );
307
+ dsa_pin (ret );
308
+ dsa_pin_mapping (ret );
309
+
310
+ /* Store handle for other backends to use. */
311
+ state -> handle = dsa_get_handle (ret );
312
+ }
313
+ else if (entry -> type != DSMR_ENTRY_TYPE_DSA )
314
+ ereport (ERROR ,
315
+ (errmsg ("requested DSA does not match type of existing entry" )));
316
+ else
317
+ {
318
+ NamedDSAState * state = & entry -> data .dsa ;
319
+
320
+ if (dsa_is_attached (state -> handle ))
321
+ ereport (ERROR ,
322
+ (errmsg ("requested DSA already attached to current process" )));
323
+
324
+ /* Initialize existing LWLock tranche for the DSA. */
325
+ LWLockRegisterTranche (state -> tranche , state -> tranche_name );
326
+
327
+ /* Attach to existing DSA. */
328
+ ret = dsa_attach (state -> handle );
329
+ dsa_pin_mapping (ret );
330
+ }
331
+
332
+ dshash_release_lock (dsm_registry_table , entry );
333
+ MemoryContextSwitchTo (oldcontext );
334
+
335
+ return ret ;
336
+ }
337
+
338
+ /*
339
+ * Initialize or attach a named dshash table.
340
+ *
341
+ * This routine returns the address of the table. The tranche_id member of
342
+ * params is ignored; new tranche IDs will be generated if needed. Note that
343
+ * the DSA lock tranche will be registered with the provided name with " DSA"
344
+ * appended. The dshash lock tranche will be registered with the provided
345
+ * name. Also note that this should be called at most once for a given table
346
+ * in each backend.
347
+ */
348
+ dshash_table *
349
+ GetNamedDSHash (const char * name , const dshash_parameters * params , bool * found )
350
+ {
351
+ DSMRegistryEntry * entry ;
352
+ MemoryContext oldcontext ;
353
+ dshash_table * ret ;
354
+
355
+ Assert (params );
356
+ Assert (found );
357
+
358
+ if (!name || * name == '\0' )
359
+ ereport (ERROR ,
360
+ (errmsg ("DSHash name cannot be empty" )));
361
+
362
+ if (strlen (name ) >= offsetof(DSMRegistryEntry , type ))
363
+ ereport (ERROR ,
364
+ (errmsg ("DSHash name too long" )));
365
+
366
+ /* Be sure any local memory allocated by DSM/DSA routines is persistent. */
367
+ oldcontext = MemoryContextSwitchTo (TopMemoryContext );
368
+
369
+ /* Connect to the registry. */
370
+ init_dsm_registry ();
371
+
372
+ entry = dshash_find_or_insert (dsm_registry_table , name , found );
373
+ if (!(* found ))
374
+ {
375
+ NamedDSAState * dsa_state = & entry -> data .dsh .dsa ;
376
+ NamedDSHState * dsh_state = & entry -> data .dsh ;
377
+ dshash_parameters params_copy ;
378
+ dsa_area * dsa ;
379
+
380
+ entry -> type = DSMR_ENTRY_TYPE_DSH ;
381
+
382
+ /* Initialize the LWLock tranche for the DSA. */
383
+ dsa_state -> tranche = LWLockNewTrancheId ();
384
+ sprintf (dsa_state -> tranche_name , "%s%s" , name , DSMR_DSA_TRANCHE_SUFFIX );
385
+ LWLockRegisterTranche (dsa_state -> tranche , dsa_state -> tranche_name );
386
+
387
+ /* Initialize the LWLock tranche for the dshash table. */
388
+ dsh_state -> tranche = LWLockNewTrancheId ();
389
+ strcpy (dsh_state -> tranche_name , name );
390
+ LWLockRegisterTranche (dsh_state -> tranche , dsh_state -> tranche_name );
391
+
392
+ /* Initialize the DSA for the hash table. */
393
+ dsa = dsa_create (dsa_state -> tranche );
394
+ dsa_pin (dsa );
395
+ dsa_pin_mapping (dsa );
396
+
397
+ /* Initialize the dshash table. */
398
+ memcpy (& params_copy , params , sizeof (dshash_parameters ));
399
+ params_copy .tranche_id = dsh_state -> tranche ;
400
+ ret = dshash_create (dsa , & params_copy , NULL );
401
+
402
+ /* Store handles for other backends to use. */
403
+ dsa_state -> handle = dsa_get_handle (dsa );
404
+ dsh_state -> handle = dshash_get_hash_table_handle (ret );
405
+ }
406
+ else if (entry -> type != DSMR_ENTRY_TYPE_DSH )
407
+ ereport (ERROR ,
408
+ (errmsg ("requested DSHash does not match type of existing entry" )));
409
+ else
410
+ {
411
+ NamedDSAState * dsa_state = & entry -> data .dsh .dsa ;
412
+ NamedDSHState * dsh_state = & entry -> data .dsh ;
413
+ dsa_area * dsa ;
414
+
415
+ /* XXX: Should we verify params matches what table was created with? */
416
+
417
+ if (dsa_is_attached (dsa_state -> handle ))
418
+ ereport (ERROR ,
419
+ (errmsg ("requested DSHash already attached to current process" )));
420
+
421
+ /* Initialize existing LWLock tranches for the DSA and dshash table. */
422
+ LWLockRegisterTranche (dsa_state -> tranche , dsa_state -> tranche_name );
423
+ LWLockRegisterTranche (dsh_state -> tranche , dsh_state -> tranche_name );
424
+
425
+ /* Attach to existing DSA for the hash table. */
426
+ dsa = dsa_attach (dsa_state -> handle );
427
+ dsa_pin_mapping (dsa );
428
+
429
+ /* Attach to existing dshash table. */
430
+ ret = dshash_attach (dsa , params , dsh_state -> handle , NULL );
431
+ }
432
+
433
+ dshash_release_lock (dsm_registry_table , entry );
434
+ MemoryContextSwitchTo (oldcontext );
435
+
436
+ return ret ;
437
+ }
0 commit comments