LCOV - code coverage report
Current view: top level - src/backend/storage/ipc - dsm_registry.c (source / functions) Hit Total Coverage
Test: PostgreSQL 17devel Lines: 50 56 89.3 %
Date: 2024-05-03 16:11:43 Functions: 4 4 100.0 %
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             :  * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
      19             :  * Portions Copyright (c) 1994, Regents of the University of California
      20             :  *
      21             :  * IDENTIFICATION
      22             :  *    src/backend/storage/ipc/dsm_registry.c
      23             :  *
      24             :  *-------------------------------------------------------------------------
      25             :  */
      26             : 
      27             : #include "postgres.h"
      28             : 
      29             : #include "lib/dshash.h"
      30             : #include "storage/dsm_registry.h"
      31             : #include "storage/lwlock.h"
      32             : #include "storage/shmem.h"
      33             : #include "utils/memutils.h"
      34             : 
      35             : typedef struct DSMRegistryCtxStruct
      36             : {
      37             :     dsa_handle  dsah;
      38             :     dshash_table_handle dshh;
      39             : } DSMRegistryCtxStruct;
      40             : 
      41             : static DSMRegistryCtxStruct *DSMRegistryCtx;
      42             : 
      43             : typedef struct DSMRegistryEntry
      44             : {
      45             :     char        name[64];
      46             :     dsm_handle  handle;
      47             :     size_t      size;
      48             : } DSMRegistryEntry;
      49             : 
      50             : static const dshash_parameters dsh_params = {
      51             :     offsetof(DSMRegistryEntry, handle),
      52             :     sizeof(DSMRegistryEntry),
      53             :     dshash_strcmp,
      54             :     dshash_strhash,
      55             :     dshash_strcpy,
      56             :     LWTRANCHE_DSM_REGISTRY_HASH
      57             : };
      58             : 
      59             : static dsa_area *dsm_registry_dsa;
      60             : static dshash_table *dsm_registry_table;
      61             : 
      62             : Size
      63        5066 : DSMRegistryShmemSize(void)
      64             : {
      65        5066 :     return MAXALIGN(sizeof(DSMRegistryCtxStruct));
      66             : }
      67             : 
      68             : void
      69        1768 : DSMRegistryShmemInit(void)
      70             : {
      71             :     bool        found;
      72             : 
      73        1768 :     DSMRegistryCtx = (DSMRegistryCtxStruct *)
      74        1768 :         ShmemInitStruct("DSM Registry Data",
      75             :                         DSMRegistryShmemSize(),
      76             :                         &found);
      77             : 
      78        1768 :     if (!found)
      79             :     {
      80        1768 :         DSMRegistryCtx->dsah = DSA_HANDLE_INVALID;
      81        1768 :         DSMRegistryCtx->dshh = DSHASH_HANDLE_INVALID;
      82             :     }
      83        1768 : }
      84             : 
      85             : /*
      86             :  * Initialize or attach to the dynamic shared hash table that stores the DSM
      87             :  * registry entries, if not already done.  This must be called before accessing
      88             :  * the table.
      89             :  */
      90             : static void
      91          12 : init_dsm_registry(void)
      92             : {
      93             :     /* Quick exit if we already did this. */
      94          12 :     if (dsm_registry_table)
      95           0 :         return;
      96             : 
      97             :     /* Otherwise, use a lock to ensure only one process creates the table. */
      98          12 :     LWLockAcquire(DSMRegistryLock, LW_EXCLUSIVE);
      99             : 
     100          12 :     if (DSMRegistryCtx->dshh == DSHASH_HANDLE_INVALID)
     101             :     {
     102             :         /* Initialize dynamic shared hash table for registry. */
     103           6 :         dsm_registry_dsa = dsa_create(LWTRANCHE_DSM_REGISTRY_DSA);
     104           6 :         dsa_pin(dsm_registry_dsa);
     105           6 :         dsa_pin_mapping(dsm_registry_dsa);
     106           6 :         dsm_registry_table = dshash_create(dsm_registry_dsa, &dsh_params, NULL);
     107             : 
     108             :         /* Store handles in shared memory for other backends to use. */
     109           6 :         DSMRegistryCtx->dsah = dsa_get_handle(dsm_registry_dsa);
     110           6 :         DSMRegistryCtx->dshh = dshash_get_hash_table_handle(dsm_registry_table);
     111             :     }
     112             :     else
     113             :     {
     114             :         /* Attach to existing dynamic shared hash table. */
     115           6 :         dsm_registry_dsa = dsa_attach(DSMRegistryCtx->dsah);
     116           6 :         dsa_pin_mapping(dsm_registry_dsa);
     117           6 :         dsm_registry_table = dshash_attach(dsm_registry_dsa, &dsh_params,
     118           6 :                                            DSMRegistryCtx->dshh, NULL);
     119             :     }
     120             : 
     121          12 :     LWLockRelease(DSMRegistryLock);
     122             : }
     123             : 
     124             : /*
     125             :  * Initialize or attach a named DSM segment.
     126             :  *
     127             :  * This routine returns the address of the segment.  init_callback is called to
     128             :  * initialize the segment when it is first created.
     129             :  */
     130             : void *
     131          12 : GetNamedDSMSegment(const char *name, size_t size,
     132             :                    void (*init_callback) (void *ptr), bool *found)
     133             : {
     134             :     DSMRegistryEntry *entry;
     135             :     MemoryContext oldcontext;
     136             :     void       *ret;
     137             : 
     138             :     Assert(found);
     139             : 
     140          12 :     if (!name || *name == '\0')
     141           0 :         ereport(ERROR,
     142             :                 (errmsg("DSM segment name cannot be empty")));
     143             : 
     144          12 :     if (strlen(name) >= offsetof(DSMRegistryEntry, handle))
     145           0 :         ereport(ERROR,
     146             :                 (errmsg("DSM segment name too long")));
     147             : 
     148          12 :     if (size == 0)
     149           0 :         ereport(ERROR,
     150             :                 (errmsg("DSM segment size must be nonzero")));
     151             : 
     152             :     /* Be sure any local memory allocated by DSM/DSA routines is persistent. */
     153          12 :     oldcontext = MemoryContextSwitchTo(TopMemoryContext);
     154             : 
     155             :     /* Connect to the registry. */
     156          12 :     init_dsm_registry();
     157             : 
     158          12 :     entry = dshash_find_or_insert(dsm_registry_table, name, found);
     159          12 :     if (!(*found))
     160             :     {
     161             :         /* Initialize the segment. */
     162           6 :         dsm_segment *seg = dsm_create(size, 0);
     163             : 
     164           6 :         dsm_pin_segment(seg);
     165           6 :         dsm_pin_mapping(seg);
     166           6 :         entry->handle = dsm_segment_handle(seg);
     167           6 :         entry->size = size;
     168           6 :         ret = dsm_segment_address(seg);
     169             : 
     170           6 :         if (init_callback)
     171           6 :             (*init_callback) (ret);
     172             :     }
     173           6 :     else if (entry->size != size)
     174             :     {
     175           0 :         ereport(ERROR,
     176             :                 (errmsg("requested DSM segment size does not match size of "
     177             :                         "existing segment")));
     178             :     }
     179             :     else
     180             :     {
     181           6 :         dsm_segment *seg = dsm_find_mapping(entry->handle);
     182             : 
     183             :         /* If the existing segment is not already attached, attach it now. */
     184           6 :         if (seg == NULL)
     185             :         {
     186           6 :             seg = dsm_attach(entry->handle);
     187           6 :             if (seg == NULL)
     188           0 :                 elog(ERROR, "could not map dynamic shared memory segment");
     189             : 
     190           6 :             dsm_pin_mapping(seg);
     191             :         }
     192             : 
     193           6 :         ret = dsm_segment_address(seg);
     194             :     }
     195             : 
     196          12 :     dshash_release_lock(dsm_registry_table, entry);
     197          12 :     MemoryContextSwitchTo(oldcontext);
     198             : 
     199          12 :     return ret;
     200             : }

Generated by: LCOV version 1.14