LCOV - code coverage report
Current view: top level - src/backend/storage/ipc - shmem.c (source / functions) Hit Total Coverage
Test: PostgreSQL 15devel Lines: 123 145 84.8 %
Date: 2021-12-04 22:09:09 Functions: 12 13 92.3 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * shmem.c
       4             :  *    create shared memory and initialize shared memory data structures.
       5             :  *
       6             :  * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group
       7             :  * Portions Copyright (c) 1994, Regents of the University of California
       8             :  *
       9             :  *
      10             :  * IDENTIFICATION
      11             :  *    src/backend/storage/ipc/shmem.c
      12             :  *
      13             :  *-------------------------------------------------------------------------
      14             :  */
      15             : /*
      16             :  * POSTGRES processes share one or more regions of shared memory.
      17             :  * The shared memory is created by a postmaster and is inherited
      18             :  * by each backend via fork() (or, in some ports, via other OS-specific
      19             :  * methods).  The routines in this file are used for allocating and
      20             :  * binding to shared memory data structures.
      21             :  *
      22             :  * NOTES:
      23             :  *      (a) There are three kinds of shared memory data structures
      24             :  *  available to POSTGRES: fixed-size structures, queues and hash
      25             :  *  tables.  Fixed-size structures contain things like global variables
      26             :  *  for a module and should never be allocated after the shared memory
      27             :  *  initialization phase.  Hash tables have a fixed maximum size, but
      28             :  *  their actual size can vary dynamically.  When entries are added
      29             :  *  to the table, more space is allocated.  Queues link data structures
      30             :  *  that have been allocated either within fixed-size structures or as hash
      31             :  *  buckets.  Each shared data structure has a string name to identify
      32             :  *  it (assigned in the module that declares it).
      33             :  *
      34             :  *      (b) During initialization, each module looks for its
      35             :  *  shared data structures in a hash table called the "Shmem Index".
      36             :  *  If the data structure is not present, the caller can allocate
      37             :  *  a new one and initialize it.  If the data structure is present,
      38             :  *  the caller "attaches" to the structure by initializing a pointer
      39             :  *  in the local address space.
      40             :  *      The shmem index has two purposes: first, it gives us
      41             :  *  a simple model of how the world looks when a backend process
      42             :  *  initializes.  If something is present in the shmem index,
      43             :  *  it is initialized.  If it is not, it is uninitialized.  Second,
      44             :  *  the shmem index allows us to allocate shared memory on demand
      45             :  *  instead of trying to preallocate structures and hard-wire the
      46             :  *  sizes and locations in header files.  If you are using a lot
      47             :  *  of shared memory in a lot of different places (and changing
      48             :  *  things during development), this is important.
      49             :  *
      50             :  *      (c) In standard Unix-ish environments, individual backends do not
      51             :  *  need to re-establish their local pointers into shared memory, because
      52             :  *  they inherit correct values of those variables via fork() from the
      53             :  *  postmaster.  However, this does not work in the EXEC_BACKEND case.
      54             :  *  In ports using EXEC_BACKEND, new backends have to set up their local
      55             :  *  pointers using the method described in (b) above.
      56             :  *
      57             :  *      (d) memory allocation model: shared memory can never be
      58             :  *  freed, once allocated.   Each hash table has its own free list,
      59             :  *  so hash buckets can be reused when an item is deleted.  However,
      60             :  *  if one hash table grows very large and then shrinks, its space
      61             :  *  cannot be redistributed to other tables.  We could build a simple
      62             :  *  hash bucket garbage collector if need be.  Right now, it seems
      63             :  *  unnecessary.
      64             :  */
      65             : 
      66             : #include "postgres.h"
      67             : 
      68             : #include "access/transam.h"
      69             : #include "fmgr.h"
      70             : #include "funcapi.h"
      71             : #include "miscadmin.h"
      72             : #include "storage/lwlock.h"
      73             : #include "storage/pg_shmem.h"
      74             : #include "storage/shmem.h"
      75             : #include "storage/spin.h"
      76             : #include "utils/builtins.h"
      77             : 
      78             : static void *ShmemAllocRaw(Size size, Size *allocated_size);
      79             : 
      80             : /* shared memory global variables */
      81             : 
      82             : static PGShmemHeader *ShmemSegHdr;  /* shared mem segment header */
      83             : 
      84             : static void *ShmemBase;         /* start address of shared memory */
      85             : 
      86             : static void *ShmemEnd;          /* end+1 address of shared memory */
      87             : 
      88             : slock_t    *ShmemLock;          /* spinlock for shared memory and LWLock
      89             :                                  * allocation */
      90             : 
      91             : static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
      92             : 
      93             : 
      94             : /*
      95             :  *  InitShmemAccess() --- set up basic pointers to shared memory.
      96             :  *
      97             :  * Note: the argument should be declared "PGShmemHeader *seghdr",
      98             :  * but we use void to avoid having to include ipc.h in shmem.h.
      99             :  */
     100             : void
     101        2894 : InitShmemAccess(void *seghdr)
     102             : {
     103        2894 :     PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
     104             : 
     105        2894 :     ShmemSegHdr = shmhdr;
     106        2894 :     ShmemBase = (void *) shmhdr;
     107        2894 :     ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
     108        2894 : }
     109             : 
     110             : /*
     111             :  *  InitShmemAllocation() --- set up shared-memory space allocation.
     112             :  *
     113             :  * This should be called only in the postmaster or a standalone backend.
     114             :  */
     115             : void
     116        2894 : InitShmemAllocation(void)
     117             : {
     118        2894 :     PGShmemHeader *shmhdr = ShmemSegHdr;
     119             :     char       *aligned;
     120             : 
     121             :     Assert(shmhdr != NULL);
     122             : 
     123             :     /*
     124             :      * Initialize the spinlock used by ShmemAlloc.  We must use
     125             :      * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
     126             :      */
     127        2894 :     ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
     128             : 
     129        2894 :     SpinLockInit(ShmemLock);
     130             : 
     131             :     /*
     132             :      * Allocations after this point should go through ShmemAlloc, which
     133             :      * expects to allocate everything on cache line boundaries.  Make sure the
     134             :      * first allocation begins on a cache line boundary.
     135             :      */
     136        2894 :     aligned = (char *)
     137        2894 :         (CACHELINEALIGN((((char *) shmhdr) + shmhdr->freeoffset)));
     138        2894 :     shmhdr->freeoffset = aligned - (char *) shmhdr;
     139             : 
     140             :     /* ShmemIndex can't be set up yet (need LWLocks first) */
     141        2894 :     shmhdr->index = NULL;
     142        2894 :     ShmemIndex = (HTAB *) NULL;
     143             : 
     144             :     /*
     145             :      * Initialize ShmemVariableCache for transaction manager. (This doesn't
     146             :      * really belong here, but not worth moving.)
     147             :      */
     148        2894 :     ShmemVariableCache = (VariableCache)
     149        2894 :         ShmemAlloc(sizeof(*ShmemVariableCache));
     150        2894 :     memset(ShmemVariableCache, 0, sizeof(*ShmemVariableCache));
     151        2894 : }
     152             : 
     153             : /*
     154             :  * ShmemAlloc -- allocate max-aligned chunk from shared memory
     155             :  *
     156             :  * Throws error if request cannot be satisfied.
     157             :  *
     158             :  * Assumes ShmemLock and ShmemSegHdr are initialized.
     159             :  */
     160             : void *
     161       28940 : ShmemAlloc(Size size)
     162             : {
     163             :     void       *newSpace;
     164             :     Size        allocated_size;
     165             : 
     166       28940 :     newSpace = ShmemAllocRaw(size, &allocated_size);
     167       28940 :     if (!newSpace)
     168           0 :         ereport(ERROR,
     169             :                 (errcode(ERRCODE_OUT_OF_MEMORY),
     170             :                  errmsg("out of shared memory (%zu bytes requested)",
     171             :                         size)));
     172       28940 :     return newSpace;
     173             : }
     174             : 
     175             : /*
     176             :  * ShmemAllocNoError -- allocate max-aligned chunk from shared memory
     177             :  *
     178             :  * As ShmemAlloc, but returns NULL if out of space, rather than erroring.
     179             :  */
     180             : void *
     181     1102292 : ShmemAllocNoError(Size size)
     182             : {
     183             :     Size        allocated_size;
     184             : 
     185     1102292 :     return ShmemAllocRaw(size, &allocated_size);
     186             : }
     187             : 
     188             : /*
     189             :  * ShmemAllocRaw -- allocate align chunk and return allocated size
     190             :  *
     191             :  * Also sets *allocated_size to the number of bytes allocated, which will
     192             :  * be equal to the number requested plus any padding we choose to add.
     193             :  */
     194             : static void *
     195     1284598 : ShmemAllocRaw(Size size, Size *allocated_size)
     196             : {
     197             :     Size        newStart;
     198             :     Size        newFree;
     199             :     void       *newSpace;
     200             : 
     201             :     /*
     202             :      * Ensure all space is adequately aligned.  We used to only MAXALIGN this
     203             :      * space but experience has proved that on modern systems that is not good
     204             :      * enough.  Many parts of the system are very sensitive to critical data
     205             :      * structures getting split across cache line boundaries.  To avoid that,
     206             :      * attempt to align the beginning of the allocation to a cache line
     207             :      * boundary.  The calling code will still need to be careful about how it
     208             :      * uses the allocated space - e.g. by padding each element in an array of
     209             :      * structures out to a power-of-two size - but without this, even that
     210             :      * won't be sufficient.
     211             :      */
     212     1284598 :     size = CACHELINEALIGN(size);
     213     1284598 :     *allocated_size = size;
     214             : 
     215             :     Assert(ShmemSegHdr != NULL);
     216             : 
     217     1284598 :     SpinLockAcquire(ShmemLock);
     218             : 
     219     1284598 :     newStart = ShmemSegHdr->freeoffset;
     220             : 
     221     1284598 :     newFree = newStart + size;
     222     1284598 :     if (newFree <= ShmemSegHdr->totalsize)
     223             :     {
     224     1284598 :         newSpace = (void *) ((char *) ShmemBase + newStart);
     225     1284598 :         ShmemSegHdr->freeoffset = newFree;
     226             :     }
     227             :     else
     228           0 :         newSpace = NULL;
     229             : 
     230     1284598 :     SpinLockRelease(ShmemLock);
     231             : 
     232             :     /* note this assert is okay with newSpace == NULL */
     233             :     Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
     234             : 
     235     1284598 :     return newSpace;
     236             : }
     237             : 
     238             : /*
     239             :  * ShmemAllocUnlocked -- allocate max-aligned chunk from shared memory
     240             :  *
     241             :  * Allocate space without locking ShmemLock.  This should be used for,
     242             :  * and only for, allocations that must happen before ShmemLock is ready.
     243             :  *
     244             :  * We consider maxalign, rather than cachealign, sufficient here.
     245             :  */
     246             : void *
     247        5788 : ShmemAllocUnlocked(Size size)
     248             : {
     249             :     Size        newStart;
     250             :     Size        newFree;
     251             :     void       *newSpace;
     252             : 
     253             :     /*
     254             :      * Ensure allocated space is adequately aligned.
     255             :      */
     256        5788 :     size = MAXALIGN(size);
     257             : 
     258             :     Assert(ShmemSegHdr != NULL);
     259             : 
     260        5788 :     newStart = ShmemSegHdr->freeoffset;
     261             : 
     262        5788 :     newFree = newStart + size;
     263        5788 :     if (newFree > ShmemSegHdr->totalsize)
     264           0 :         ereport(ERROR,
     265             :                 (errcode(ERRCODE_OUT_OF_MEMORY),
     266             :                  errmsg("out of shared memory (%zu bytes requested)",
     267             :                         size)));
     268        5788 :     ShmemSegHdr->freeoffset = newFree;
     269             : 
     270        5788 :     newSpace = (void *) ((char *) ShmemBase + newStart);
     271             : 
     272             :     Assert(newSpace == (void *) MAXALIGN(newSpace));
     273             : 
     274        5788 :     return newSpace;
     275             : }
     276             : 
     277             : /*
     278             :  * ShmemAddrIsValid -- test if an address refers to shared memory
     279             :  *
     280             :  * Returns true if the pointer points within the shared memory segment.
     281             :  */
     282             : bool
     283           0 : ShmemAddrIsValid(const void *addr)
     284             : {
     285           0 :     return (addr >= ShmemBase) && (addr < ShmemEnd);
     286             : }
     287             : 
     288             : /*
     289             :  *  InitShmemIndex() --- set up or attach to shmem index table.
     290             :  */
     291             : void
     292        2894 : InitShmemIndex(void)
     293             : {
     294             :     HASHCTL     info;
     295             : 
     296             :     /*
     297             :      * Create the shared memory shmem index.
     298             :      *
     299             :      * Since ShmemInitHash calls ShmemInitStruct, which expects the ShmemIndex
     300             :      * hashtable to exist already, we have a bit of a circularity problem in
     301             :      * initializing the ShmemIndex itself.  The special "ShmemIndex" hash
     302             :      * table name will tell ShmemInitStruct to fake it.
     303             :      */
     304        2894 :     info.keysize = SHMEM_INDEX_KEYSIZE;
     305        2894 :     info.entrysize = sizeof(ShmemIndexEnt);
     306             : 
     307        2894 :     ShmemIndex = ShmemInitHash("ShmemIndex",
     308             :                                SHMEM_INDEX_SIZE, SHMEM_INDEX_SIZE,
     309             :                                &info,
     310             :                                HASH_ELEM | HASH_STRINGS);
     311        2894 : }
     312             : 
     313             : /*
     314             :  * ShmemInitHash -- Create and initialize, or attach to, a
     315             :  *      shared memory hash table.
     316             :  *
     317             :  * We assume caller is doing some kind of synchronization
     318             :  * so that two processes don't try to create/initialize the same
     319             :  * table at once.  (In practice, all creations are done in the postmaster
     320             :  * process; child processes should always be attaching to existing tables.)
     321             :  *
     322             :  * max_size is the estimated maximum number of hashtable entries.  This is
     323             :  * not a hard limit, but the access efficiency will degrade if it is
     324             :  * exceeded substantially (since it's used to compute directory size and
     325             :  * the hash table buckets will get overfull).
     326             :  *
     327             :  * init_size is the number of hashtable entries to preallocate.  For a table
     328             :  * whose maximum size is certain, this should be equal to max_size; that
     329             :  * ensures that no run-time out-of-shared-memory failures can occur.
     330             :  *
     331             :  * *infoP and hash_flags must specify at least the entry sizes and key
     332             :  * comparison semantics (see hash_create()).  Flag bits and values specific
     333             :  * to shared-memory hash tables are added here, except that callers may
     334             :  * choose to specify HASH_PARTITION and/or HASH_FIXED_SIZE.
     335             :  *
     336             :  * Note: before Postgres 9.0, this function returned NULL for some failure
     337             :  * cases.  Now, it always throws error instead, so callers need not check
     338             :  * for NULL.
     339             :  */
     340             : HTAB *
     341       20260 : ShmemInitHash(const char *name,     /* table string name for shmem index */
     342             :               long init_size,   /* initial table size */
     343             :               long max_size,    /* max size of the table */
     344             :               HASHCTL *infoP,   /* info about key and bucket size */
     345             :               int hash_flags)   /* info about infoP */
     346             : {
     347             :     bool        found;
     348             :     void       *location;
     349             : 
     350             :     /*
     351             :      * Hash tables allocated in shared memory have a fixed directory; it can't
     352             :      * grow or other backends wouldn't be able to find it. So, make sure we
     353             :      * make it big enough to start with.
     354             :      *
     355             :      * The shared memory allocator must be specified too.
     356             :      */
     357       20260 :     infoP->dsize = infoP->max_dsize = hash_select_dirsize(max_size);
     358       20260 :     infoP->alloc = ShmemAllocNoError;
     359       20260 :     hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
     360             : 
     361             :     /* look it up in the shmem index */
     362       20260 :     location = ShmemInitStruct(name,
     363             :                                hash_get_shared_size(infoP, hash_flags),
     364             :                                &found);
     365             : 
     366             :     /*
     367             :      * if it already exists, attach to it rather than allocate and initialize
     368             :      * new space
     369             :      */
     370       20260 :     if (found)
     371           0 :         hash_flags |= HASH_ATTACH;
     372             : 
     373             :     /* Pass location of hashtable header to hash_create */
     374       20260 :     infoP->hctl = (HASHHDR *) location;
     375             : 
     376       20260 :     return hash_create(name, init_size, infoP, hash_flags);
     377             : }
     378             : 
     379             : /*
     380             :  * ShmemInitStruct -- Create/attach to a structure in shared memory.
     381             :  *
     382             :  *      This is called during initialization to find or allocate
     383             :  *      a data structure in shared memory.  If no other process
     384             :  *      has created the structure, this routine allocates space
     385             :  *      for it.  If it exists already, a pointer to the existing
     386             :  *      structure is returned.
     387             :  *
     388             :  *  Returns: pointer to the object.  *foundPtr is set true if the object was
     389             :  *      already in the shmem index (hence, already initialized).
     390             :  *
     391             :  *  Note: before Postgres 9.0, this function returned NULL for some failure
     392             :  *  cases.  Now, it always throws error instead, so callers need not check
     393             :  *  for NULL.
     394             :  */
     395             : void *
     396      156260 : ShmemInitStruct(const char *name, Size size, bool *foundPtr)
     397             : {
     398             :     ShmemIndexEnt *result;
     399             :     void       *structPtr;
     400             : 
     401      156260 :     LWLockAcquire(ShmemIndexLock, LW_EXCLUSIVE);
     402             : 
     403      156260 :     if (!ShmemIndex)
     404             :     {
     405        2894 :         PGShmemHeader *shmemseghdr = ShmemSegHdr;
     406             : 
     407             :         /* Must be trying to create/attach to ShmemIndex itself */
     408             :         Assert(strcmp(name, "ShmemIndex") == 0);
     409             : 
     410        2894 :         if (IsUnderPostmaster)
     411             :         {
     412             :             /* Must be initializing a (non-standalone) backend */
     413             :             Assert(shmemseghdr->index != NULL);
     414           0 :             structPtr = shmemseghdr->index;
     415           0 :             *foundPtr = true;
     416             :         }
     417             :         else
     418             :         {
     419             :             /*
     420             :              * If the shmem index doesn't exist, we are bootstrapping: we must
     421             :              * be trying to init the shmem index itself.
     422             :              *
     423             :              * Notice that the ShmemIndexLock is released before the shmem
     424             :              * index has been initialized.  This should be OK because no other
     425             :              * process can be accessing shared memory yet.
     426             :              */
     427             :             Assert(shmemseghdr->index == NULL);
     428        2894 :             structPtr = ShmemAlloc(size);
     429        2894 :             shmemseghdr->index = structPtr;
     430        2894 :             *foundPtr = false;
     431             :         }
     432        2894 :         LWLockRelease(ShmemIndexLock);
     433        2894 :         return structPtr;
     434             :     }
     435             : 
     436             :     /* look it up in the shmem index */
     437             :     result = (ShmemIndexEnt *)
     438      153366 :         hash_search(ShmemIndex, name, HASH_ENTER_NULL, foundPtr);
     439             : 
     440      153366 :     if (!result)
     441             :     {
     442           0 :         LWLockRelease(ShmemIndexLock);
     443           0 :         ereport(ERROR,
     444             :                 (errcode(ERRCODE_OUT_OF_MEMORY),
     445             :                  errmsg("could not create ShmemIndex entry for data structure \"%s\"",
     446             :                         name)));
     447             :     }
     448             : 
     449      153366 :     if (*foundPtr)
     450             :     {
     451             :         /*
     452             :          * Structure is in the shmem index so someone else has allocated it
     453             :          * already.  The size better be the same as the size we are trying to
     454             :          * initialize to, or there is a name conflict (or worse).
     455             :          */
     456           0 :         if (result->size != size)
     457             :         {
     458           0 :             LWLockRelease(ShmemIndexLock);
     459           0 :             ereport(ERROR,
     460             :                     (errmsg("ShmemIndex entry size is wrong for data structure"
     461             :                             " \"%s\": expected %zu, actual %zu",
     462             :                             name, size, result->size)));
     463             :         }
     464           0 :         structPtr = result->location;
     465             :     }
     466             :     else
     467             :     {
     468             :         Size        allocated_size;
     469             : 
     470             :         /* It isn't in the table yet. allocate and initialize it */
     471      153366 :         structPtr = ShmemAllocRaw(size, &allocated_size);
     472      153366 :         if (structPtr == NULL)
     473             :         {
     474             :             /* out of memory; remove the failed ShmemIndex entry */
     475           0 :             hash_search(ShmemIndex, name, HASH_REMOVE, NULL);
     476           0 :             LWLockRelease(ShmemIndexLock);
     477           0 :             ereport(ERROR,
     478             :                     (errcode(ERRCODE_OUT_OF_MEMORY),
     479             :                      errmsg("not enough shared memory for data structure"
     480             :                             " \"%s\" (%zu bytes requested)",
     481             :                             name, size)));
     482             :         }
     483      153366 :         result->size = size;
     484      153366 :         result->allocated_size = allocated_size;
     485      153366 :         result->location = structPtr;
     486             :     }
     487             : 
     488      153366 :     LWLockRelease(ShmemIndexLock);
     489             : 
     490             :     Assert(ShmemAddrIsValid(structPtr));
     491             : 
     492             :     Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
     493             : 
     494      153366 :     return structPtr;
     495             : }
     496             : 
     497             : 
     498             : /*
     499             :  * Add two Size values, checking for overflow
     500             :  */
     501             : Size
     502      869368 : add_size(Size s1, Size s2)
     503             : {
     504             :     Size        result;
     505             : 
     506      869368 :     result = s1 + s2;
     507             :     /* We are assuming Size is an unsigned type here... */
     508      869368 :     if (result < s1 || result < s2)
     509           0 :         ereport(ERROR,
     510             :                 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
     511             :                  errmsg("requested shared memory size overflows size_t")));
     512      869368 :     return result;
     513             : }
     514             : 
     515             : /*
     516             :  * Multiply two Size values, checking for overflow
     517             :  */
     518             : Size
     519      386858 : mul_size(Size s1, Size s2)
     520             : {
     521             :     Size        result;
     522             : 
     523      386858 :     if (s1 == 0 || s2 == 0)
     524       21192 :         return 0;
     525      365666 :     result = s1 * s2;
     526             :     /* We are assuming Size is an unsigned type here... */
     527      365666 :     if (result / s2 != s1)
     528           0 :         ereport(ERROR,
     529             :                 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
     530             :                  errmsg("requested shared memory size overflows size_t")));
     531      365666 :     return result;
     532             : }
     533             : 
     534             : /* SQL SRF showing allocated shared memory */
     535             : Datum
     536           4 : pg_get_shmem_allocations(PG_FUNCTION_ARGS)
     537             : {
     538             : #define PG_GET_SHMEM_SIZES_COLS 4
     539           4 :     ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
     540             :     TupleDesc   tupdesc;
     541             :     Tuplestorestate *tupstore;
     542             :     MemoryContext per_query_ctx;
     543             :     MemoryContext oldcontext;
     544             :     HASH_SEQ_STATUS hstat;
     545             :     ShmemIndexEnt *ent;
     546           4 :     Size        named_allocated = 0;
     547             :     Datum       values[PG_GET_SHMEM_SIZES_COLS];
     548             :     bool        nulls[PG_GET_SHMEM_SIZES_COLS];
     549             : 
     550             :     /* check to see if caller supports us returning a tuplestore */
     551           4 :     if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
     552           0 :         ereport(ERROR,
     553             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     554             :                  errmsg("set-valued function called in context that cannot accept a set")));
     555           4 :     if (!(rsinfo->allowedModes & SFRM_Materialize))
     556           0 :         ereport(ERROR,
     557             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     558             :                  errmsg("materialize mode required, but it is not allowed in this context")));
     559             : 
     560             :     /* Build a tuple descriptor for our result type */
     561           4 :     if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
     562           0 :         elog(ERROR, "return type must be a row type");
     563             : 
     564           4 :     per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
     565           4 :     oldcontext = MemoryContextSwitchTo(per_query_ctx);
     566             : 
     567           4 :     tupstore = tuplestore_begin_heap(true, false, work_mem);
     568           4 :     rsinfo->returnMode = SFRM_Materialize;
     569           4 :     rsinfo->setResult = tupstore;
     570           4 :     rsinfo->setDesc = tupdesc;
     571             : 
     572           4 :     MemoryContextSwitchTo(oldcontext);
     573             : 
     574           4 :     LWLockAcquire(ShmemIndexLock, LW_SHARED);
     575             : 
     576           4 :     hash_seq_init(&hstat, ShmemIndex);
     577             : 
     578             :     /* output all allocated entries */
     579           4 :     memset(nulls, 0, sizeof(nulls));
     580         216 :     while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
     581             :     {
     582         212 :         values[0] = CStringGetTextDatum(ent->key);
     583         212 :         values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
     584         212 :         values[2] = Int64GetDatum(ent->size);
     585         212 :         values[3] = Int64GetDatum(ent->allocated_size);
     586         212 :         named_allocated += ent->allocated_size;
     587             : 
     588         212 :         tuplestore_putvalues(tupstore, tupdesc, values, nulls);
     589             :     }
     590             : 
     591             :     /* output shared memory allocated but not counted via the shmem index */
     592           4 :     values[0] = CStringGetTextDatum("<anonymous>");
     593           4 :     nulls[1] = true;
     594           4 :     values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
     595           4 :     values[3] = values[2];
     596           4 :     tuplestore_putvalues(tupstore, tupdesc, values, nulls);
     597             : 
     598             :     /* output as-of-yet unused shared memory */
     599           4 :     nulls[0] = true;
     600           4 :     values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
     601           4 :     nulls[1] = false;
     602           4 :     values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
     603           4 :     values[3] = values[2];
     604           4 :     tuplestore_putvalues(tupstore, tupdesc, values, nulls);
     605             : 
     606           4 :     LWLockRelease(ShmemIndexLock);
     607             : 
     608             :     tuplestore_donestoring(tupstore);
     609             : 
     610           4 :     return (Datum) 0;
     611             : }

Generated by: LCOV version 1.14