PostgreSQL Source Code: src/backend/storage/ipc/dsm_registry.c Source File

PostgreSQL Source Code git master
dsm_registry.c
Go to the documentation of this file.
1/*-------------------------------------------------------------------------
2 *
3 * dsm_registry.c
4 * Functions for interfacing with the dynamic shared memory registry.
5 *
6 * This provides a way for libraries to use shared memory without needing
7 * to request it at startup time via a shmem_request_hook. The registry
8 * stores dynamic shared memory (DSM) segment handles keyed by a
9 * library-specified string.
10 *
11 * The registry is accessed by calling GetNamedDSMSegment(). If a segment
12 * with the provided name does not yet exist, it is created and initialized
13 * with the provided init_callback callback function. Otherwise,
14 * GetNamedDSMSegment() simply ensures that the segment is attached to the
15 * current backend. This function guarantees that only one backend
16 * initializes the segment and that all other backends just attach it.
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 *
32 * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
33 * Portions Copyright (c) 1994, Regents of the University of California
34 *
35 * IDENTIFICATION
36 * src/backend/storage/ipc/dsm_registry.c
37 *
38 *-------------------------------------------------------------------------
39 */
40
41#include "postgres.h"
42
43#include "funcapi.h"
44#include "lib/dshash.h"
45#include "storage/dsm_registry.h"
46#include "storage/lwlock.h"
47#include "storage/shmem.h"
48#include "utils/builtins.h"
49#include "utils/memutils.h"
50
51 typedef struct DSMRegistryCtxStruct
52{
53 dsa_handle dsah;
54 dshash_table_handle dshh;
55 } DSMRegistryCtxStruct;
56
57 static DSMRegistryCtxStruct *DSMRegistryCtx;
58
59 typedef struct NamedDSMState
60{
61 dsm_handle handle;
62 size_t size;
63 } NamedDSMState;
64
65 typedef struct NamedDSAState
66{
67 dsa_handle handle;
68 int tranche;
69 } NamedDSAState;
70
71 typedef struct NamedDSHState
72{
73 dsa_handle dsa_handle;
74 dshash_table_handle dsh_handle;
75 int tranche;
76 } NamedDSHState;
77
78 typedef enum DSMREntryType
79{
80 DSMR_ENTRY_TYPE_DSM,
81 DSMR_ENTRY_TYPE_DSA,
82 DSMR_ENTRY_TYPE_DSH,
83 } DSMREntryType;
84
85 static const char *const DSMREntryTypeNames[] =
86{
87 [DSMR_ENTRY_TYPE_DSM] = "segment",
88 [DSMR_ENTRY_TYPE_DSA] = "area",
89 [DSMR_ENTRY_TYPE_DSH] = "hash",
90};
91
92 typedef struct DSMRegistryEntry
93{
94 char name[NAMEDATALEN];
95 DSMREntryType type;
96 union
97 {
98 NamedDSMState dsm;
99 NamedDSAState dsa;
100 NamedDSHState dsh;
101 };
102 } DSMRegistryEntry;
103
104 static const dshash_parameters dsh_params = {
105 offsetof(DSMRegistryEntry, type),
106 sizeof(DSMRegistryEntry),
107 dshash_strcmp,
108 dshash_strhash,
109 dshash_strcpy,
110 LWTRANCHE_DSM_REGISTRY_HASH
111};
112
113 static dsa_area *dsm_registry_dsa;
114 static dshash_table *dsm_registry_table;
115
116Size
117 DSMRegistryShmemSize(void)
118{
119 return MAXALIGN(sizeof(DSMRegistryCtxStruct));
120}
121
122void
123 DSMRegistryShmemInit(void)
124{
125 bool found;
126
127 DSMRegistryCtx = (DSMRegistryCtxStruct *)
128 ShmemInitStruct("DSM Registry Data",
129 DSMRegistryShmemSize(),
130 &found);
131
132 if (!found)
133 {
134 DSMRegistryCtx->dsah = DSA_HANDLE_INVALID;
135 DSMRegistryCtx->dshh = DSHASH_HANDLE_INVALID;
136 }
137}
138
139/*
140 * Initialize or attach to the dynamic shared hash table that stores the DSM
141 * registry entries, if not already done. This must be called before accessing
142 * the table.
143 */
144static void
145 init_dsm_registry(void)
146{
147 /* Quick exit if we already did this. */
148 if (dsm_registry_table)
149 return;
150
151 /* Otherwise, use a lock to ensure only one process creates the table. */
152 LWLockAcquire(DSMRegistryLock, LW_EXCLUSIVE);
153
154 if (DSMRegistryCtx->dshh == DSHASH_HANDLE_INVALID)
155 {
156 /* Initialize dynamic shared hash table for registry. */
157 dsm_registry_dsa = dsa_create(LWTRANCHE_DSM_REGISTRY_DSA);
158 dsa_pin(dsm_registry_dsa);
159 dsa_pin_mapping(dsm_registry_dsa);
160 dsm_registry_table = dshash_create(dsm_registry_dsa, &dsh_params, NULL);
161
162 /* Store handles in shared memory for other backends to use. */
163 DSMRegistryCtx->dsah = dsa_get_handle(dsm_registry_dsa);
164 DSMRegistryCtx->dshh = dshash_get_hash_table_handle(dsm_registry_table);
165 }
166 else
167 {
168 /* Attach to existing dynamic shared hash table. */
169 dsm_registry_dsa = dsa_attach(DSMRegistryCtx->dsah);
170 dsa_pin_mapping(dsm_registry_dsa);
171 dsm_registry_table = dshash_attach(dsm_registry_dsa, &dsh_params,
172 DSMRegistryCtx->dshh, NULL);
173 }
174
175 LWLockRelease(DSMRegistryLock);
176}
177
178/*
179 * Initialize or attach a named DSM segment.
180 *
181 * This routine returns the address of the segment. init_callback is called to
182 * initialize the segment when it is first created.
183 */
184void *
185 GetNamedDSMSegment(const char *name, size_t size,
186 void (*init_callback) (void *ptr), bool *found)
187{
188 DSMRegistryEntry *entry;
189 MemoryContext oldcontext;
190 void *ret;
191
192 Assert(found);
193
194 if (!name || *name == '0円')
195 ereport(ERROR,
196 (errmsg("DSM segment name cannot be empty")));
197
198 if (strlen(name) >= offsetof(DSMRegistryEntry, type))
199 ereport(ERROR,
200 (errmsg("DSM segment name too long")));
201
202 if (size == 0)
203 ereport(ERROR,
204 (errmsg("DSM segment size must be nonzero")));
205
206 /* Be sure any local memory allocated by DSM/DSA routines is persistent. */
207 oldcontext = MemoryContextSwitchTo(TopMemoryContext);
208
209 /* Connect to the registry. */
210 init_dsm_registry();
211
212 entry = dshash_find_or_insert(dsm_registry_table, name, found);
213 if (!(*found))
214 {
215 NamedDSMState *state = &entry->dsm;
216 dsm_segment *seg;
217
218 entry->type = DSMR_ENTRY_TYPE_DSM;
219
220 /* Initialize the segment. */
221 seg = dsm_create(size, 0);
222
223 dsm_pin_segment(seg);
224 dsm_pin_mapping(seg);
225 state->handle = dsm_segment_handle(seg);
226 state->size = size;
227 ret = dsm_segment_address(seg);
228
229 if (init_callback)
230 (*init_callback) (ret);
231 }
232 else if (entry->type != DSMR_ENTRY_TYPE_DSM)
233 ereport(ERROR,
234 (errmsg("requested DSM segment does not match type of existing entry")));
235 else if (entry->dsm.size != size)
236 ereport(ERROR,
237 (errmsg("requested DSM segment size does not match size of existing segment")));
238 else
239 {
240 NamedDSMState *state = &entry->dsm;
241 dsm_segment *seg;
242
243 /* If the existing segment is not already attached, attach it now. */
244 seg = dsm_find_mapping(state->handle);
245 if (seg == NULL)
246 {
247 seg = dsm_attach(state->handle);
248 if (seg == NULL)
249 elog(ERROR, "could not map dynamic shared memory segment");
250
251 dsm_pin_mapping(seg);
252 }
253
254 ret = dsm_segment_address(seg);
255 }
256
257 dshash_release_lock(dsm_registry_table, entry);
258 MemoryContextSwitchTo(oldcontext);
259
260 return ret;
261}
262
263/*
264 * Initialize or attach a named DSA.
265 *
266 * This routine returns a pointer to the DSA. A new LWLock tranche ID will be
267 * generated if needed. Note that the lock tranche will be registered with the
268 * provided name. Also note that this should be called at most once for a
269 * given DSA in each backend.
270 */
271dsa_area *
272 GetNamedDSA(const char *name, bool *found)
273{
274 DSMRegistryEntry *entry;
275 MemoryContext oldcontext;
276 dsa_area *ret;
277
278 Assert(found);
279
280 if (!name || *name == '0円')
281 ereport(ERROR,
282 (errmsg("DSA name cannot be empty")));
283
284 if (strlen(name) >= offsetof(DSMRegistryEntry, type))
285 ereport(ERROR,
286 (errmsg("DSA name too long")));
287
288 /* Be sure any local memory allocated by DSM/DSA routines is persistent. */
289 oldcontext = MemoryContextSwitchTo(TopMemoryContext);
290
291 /* Connect to the registry. */
292 init_dsm_registry();
293
294 entry = dshash_find_or_insert(dsm_registry_table, name, found);
295 if (!(*found))
296 {
297 NamedDSAState *state = &entry->dsa;
298
299 entry->type = DSMR_ENTRY_TYPE_DSA;
300
301 /* Initialize the LWLock tranche for the DSA. */
302 state->tranche = LWLockNewTrancheId(name);
303
304 /* Initialize the DSA. */
305 ret = dsa_create(state->tranche);
306 dsa_pin(ret);
307 dsa_pin_mapping(ret);
308
309 /* Store handle for other backends to use. */
310 state->handle = dsa_get_handle(ret);
311 }
312 else if (entry->type != DSMR_ENTRY_TYPE_DSA)
313 ereport(ERROR,
314 (errmsg("requested DSA does not match type of existing entry")));
315 else
316 {
317 NamedDSAState *state = &entry->dsa;
318
319 if (dsa_is_attached(state->handle))
320 ereport(ERROR,
321 (errmsg("requested DSA already attached to current process")));
322
323 /* Attach to existing DSA. */
324 ret = dsa_attach(state->handle);
325 dsa_pin_mapping(ret);
326 }
327
328 dshash_release_lock(dsm_registry_table, entry);
329 MemoryContextSwitchTo(oldcontext);
330
331 return ret;
332}
333
334/*
335 * Initialize or attach a named dshash table.
336 *
337 * This routine returns the address of the table. The tranche_id member of
338 * params is ignored; a new LWLock tranche ID will be generated if needed.
339 * Note that the lock tranche will be registered with the provided name. Also
340 * note that this should be called at most once for a given table in each
341 * backend.
342 */
343dshash_table *
344 GetNamedDSHash(const char *name, const dshash_parameters *params, bool *found)
345{
346 DSMRegistryEntry *entry;
347 MemoryContext oldcontext;
348 dshash_table *ret;
349
350 Assert(params);
351 Assert(found);
352
353 if (!name || *name == '0円')
354 ereport(ERROR,
355 (errmsg("DSHash name cannot be empty")));
356
357 if (strlen(name) >= offsetof(DSMRegistryEntry, type))
358 ereport(ERROR,
359 (errmsg("DSHash name too long")));
360
361 /* Be sure any local memory allocated by DSM/DSA routines is persistent. */
362 oldcontext = MemoryContextSwitchTo(TopMemoryContext);
363
364 /* Connect to the registry. */
365 init_dsm_registry();
366
367 entry = dshash_find_or_insert(dsm_registry_table, name, found);
368 if (!(*found))
369 {
370 NamedDSHState *dsh_state = &entry->dsh;
371 dshash_parameters params_copy;
372 dsa_area *dsa;
373
374 entry->type = DSMR_ENTRY_TYPE_DSH;
375
376 /* Initialize the LWLock tranche for the hash table. */
377 dsh_state->tranche = LWLockNewTrancheId(name);
378
379 /* Initialize the DSA for the hash table. */
380 dsa = dsa_create(dsh_state->tranche);
381 dsa_pin(dsa);
382 dsa_pin_mapping(dsa);
383
384 /* Initialize the dshash table. */
385 memcpy(&params_copy, params, sizeof(dshash_parameters));
386 params_copy.tranche_id = dsh_state->tranche;
387 ret = dshash_create(dsa, &params_copy, NULL);
388
389 /* Store handles for other backends to use. */
390 dsh_state->dsa_handle = dsa_get_handle(dsa);
391 dsh_state->dsh_handle = dshash_get_hash_table_handle(ret);
392 }
393 else if (entry->type != DSMR_ENTRY_TYPE_DSH)
394 ereport(ERROR,
395 (errmsg("requested DSHash does not match type of existing entry")));
396 else
397 {
398 NamedDSHState *dsh_state = &entry->dsh;
399 dsa_area *dsa;
400
401 /* XXX: Should we verify params matches what table was created with? */
402
403 if (dsa_is_attached(dsh_state->dsa_handle))
404 ereport(ERROR,
405 (errmsg("requested DSHash already attached to current process")));
406
407 /* Attach to existing DSA for the hash table. */
408 dsa = dsa_attach(dsh_state->dsa_handle);
409 dsa_pin_mapping(dsa);
410
411 /* Attach to existing dshash table. */
412 ret = dshash_attach(dsa, params, dsh_state->dsh_handle, NULL);
413 }
414
415 dshash_release_lock(dsm_registry_table, entry);
416 MemoryContextSwitchTo(oldcontext);
417
418 return ret;
419}
420
421Datum
422 pg_get_dsm_registry_allocations(PG_FUNCTION_ARGS)
423{
424 ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
425 DSMRegistryEntry *entry;
426 MemoryContext oldcontext;
427 dshash_seq_status status;
428
429 InitMaterializedSRF(fcinfo, MAT_SRF_USE_EXPECTED_DESC);
430
431 /* Be sure any local memory allocated by DSM/DSA routines is persistent. */
432 oldcontext = MemoryContextSwitchTo(TopMemoryContext);
433 init_dsm_registry();
434 MemoryContextSwitchTo(oldcontext);
435
436 dshash_seq_init(&status, dsm_registry_table, false);
437 while ((entry = dshash_seq_next(&status)) != NULL)
438 {
439 Datum vals[3];
440 bool nulls[3] = {0};
441
442 vals[0] = CStringGetTextDatum(entry->name);
443 vals[1] = CStringGetTextDatum(DSMREntryTypeNames[entry->type]);
444
445 /*
446 * Since we can't know the size of DSA/dshash entries without first
447 * attaching to them, return NULL for those.
448 */
449 if (entry->type == DSMR_ENTRY_TYPE_DSM)
450 vals[2] = Int64GetDatum(entry->dsm.size);
451 else
452 nulls[2] = true;
453
454 tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, vals, nulls);
455 }
456 dshash_seq_term(&status);
457
458 return (Datum) 0;
459}
#define CStringGetTextDatum(s)
Definition: builtins.h:97
#define MAXALIGN(LEN)
Definition: c.h:810
size_t Size
Definition: c.h:610
dsa_area * dsa_attach(dsa_handle handle)
Definition: dsa.c:510
void dsa_pin_mapping(dsa_area *area)
Definition: dsa.c:650
dsa_handle dsa_get_handle(dsa_area *area)
Definition: dsa.c:498
bool dsa_is_attached(dsa_handle handle)
Definition: dsa.c:540
void dsa_pin(dsa_area *area)
Definition: dsa.c:990
#define dsa_create(tranche_id)
Definition: dsa.h:117
dsm_handle dsa_handle
Definition: dsa.h:136
#define DSA_HANDLE_INVALID
Definition: dsa.h:139
void dshash_strcpy(void *dest, const void *src, size_t size, void *arg)
Definition: dshash.c:622
void dshash_release_lock(dshash_table *hash_table, void *entry)
Definition: dshash.c:558
void dshash_seq_init(dshash_seq_status *status, dshash_table *hash_table, bool exclusive)
Definition: dshash.c:638
dshash_hash dshash_strhash(const void *v, size_t size, void *arg)
Definition: dshash.c:611
dshash_table_handle dshash_get_hash_table_handle(dshash_table *hash_table)
Definition: dshash.c:367
dshash_table * dshash_attach(dsa_area *area, const dshash_parameters *params, dshash_table_handle handle, void *arg)
Definition: dshash.c:270
void dshash_seq_term(dshash_seq_status *status)
Definition: dshash.c:747
int dshash_strcmp(const void *a, const void *b, size_t size, void *arg)
Definition: dshash.c:599
void * dshash_find_or_insert(dshash_table *hash_table, const void *key, bool *found)
Definition: dshash.c:433
void * dshash_seq_next(dshash_seq_status *status)
Definition: dshash.c:657
dshash_table * dshash_create(dsa_area *area, const dshash_parameters *params, void *arg)
Definition: dshash.c:206
#define DSHASH_HANDLE_INVALID
Definition: dshash.h:27
dsa_pointer dshash_table_handle
Definition: dshash.h:24
dsm_handle dsm_segment_handle(dsm_segment *seg)
Definition: dsm.c:1123
void dsm_pin_mapping(dsm_segment *seg)
Definition: dsm.c:915
void dsm_pin_segment(dsm_segment *seg)
Definition: dsm.c:955
void * dsm_segment_address(dsm_segment *seg)
Definition: dsm.c:1095
dsm_segment * dsm_create(Size size, int flags)
Definition: dsm.c:516
dsm_segment * dsm_attach(dsm_handle h)
Definition: dsm.c:665
dsm_segment * dsm_find_mapping(dsm_handle handle)
Definition: dsm.c:1076
uint32 dsm_handle
Definition: dsm_impl.h:55
dsa_area * GetNamedDSA(const char *name, bool *found)
Definition: dsm_registry.c:272
Datum pg_get_dsm_registry_allocations(PG_FUNCTION_ARGS)
Definition: dsm_registry.c:422
DSMREntryType
Definition: dsm_registry.c:79
@ DSMR_ENTRY_TYPE_DSM
Definition: dsm_registry.c:80
@ DSMR_ENTRY_TYPE_DSA
Definition: dsm_registry.c:81
@ DSMR_ENTRY_TYPE_DSH
Definition: dsm_registry.c:82
struct NamedDSMState NamedDSMState
void DSMRegistryShmemInit(void)
Definition: dsm_registry.c:123
static void init_dsm_registry(void)
Definition: dsm_registry.c:145
static const char *const DSMREntryTypeNames[]
Definition: dsm_registry.c:85
struct NamedDSAState NamedDSAState
static DSMRegistryCtxStruct * DSMRegistryCtx
Definition: dsm_registry.c:57
struct DSMRegistryEntry DSMRegistryEntry
static dshash_table * dsm_registry_table
Definition: dsm_registry.c:114
dshash_table * GetNamedDSHash(const char *name, const dshash_parameters *params, bool *found)
Definition: dsm_registry.c:344
static dsa_area * dsm_registry_dsa
Definition: dsm_registry.c:113
static const dshash_parameters dsh_params
Definition: dsm_registry.c:104
struct DSMRegistryCtxStruct DSMRegistryCtxStruct
struct NamedDSHState NamedDSHState
Size DSMRegistryShmemSize(void)
Definition: dsm_registry.c:117
void * GetNamedDSMSegment(const char *name, size_t size, void(*init_callback)(void *ptr), bool *found)
Definition: dsm_registry.c:185
int errmsg(const char *fmt,...)
Definition: elog.c:1071
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:226
#define ereport(elevel,...)
Definition: elog.h:150
#define PG_FUNCTION_ARGS
Definition: fmgr.h:193
void InitMaterializedSRF(FunctionCallInfo fcinfo, bits32 flags)
Definition: funcapi.c:76
#define MAT_SRF_USE_EXPECTED_DESC
Definition: funcapi.h:296
Assert(PointerIsAligned(start, uint64))
bool LWLockAcquire(LWLock *lock, LWLockMode mode)
Definition: lwlock.c:1174
int LWLockNewTrancheId(const char *name)
Definition: lwlock.c:596
void LWLockRelease(LWLock *lock)
Definition: lwlock.c:1894
@ LW_EXCLUSIVE
Definition: lwlock.h:112
MemoryContext TopMemoryContext
Definition: mcxt.c:166
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:124
#define NAMEDATALEN
static Datum Int64GetDatum(int64 X)
Definition: postgres.h:403
uint64_t Datum
Definition: postgres.h:70
void * ShmemInitStruct(const char *name, Size size, bool *foundPtr)
Definition: shmem.c:387
dsa_handle dsah
Definition: dsm_registry.c:53
dshash_table_handle dshh
Definition: dsm_registry.c:54
NamedDSMState dsm
Definition: dsm_registry.c:98
char name[NAMEDATALEN]
Definition: dsm_registry.c:94
NamedDSAState dsa
Definition: dsm_registry.c:99
NamedDSHState dsh
Definition: dsm_registry.c:100
DSMREntryType type
Definition: dsm_registry.c:95
dsa_handle handle
Definition: dsm_registry.c:67
dsa_handle dsa_handle
Definition: dsm_registry.c:73
dshash_table_handle dsh_handle
Definition: dsm_registry.c:74
size_t size
Definition: dsm_registry.c:62
dsm_handle handle
Definition: dsm_registry.c:61
TupleDesc setDesc
Definition: execnodes.h:364
Tuplestorestate * setResult
Definition: execnodes.h:363
Definition: dsa.c:348
int tranche_id
Definition: dshash.h:61
Definition: dsm.c:67
Definition: regguts.h:323
void tuplestore_putvalues(Tuplestorestate *state, TupleDesc tdesc, const Datum *values, const bool *isnull)
Definition: tuplestore.c:784
const char * type
const char * name

AltStyle によって変換されたページ (->オリジナル) /