8000 Add GetNamedDSA() and GetNamedDSHash(). · sthagen/postgres-postgres@fe07100 · GitHub
[go: up one dir, main page]

Skip to content

Commit fe07100

Browse files
Add GetNamedDSA() and GetNamedDSHash().
Presently, the dynamic shared memory (DSM) registry only provides GetNamedDSMSegment(), which allocates a fixed-size segment. To use the DSM registry for more sophisticated things like dynamic shared memory areas (DSAs) or a hash table backed by a DSA (dshash), users need to create a DSM segment that stores various handles and LWLock tranche IDs and to write fairly complicated initialization code. Furthermore, there is likely little variation in this initialization code between libraries. This commit introduces functions that simplify allocating a DSA or dshash within the DSM registry. These functions are very similar to GetNamedDSMSegment(). Notable differences include the lack of an initialization callback parameter and the prohibition of calling the functions more than once for a given entry in each backend (which should be trivially avoidable in most circumstances). While at it, this commit bumps the maximum DSM registry entry name length from 63 bytes to 127 bytes. Also note that even though one could presumably detach/destroy the DSAs and dshashes created in the registry, such use-cases are not yet well-supported, if for no other reason than the associated DSM registry entries cannot be removed. Adding such support is left as a future exercise. The test_dsm_registry test module contains tests for the new functions and also serves as a complete usage example. Reviewed-by: Dagfinn Ilmari Mannsåker <ilmari@ilmari.org> Reviewed-by: Sami Imseih <samimseih@gmail.com> Reviewed-by: Florents Tselai <florents.tselai@gmail.com> Reviewed-by: Rahila Syed <rahilasyed90@gmail.com> Discussion: https://postgr.es/m/aEC8HGy2tRQjZg_8%40nathan
1 parent 9ca30a0 commit fe07100

File tree

9 files changed

+400
-46
lines changed

9 files changed

+400
-46
lines changed

src/backend/storage/ipc/dsm_registry.c

Lines changed: 251 additions & 14 deletions
40
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,20 @@
1515
* current backend. This function guarantees that only one backend
1616
* initializes the segment and that all other backends just attach it.
1717
*
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+
*
1832
* Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
1933
* Portions Copyright (c) 1994, Regents of the University of California
2034
*
@@ -32,6 +46,12 @@
3246
#include "storage/shmem.h"
3347
#include "utils/memutils.h"
3448

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+
3555
typedef struct DSMRegistryCtxStruct
3656
{
3757
dsa_handle dsah;
@@ -40,15 +60,48 @@ typedef struct DSMRegistryCtxStruct
60

4161
static DSMRegistryCtxStruct *DSMRegistryCtx;
4262

43-
typedef struct DSMRegistryEntry
63+
typedef struct NamedDSMState
4464
{
45-
char name[64];
4665
dsm_handle handle;
4766
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;
48101
} DSMRegistryEntry;
49102

50103
static const dshash_parameters dsh_params = {
51-
offsetof(DSMRegistryEntry, handle),
104+
offsetof(DSMRegistryEntry, type),
52105
sizeof(DSMRegistryEntry),
53106
dshash_strcmp,
54107
dshash_strhash,
@@ -141,7 +194,7 @@ GetNamedDSMSegment(const char *name, size_t size,
141194
ereport(ERROR,
142195
(errmsg("DSM segment name cannot be empty")));
143196

144-
if (strlen(name) >= offsetof(DSMRegistryEntry, handle))
197+
if (strlen(name) >= offsetof(DSMRegistryEntry, type))
145198
ereport(ERROR,
146199
(errmsg("DSM segment name too long")));
147200

@@ -158,32 +211,39 @@ GetNamedDSMSegment(const char *name, size_t size,
158211
entry = dshash_find_or_insert(dsm_registry_table, name, found);
159212
if (!(*found))
160213
{
214+
NamedDSMState *state = &entry->data.dsm;
215+
dsm_segment *seg;
216+
217+
entry->type = DSMR_ENTRY_TYPE_DSM;
218+
161219
/* Initialize the segment. */
162-
dsm_segment *seg = dsm_create(size, 0);
220+
seg = dsm_create(size, 0);
163221

164222
dsm_pin_segment(seg);
165223
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;
168226
ret = dsm_segment_address(seg);
169227

170228
if (init_callback)
171229
(*init_callback) (ret);
172230
}
173-
else if (entry->size != size)
174-
{
231+
else if (entry->type != DSMR_ENTRY_TYPE_DSM)
175232
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")));
179237
else
180238
{
181-
dsm_segment *seg = dsm_find_mapping(entry->handle);
239+
NamedDSMState *state = &entry->data.dsm;
240+
dsm_segment *seg;
182241

183242
/* If the existing segment is not already attached, attach it now. */
243+
seg = dsm_find_mapping(state->handle);
184244
if (seg == NULL)
185245
{
186-
seg = dsm_attach(entry->handle);
246+
seg = dsm_attach(state->handle);
187247
if (seg == NULL)
188248
elog(ERROR, "could not map dynamic shared memory segment");
189249

@@ -198,3 +258,180 @@ GetNamedDSMSegment(const char *name, size_t size,
198258

199259
return ret;
200260
}
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+
}

src/backend/utils/mmgr/dsa.c

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -531,6 +531,21 @@ dsa_attach(dsa_handle handle)
531531
return area;
532532
}
533533

534+
/*
535+
* Returns whether the area with the given handle was already attached by the
536+
* current process. The area must have been created with dsa_create (not
537+
* dsa_create_in_place).
538+
*/
539+
bool
540+
dsa_is_attached(dsa_handle handle)
541+
{
542+
/*
543+
* An area handle is really a DSM segment handle for the first segment, so
544+
* we can just search for that.
545+
*/
546+
return dsm_find_mapping(handle) != NULL;
547+
}
548+
534549
/*
535550
* Attach to an area that was created with dsa_create_in_place. The caller
536551
* must somehow know the location in memory that was used when the area was

src/include/storage/dsm_registry.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,15 @@
1313
#ifndef DSM_REGISTRY_H
1414
#define DSM_REGISTRY_H
1515

16+
#include "lib/dshash.h"
17+
1618
extern void *GetNamedDSMSegment(const char *name, size_t size,
1719
void (*init_callback) (void *ptr),
1820
bool *found);
19-
21+
extern dsa_area *GetNamedDSA(const char *name, bool *found);
22+
extern dshash_table *GetNamedDSHash(const char *name,
23+
const dshash_parameters *params,
24+
bool *found);
2025
extern Size DSMRegistryShmemSize(void);
2126
extern void DSMRegistryShmemInit(void);
2227

src/include/utils/dsa.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ extern dsa_area *dsa_create_in_place_ext(void *place, size_t size,
145145
size_t init_segment_size,
146146
size_t max_segment_size);
147147
extern dsa_area *dsa_attach(dsa_handle handle);
148+
extern bool dsa_is_attached(dsa_handle handle);
148149
extern dsa_area *dsa_attach_in_place(void *place, dsm_segment *segment);
149150
extern void dsa_release_in_place(void *place);
150151
extern void dsa_on_dsm_detach_release_in_place(dsm_segment *, Datum);

0 commit comments

Comments
 (0)
0