PostgreSQL Source Code git master
Loading...
Searching...
No Matches
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-2026, 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"
46#include "storage/lwlock.h"
47#include "storage/shmem.h"
48#include "storage/subsystems.h"
49#include "utils/builtins.h"
50#include "utils/memutils.h"
51#include "utils/tuplestore.h"
52
58
60
61static void DSMRegistryShmemRequest(void *arg);
62static void DSMRegistryShmemInit(void *arg);
63
68
74
80
87
94
95static const char *const DSMREntryTypeNames[] =
96{
97 [DSMR_ENTRY_TYPE_DSM] = "segment",
98 [DSMR_ENTRY_TYPE_DSA] = "area",
99 [DSMR_ENTRY_TYPE_DSH] = "hash",
100};
101
113
122
125
126static void
128{
129 ShmemRequestStruct(.name = "DSM Registry Data",
130 .size = sizeof(DSMRegistryCtxStruct),
131 .ptr = (void **) &DSMRegistryCtx,
132 );
133}
134
135static void
141
142/*
143 * Initialize or attach to the dynamic shared hash table that stores the DSM
144 * registry entries, if not already done. This must be called before accessing
145 * the table.
146 */
147static void
149{
150 /* Quick exit if we already did this. */
152 return;
153
154 /* Otherwise, use a lock to ensure only one process creates the table. */
156
158 {
159 /* Initialize dynamic shared hash table for registry. */
162
165
166 /* Store handles in shared memory for other backends to use. */
169 }
170 else
171 {
172 /* Attach to existing dynamic shared hash table. */
177 }
178
180}
181
182/*
183 * Initialize or attach a named DSM segment.
184 *
185 * This routine returns the address of the segment. init_callback is called to
186 * initialize the segment when it is first created. 'arg' is passed through to
187 * the initialization callback function.
188 */
189void *
190GetNamedDSMSegment(const char *name, size_t size,
191 void (*init_callback) (void *ptr, void *arg),
192 bool *found, void *arg)
193{
194 DSMRegistryEntry *entry;
195 MemoryContext oldcontext;
196 void *ret;
198 dsm_segment *seg;
199
200 Assert(found);
201
202 if (!name || *name == '\0')
204 (errmsg("DSM segment name cannot be empty")));
205
208 (errmsg("DSM segment name too long")));
209
210 if (size == 0)
212 (errmsg("DSM segment size must be nonzero")));
213
214 /* Be sure any local memory allocated by DSM/DSA routines is persistent. */
216
217 /* Connect to the registry. */
219
221 state = &entry->dsm;
222 if (!(*found))
223 {
224 entry->type = DSMR_ENTRY_TYPE_DSM;
225 state->handle = DSM_HANDLE_INVALID;
226 state->size = size;
227 }
228 else if (entry->type != DSMR_ENTRY_TYPE_DSM)
230 (errmsg("requested DSM segment does not match type of existing entry")));
231 else if (state->size != size)
233 (errmsg("requested DSM segment size does not match size of existing segment")));
234
235 if (state->handle == DSM_HANDLE_INVALID)
236 {
237 *found = false;
238
239 /* Initialize the segment. */
240 seg = dsm_create(size, 0);
241
242 if (init_callback)
243 (*init_callback) (dsm_segment_address(seg), arg);
244
245 dsm_pin_segment(seg);
246 dsm_pin_mapping(seg);
247 state->handle = dsm_segment_handle(seg);
248 }
249 else
250 {
251 /* If the existing segment is not already attached, attach it now. */
252 seg = dsm_find_mapping(state->handle);
253 if (seg == NULL)
254 {
255 seg = dsm_attach(state->handle);
256 if (seg == NULL)
257 elog(ERROR, "could not map dynamic shared memory segment");
258
259 dsm_pin_mapping(seg);
260 }
261 }
262
263 ret = dsm_segment_address(seg);
265 MemoryContextSwitchTo(oldcontext);
266
267 return ret;
268}
269
270/*
271 * Initialize or attach a named DSA.
272 *
273 * This routine returns a pointer to the DSA. A new LWLock tranche ID will be
274 * generated if needed. Note that the lock tranche will be registered with the
275 * provided name. Also note that this should be called at most once for a
276 * given DSA in each backend.
277 */
278dsa_area *
279GetNamedDSA(const char *name, bool *found)
280{
281 DSMRegistryEntry *entry;
282 MemoryContext oldcontext;
283 dsa_area *ret;
285
286 Assert(found);
287
288 if (!name || *name == '\0')
290 (errmsg("DSA name cannot be empty")));
291
294 (errmsg("DSA name too long")));
295
296 /* Be sure any local memory allocated by DSM/DSA routines is persistent. */
298
299 /* Connect to the registry. */
301
303 state = &entry->dsa;
304 if (!(*found))
305 {
306 entry->type = DSMR_ENTRY_TYPE_DSA;
307 state->handle = DSA_HANDLE_INVALID;
308 state->tranche = -1;
309 }
310 else if (entry->type != DSMR_ENTRY_TYPE_DSA)
312 (errmsg("requested DSA does not match type of existing entry")));
313
314 if (state->tranche == -1)
315 {
316 *found = false;
317
318 /* Initialize the LWLock tranche for the DSA. */
319 state->tranche = LWLockNewTrancheId(name);
320 }
321
322 if (state->handle == DSA_HANDLE_INVALID)
323 {
324 *found = false;
325
326 /* Initialize the DSA. */
327 ret = dsa_create(state->tranche);
328 dsa_pin(ret);
329 dsa_pin_mapping(ret);
330
331 /* Store handle for other backends to use. */
332 state->handle = dsa_get_handle(ret);
333 }
334 else if (dsa_is_attached(state->handle))
336 (errmsg("requested DSA already attached to current process")));
337 else
338 {
339 /* Attach to existing DSA. */
340 ret = dsa_attach(state->handle);
341 dsa_pin_mapping(ret);
342 }
343
345 MemoryContextSwitchTo(oldcontext);
346
347 return ret;
348}
349
350/*
351 * Initialize or attach a named dshash table.
352 *
353 * This routine returns the address of the table. The tranche_id member of
354 * params is ignored; a new LWLock tranche ID will be generated if needed.
355 * Note that the lock tranche will be registered with the provided name. Also
356 * note that this should be called at most once for a given table in each
357 * backend.
358 */
360GetNamedDSHash(const char *name, const dshash_parameters *params, bool *found)
361{
362 DSMRegistryEntry *entry;
363 MemoryContext oldcontext;
364 dshash_table *ret;
366
367 Assert(params);
368 Assert(found);
369
370 if (!name || *name == '\0')
372 (errmsg("DSHash name cannot be empty")));
373
376 (errmsg("DSHash name too long")));
377
378 /* Be sure any local memory allocated by DSM/DSA routines is persistent. */
380
381 /* Connect to the registry. */
383
385 dsh_state = &entry->dsh;
386 if (!(*found))
387 {
388 entry->type = DSMR_ENTRY_TYPE_DSH;
389 dsh_state->dsa_handle = DSA_HANDLE_INVALID;
390 dsh_state->dsh_handle = DSHASH_HANDLE_INVALID;
391 dsh_state->tranche = -1;
392 }
393 else if (entry->type != DSMR_ENTRY_TYPE_DSH)
395 (errmsg("requested DSHash does not match type of existing entry")));
396
397 if (dsh_state->tranche == -1)
398 {
399 *found = false;
400
401 /* Initialize the LWLock tranche for the hash table. */
403 }
404
405 if (dsh_state->dsa_handle == DSA_HANDLE_INVALID)
406 {
408 dsa_area *dsa;
409
410 *found = false;
411
412 /* Initialize the DSA for the hash table. */
413 dsa = dsa_create(dsh_state->tranche);
414
415 /* Initialize the dshash table. */
416 memcpy(&params_copy, params, sizeof(dshash_parameters));
417 params_copy.tranche_id = dsh_state->tranche;
418 ret = dshash_create(dsa, &params_copy, NULL);
419
420 dsa_pin(dsa);
421 dsa_pin_mapping(dsa);
422
423 /* Store handles for other backends to use. */
424 dsh_state->dsa_handle = dsa_get_handle(dsa);
425 dsh_state->dsh_handle = dshash_get_hash_table_handle(ret);
426 }
427 else if (dsa_is_attached(dsh_state->dsa_handle))
429 (errmsg("requested DSHash already attached to current process")));
430 else
431 {
432 dsa_area *dsa;
433
434 /* XXX: Should we verify params matches what table was created with? */
435
436 /* Attach to existing DSA for the hash table. */
437 dsa = dsa_attach(dsh_state->dsa_handle);
438 dsa_pin_mapping(dsa);
439
440 /* Attach to existing dshash table. */
441 ret = dshash_attach(dsa, params, dsh_state->dsh_handle, NULL);
442 }
443
445 MemoryContextSwitchTo(oldcontext);
446
447 return ret;
448}
449
450Datum
452{
453 ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
454 DSMRegistryEntry *entry;
455 MemoryContext oldcontext;
456 dshash_seq_status status;
457
459
460 /* Be sure any local memory allocated by DSM/DSA routines is persistent. */
463 MemoryContextSwitchTo(oldcontext);
464
465 dshash_seq_init(&status, dsm_registry_table, false);
466 while ((entry = dshash_seq_next(&status)) != NULL)
467 {
468 Datum vals[3];
469 bool nulls[3] = {0};
470
471 vals[0] = CStringGetTextDatum(entry->name);
473
474 /* Be careful to only return the sizes of initialized entries. */
475 if (entry->type == DSMR_ENTRY_TYPE_DSM &&
476 entry->dsm.handle != DSM_HANDLE_INVALID)
477 vals[2] = Int64GetDatum(entry->dsm.size);
478 else if (entry->type == DSMR_ENTRY_TYPE_DSA &&
479 entry->dsa.handle != DSA_HANDLE_INVALID)
481 else if (entry->type == DSMR_ENTRY_TYPE_DSH &&
484 else
485 nulls[2] = true;
486
487 tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, vals, nulls);
488 }
489 dshash_seq_term(&status);
490
491 return (Datum) 0;
492}
#define CStringGetTextDatum(s)
Definition builtins.h:98
#define Assert(condition)
Definition c.h:943
memcpy(sums, checksumBaseOffsets, sizeof(checksumBaseOffsets))
dsa_area * dsa_attach(dsa_handle handle)
Definition dsa.c:510
size_t dsa_get_total_size_from_handle(dsa_handle handle)
Definition dsa.c:1058
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:643
void dshash_release_lock(dshash_table *hash_table, void *entry)
Definition dshash.c:579
void dshash_seq_init(dshash_seq_status *status, dshash_table *hash_table, bool exclusive)
Definition dshash.c:659
dshash_hash dshash_strhash(const void *v, size_t size, void *arg)
Definition dshash.c:632
dshash_table_handle dshash_get_hash_table_handle(dshash_table *hash_table)
Definition dshash.c:371
dshash_table * dshash_attach(dsa_area *area, const dshash_parameters *params, dshash_table_handle handle, void *arg)
Definition dshash.c:274
void dshash_seq_term(dshash_seq_status *status)
Definition dshash.c:768
int dshash_strcmp(const void *a, const void *b, size_t size, void *arg)
Definition dshash.c:620
void * dshash_seq_next(dshash_seq_status *status)
Definition dshash.c:678
dshash_table * dshash_create(dsa_area *area, const dshash_parameters *params, void *arg)
Definition dshash.c:210
#define DSHASH_HANDLE_INVALID
Definition dshash.h:27
dsa_pointer dshash_table_handle
Definition dshash.h:24
#define dshash_find_or_insert(hash_table, key, found)
Definition dshash.h:109
dsm_handle dsm_segment_handle(dsm_segment *seg)
Definition dsm.c:1131
void dsm_pin_mapping(dsm_segment *seg)
Definition dsm.c:923
void dsm_pin_segment(dsm_segment *seg)
Definition dsm.c:963
void * dsm_segment_address(dsm_segment *seg)
Definition dsm.c:1103
dsm_segment * dsm_create(Size size, int flags)
Definition dsm.c:524
dsm_segment * dsm_attach(dsm_handle h)
Definition dsm.c:673
dsm_segment * dsm_find_mapping(dsm_handle handle)
Definition dsm.c:1084
uint32 dsm_handle
Definition dsm_impl.h:55
#define DSM_HANDLE_INVALID
Definition dsm_impl.h:58
dsa_area * GetNamedDSA(const char *name, bool *found)
void * GetNamedDSMSegment(const char *name, size_t size, void(*init_callback)(void *ptr, void *arg), bool *found, void *arg)
Datum pg_get_dsm_registry_allocations(PG_FUNCTION_ARGS)
DSMREntryType
@ DSMR_ENTRY_TYPE_DSM
@ DSMR_ENTRY_TYPE_DSA
@ DSMR_ENTRY_TYPE_DSH
static void init_dsm_registry(void)
static void DSMRegistryShmemRequest(void *arg)
static const char *const DSMREntryTypeNames[]
static DSMRegistryCtxStruct * DSMRegistryCtx
static dshash_table * dsm_registry_table
dshash_table * GetNamedDSHash(const char *name, const dshash_parameters *params, bool *found)
static dsa_area * dsm_registry_dsa
static const dshash_parameters dsh_params
const ShmemCallbacks DSMRegistryShmemCallbacks
static void DSMRegistryShmemInit(void *arg)
Datum arg
Definition elog.c:1323
#define ERROR
Definition elog.h:40
#define elog(elevel,...)
Definition elog.h:228
#define ereport(elevel,...)
Definition elog.h:152
#define PG_FUNCTION_ARGS
Definition fmgr.h:193
void InitMaterializedSRF(FunctionCallInfo fcinfo, uint32 flags)
Definition funcapi.c:76
#define MAT_SRF_USE_EXPECTED_DESC
Definition funcapi.h:296
bool LWLockAcquire(LWLock *lock, LWLockMode mode)
Definition lwlock.c:1150
int LWLockNewTrancheId(const char *name)
Definition lwlock.c:562
void LWLockRelease(LWLock *lock)
Definition lwlock.c:1767
@ LW_EXCLUSIVE
Definition lwlock.h:104
MemoryContext TopMemoryContext
Definition mcxt.c:166
static char * errmsg
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition palloc.h:124
#define NAMEDATALEN
static Datum Int64GetDatum(int64 X)
Definition postgres.h:426
uint64_t Datum
Definition postgres.h:70
static int fb(int x)
#define ShmemRequestStruct(...)
Definition shmem.h:176
dshash_table_handle dshh
NamedDSMState dsm
char name[NAMEDATALEN]
NamedDSAState dsa
NamedDSHState dsh
DSMREntryType type
dsa_handle handle
dsa_handle dsa_handle
dshash_table_handle dsh_handle
dsm_handle handle
ShmemRequestCallback request_fn
Definition shmem.h:133
void tuplestore_putvalues(Tuplestorestate *state, TupleDesc tdesc, const Datum *values, const bool *isnull)
Definition tuplestore.c:785
const char * type
const char * name