LCOV - code coverage report
Current view: top level - src/backend/storage/ipc - dsm_registry.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 90.3 % 154 139
Test Date: 2026-02-28 02:14:46 Functions: 100.0 % 7 7
Legend: Lines:     hit not hit

            Line data    Source code
       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"
      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              : 
     116              : Size
     117         3297 : DSMRegistryShmemSize(void)
     118              : {
     119         3297 :     return MAXALIGN(sizeof(DSMRegistryCtxStruct));
     120              : }
     121              : 
     122              : void
     123         1150 : DSMRegistryShmemInit(void)
     124              : {
     125              :     bool        found;
     126              : 
     127         1150 :     DSMRegistryCtx = (DSMRegistryCtxStruct *)
     128         1150 :         ShmemInitStruct("DSM Registry Data",
     129              :                         DSMRegistryShmemSize(),
     130              :                         &found);
     131              : 
     132         1150 :     if (!found)
     133              :     {
     134         1150 :         DSMRegistryCtx->dsah = DSA_HANDLE_INVALID;
     135         1150 :         DSMRegistryCtx->dshh = DSHASH_HANDLE_INVALID;
     136              :     }
     137         1150 : }
     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              :  */
     144              : static void
     145          135 : init_dsm_registry(void)
     146              : {
     147              :     /* Quick exit if we already did this. */
     148          135 :     if (dsm_registry_table)
     149            8 :         return;
     150              : 
     151              :     /* Otherwise, use a lock to ensure only one process creates the table. */
     152          127 :     LWLockAcquire(DSMRegistryLock, LW_EXCLUSIVE);
     153              : 
     154          127 :     if (DSMRegistryCtx->dshh == DSHASH_HANDLE_INVALID)
     155              :     {
     156              :         /* Initialize dynamic shared hash table for registry. */
     157           15 :         dsm_registry_dsa = dsa_create(LWTRANCHE_DSM_REGISTRY_DSA);
     158           15 :         dsm_registry_table = dshash_create(dsm_registry_dsa, &dsh_params, NULL);
     159              : 
     160           15 :         dsa_pin(dsm_registry_dsa);
     161           15 :         dsa_pin_mapping(dsm_registry_dsa);
     162              : 
     163              :         /* Store handles in shared memory for other backends to use. */
     164           15 :         DSMRegistryCtx->dsah = dsa_get_handle(dsm_registry_dsa);
     165           15 :         DSMRegistryCtx->dshh = dshash_get_hash_table_handle(dsm_registry_table);
     166              :     }
     167              :     else
     168              :     {
     169              :         /* Attach to existing dynamic shared hash table. */
     170          112 :         dsm_registry_dsa = dsa_attach(DSMRegistryCtx->dsah);
     171          112 :         dsa_pin_mapping(dsm_registry_dsa);
     172          112 :         dsm_registry_table = dshash_attach(dsm_registry_dsa, &dsh_params,
     173          112 :                                            DSMRegistryCtx->dshh, NULL);
     174              :     }
     175              : 
     176          127 :     LWLockRelease(DSMRegistryLock);
     177              : }
     178              : 
     179              : /*
     180              :  * Initialize or attach a named DSM segment.
     181              :  *
     182              :  * This routine returns the address of the segment.  init_callback is called to
     183              :  * initialize the segment when it is first created.  'arg' is passed through to
     184              :  * the initialization callback function.
     185              :  */
     186              : void *
     187          117 : GetNamedDSMSegment(const char *name, size_t size,
     188              :                    void (*init_callback) (void *ptr, void *arg),
     189              :                    bool *found, void *arg)
     190              : {
     191              :     DSMRegistryEntry *entry;
     192              :     MemoryContext oldcontext;
     193              :     void       *ret;
     194              :     NamedDSMState *state;
     195              :     dsm_segment *seg;
     196              : 
     197              :     Assert(found);
     198              : 
     199          117 :     if (!name || *name == '\0')
     200            0 :         ereport(ERROR,
     201              :                 (errmsg("DSM segment name cannot be empty")));
     202              : 
     203          117 :     if (strlen(name) >= offsetof(DSMRegistryEntry, type))
     204            0 :         ereport(ERROR,
     205              :                 (errmsg("DSM segment name too long")));
     206              : 
     207          117 :     if (size == 0)
     208            0 :         ereport(ERROR,
     209              :                 (errmsg("DSM segment size must be nonzero")));
     210              : 
     211              :     /* Be sure any local memory allocated by DSM/DSA routines is persistent. */
     212          117 :     oldcontext = MemoryContextSwitchTo(TopMemoryContext);
     213              : 
     214              :     /* Connect to the registry. */
     215          117 :     init_dsm_registry();
     216              : 
     217          117 :     entry = dshash_find_or_insert(dsm_registry_table, name, found);
     218          117 :     state = &entry->dsm;
     219          117 :     if (!(*found))
     220              :     {
     221           13 :         entry->type = DSMR_ENTRY_TYPE_DSM;
     222           13 :         state->handle = DSM_HANDLE_INVALID;
     223           13 :         state->size = size;
     224              :     }
     225          104 :     else if (entry->type != DSMR_ENTRY_TYPE_DSM)
     226            0 :         ereport(ERROR,
     227              :                 (errmsg("requested DSM segment does not match type of existing entry")));
     228          104 :     else if (state->size != size)
     229            0 :         ereport(ERROR,
     230              :                 (errmsg("requested DSM segment size does not match size of existing segment")));
     231              : 
     232          117 :     if (state->handle == DSM_HANDLE_INVALID)
     233              :     {
     234           13 :         *found = false;
     235              : 
     236              :         /* Initialize the segment. */
     237           13 :         seg = dsm_create(size, 0);
     238              : 
     239           13 :         if (init_callback)
     240           13 :             (*init_callback) (dsm_segment_address(seg), arg);
     241              : 
     242           13 :         dsm_pin_segment(seg);
     243           13 :         dsm_pin_mapping(seg);
     244           13 :         state->handle = dsm_segment_handle(seg);
     245              :     }
     246              :     else
     247              :     {
     248              :         /* If the existing segment is not already attached, attach it now. */
     249          104 :         seg = dsm_find_mapping(state->handle);
     250          104 :         if (seg == NULL)
     251              :         {
     252          101 :             seg = dsm_attach(state->handle);
     253          101 :             if (seg == NULL)
     254            0 :                 elog(ERROR, "could not map dynamic shared memory segment");
     255              : 
     256          101 :             dsm_pin_mapping(seg);
     257              :         }
     258              :     }
     259              : 
     260          117 :     ret = dsm_segment_address(seg);
     261          117 :     dshash_release_lock(dsm_registry_table, entry);
     262          117 :     MemoryContextSwitchTo(oldcontext);
     263              : 
     264          117 :     return ret;
     265              : }
     266              : 
     267              : /*
     268              :  * Initialize or attach a named DSA.
     269              :  *
     270              :  * This routine returns a pointer to the DSA.  A new LWLock tranche ID will be
     271              :  * generated if needed.  Note that the lock tranche will be registered with the
     272              :  * provided name.  Also note that this should be called at most once for a
     273              :  * given DSA in each backend.
     274              :  */
     275              : dsa_area *
     276           14 : GetNamedDSA(const char *name, bool *found)
     277              : {
     278              :     DSMRegistryEntry *entry;
     279              :     MemoryContext oldcontext;
     280              :     dsa_area   *ret;
     281              :     NamedDSAState *state;
     282              : 
     283              :     Assert(found);
     284              : 
     285           14 :     if (!name || *name == '\0')
     286            0 :         ereport(ERROR,
     287              :                 (errmsg("DSA name cannot be empty")));
     288              : 
     289           14 :     if (strlen(name) >= offsetof(DSMRegistryEntry, type))
     290            0 :         ereport(ERROR,
     291              :                 (errmsg("DSA name too long")));
     292              : 
     293              :     /* Be sure any local memory allocated by DSM/DSA routines is persistent. */
     294           14 :     oldcontext = MemoryContextSwitchTo(TopMemoryContext);
     295              : 
     296              :     /* Connect to the registry. */
     297           14 :     init_dsm_registry();
     298              : 
     299           14 :     entry = dshash_find_or_insert(dsm_registry_table, name, found);
     300           14 :     state = &entry->dsa;
     301           14 :     if (!(*found))
     302              :     {
     303            3 :         entry->type = DSMR_ENTRY_TYPE_DSA;
     304            3 :         state->handle = DSA_HANDLE_INVALID;
     305            3 :         state->tranche = -1;
     306              :     }
     307           11 :     else if (entry->type != DSMR_ENTRY_TYPE_DSA)
     308            0 :         ereport(ERROR,
     309              :                 (errmsg("requested DSA does not match type of existing entry")));
     310              : 
     311           14 :     if (state->tranche == -1)
     312              :     {
     313            3 :         *found = false;
     314              : 
     315              :         /* Initialize the LWLock tranche for the DSA. */
     316            3 :         state->tranche = LWLockNewTrancheId(name);
     317              :     }
     318              : 
     319           14 :     if (state->handle == DSA_HANDLE_INVALID)
     320              :     {
     321            3 :         *found = false;
     322              : 
     323              :         /* Initialize the DSA. */
     324            3 :         ret = dsa_create(state->tranche);
     325            3 :         dsa_pin(ret);
     326            3 :         dsa_pin_mapping(ret);
     327              : 
     328              :         /* Store handle for other backends to use. */
     329            3 :         state->handle = dsa_get_handle(ret);
     330              :     }
     331           11 :     else if (dsa_is_attached(state->handle))
     332            0 :         ereport(ERROR,
     333              :                 (errmsg("requested DSA already attached to current process")));
     334              :     else
     335              :     {
     336              :         /* Attach to existing DSA. */
     337           11 :         ret = dsa_attach(state->handle);
     338           11 :         dsa_pin_mapping(ret);
     339              :     }
     340              : 
     341           14 :     dshash_release_lock(dsm_registry_table, entry);
     342           14 :     MemoryContextSwitchTo(oldcontext);
     343              : 
     344           14 :     return ret;
     345              : }
     346              : 
     347              : /*
     348              :  * Initialize or attach a named dshash table.
     349              :  *
     350              :  * This routine returns the address of the table.  The tranche_id member of
     351              :  * params is ignored; a new LWLock tranche ID will be generated if needed.
     352              :  * Note that the lock tranche will be registered with the provided name.  Also
     353              :  * note that this should be called at most once for a given table in each
     354              :  * backend.
     355              :  */
     356              : dshash_table *
     357            2 : GetNamedDSHash(const char *name, const dshash_parameters *params, bool *found)
     358              : {
     359              :     DSMRegistryEntry *entry;
     360              :     MemoryContext oldcontext;
     361              :     dshash_table *ret;
     362              :     NamedDSHState *dsh_state;
     363              : 
     364              :     Assert(params);
     365              :     Assert(found);
     366              : 
     367            2 :     if (!name || *name == '\0')
     368            0 :         ereport(ERROR,
     369              :                 (errmsg("DSHash name cannot be empty")));
     370              : 
     371            2 :     if (strlen(name) >= offsetof(DSMRegistryEntry, type))
     372            0 :         ereport(ERROR,
     373              :                 (errmsg("DSHash name too long")));
     374              : 
     375              :     /* Be sure any local memory allocated by DSM/DSA routines is persistent. */
     376            2 :     oldcontext = MemoryContextSwitchTo(TopMemoryContext);
     377              : 
     378              :     /* Connect to the registry. */
     379            2 :     init_dsm_registry();
     380              : 
     381            2 :     entry = dshash_find_or_insert(dsm_registry_table, name, found);
     382            2 :     dsh_state = &entry->dsh;
     383            2 :     if (!(*found))
     384              :     {
     385            1 :         entry->type = DSMR_ENTRY_TYPE_DSH;
     386            1 :         dsh_state->dsa_handle = DSA_HANDLE_INVALID;
     387            1 :         dsh_state->dsh_handle = DSHASH_HANDLE_INVALID;
     388            1 :         dsh_state->tranche = -1;
     389              :     }
     390            1 :     else if (entry->type != DSMR_ENTRY_TYPE_DSH)
     391            0 :         ereport(ERROR,
     392              :                 (errmsg("requested DSHash does not match type of existing entry")));
     393              : 
     394            2 :     if (dsh_state->tranche == -1)
     395              :     {
     396            1 :         *found = false;
     397              : 
     398              :         /* Initialize the LWLock tranche for the hash table. */
     399            1 :         dsh_state->tranche = LWLockNewTrancheId(name);
     400              :     }
     401              : 
     402            2 :     if (dsh_state->dsa_handle == DSA_HANDLE_INVALID)
     403              :     {
     404              :         dshash_parameters params_copy;
     405              :         dsa_area   *dsa;
     406              : 
     407            1 :         *found = false;
     408              : 
     409              :         /* Initialize the DSA for the hash table. */
     410            1 :         dsa = dsa_create(dsh_state->tranche);
     411              : 
     412              :         /* Initialize the dshash table. */
     413            1 :         memcpy(&params_copy, params, sizeof(dshash_parameters));
     414            1 :         params_copy.tranche_id = dsh_state->tranche;
     415            1 :         ret = dshash_create(dsa, &params_copy, NULL);
     416              : 
     417            1 :         dsa_pin(dsa);
     418            1 :         dsa_pin_mapping(dsa);
     419              : 
     420              :         /* Store handles for other backends to use. */
     421            1 :         dsh_state->dsa_handle = dsa_get_handle(dsa);
     422            1 :         dsh_state->dsh_handle = dshash_get_hash_table_handle(ret);
     423              :     }
     424            1 :     else if (dsa_is_attached(dsh_state->dsa_handle))
     425            0 :         ereport(ERROR,
     426              :                 (errmsg("requested DSHash already attached to current process")));
     427              :     else
     428              :     {
     429              :         dsa_area   *dsa;
     430              : 
     431              :         /* XXX: Should we verify params matches what table was created with? */
     432              : 
     433              :         /* Attach to existing DSA for the hash table. */
     434            1 :         dsa = dsa_attach(dsh_state->dsa_handle);
     435            1 :         dsa_pin_mapping(dsa);
     436              : 
     437              :         /* Attach to existing dshash table. */
     438            1 :         ret = dshash_attach(dsa, params, dsh_state->dsh_handle, NULL);
     439              :     }
     440              : 
     441            2 :     dshash_release_lock(dsm_registry_table, entry);
     442            2 :     MemoryContextSwitchTo(oldcontext);
     443              : 
     444            2 :     return ret;
     445              : }
     446              : 
     447              : Datum
     448            2 : pg_get_dsm_registry_allocations(PG_FUNCTION_ARGS)
     449              : {
     450            2 :     ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
     451              :     DSMRegistryEntry *entry;
     452              :     MemoryContext oldcontext;
     453              :     dshash_seq_status status;
     454              : 
     455            2 :     InitMaterializedSRF(fcinfo, MAT_SRF_USE_EXPECTED_DESC);
     456              : 
     457              :     /* Be sure any local memory allocated by DSM/DSA routines is persistent. */
     458            2 :     oldcontext = MemoryContextSwitchTo(TopMemoryContext);
     459            2 :     init_dsm_registry();
     460            2 :     MemoryContextSwitchTo(oldcontext);
     461              : 
     462            2 :     dshash_seq_init(&status, dsm_registry_table, false);
     463            5 :     while ((entry = dshash_seq_next(&status)) != NULL)
     464              :     {
     465              :         Datum       vals[3];
     466            3 :         bool        nulls[3] = {0};
     467              : 
     468            3 :         vals[0] = CStringGetTextDatum(entry->name);
     469            3 :         vals[1] = CStringGetTextDatum(DSMREntryTypeNames[entry->type]);
     470              : 
     471              :         /* Be careful to only return the sizes of initialized entries. */
     472            3 :         if (entry->type == DSMR_ENTRY_TYPE_DSM &&
     473            1 :             entry->dsm.handle != DSM_HANDLE_INVALID)
     474            1 :             vals[2] = Int64GetDatum(entry->dsm.size);
     475            2 :         else if (entry->type == DSMR_ENTRY_TYPE_DSA &&
     476            1 :                  entry->dsa.handle != DSA_HANDLE_INVALID)
     477            1 :             vals[2] = Int64GetDatum(dsa_get_total_size_from_handle(entry->dsa.handle));
     478            1 :         else if (entry->type == DSMR_ENTRY_TYPE_DSH &&
     479            1 :                  entry->dsh.dsa_handle !=DSA_HANDLE_INVALID)
     480            1 :             vals[2] = Int64GetDatum(dsa_get_total_size_from_handle(entry->dsh.dsa_handle));
     481              :         else
     482            0 :             nulls[2] = true;
     483              : 
     484            3 :         tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, vals, nulls);
     485              :     }
     486            2 :     dshash_seq_term(&status);
     487              : 
     488            2 :     return (Datum) 0;
     489              : }
        

Generated by: LCOV version 2.0-1