LCOV - differential code coverage report
Current view: top level - src/backend/utils/mmgr - slab.c (source / functions) Coverage Total Hit UNC LBC UIC GIC GNC CBC EUB ECB DUB DCB
Current: Differential Code Coverage HEAD vs 15 Lines: 75.5 % 261 197 37 8 19 28 163 6 52 109 12 74
Current Date: 2023-04-08 15:15:32 Functions: 64.3 % 14 9 3 2 5 4 4 6 1 3
Baseline: 15
Baseline Date: 2023-04-08 15:09:40
Legend: Lines: hit not hit

           TLA  Line data    Source code
       1                 : /*-------------------------------------------------------------------------
       2                 :  *
       3                 :  * slab.c
       4                 :  *    SLAB allocator definitions.
       5                 :  *
       6                 :  * SLAB is a MemoryContext implementation designed for cases where large
       7                 :  * numbers of equally-sized objects can be allocated and freed efficiently
       8                 :  * with minimal memory wastage and fragmentation.
       9                 :  *
      10                 :  *
      11                 :  * Portions Copyright (c) 2017-2023, PostgreSQL Global Development Group
      12                 :  *
      13                 :  * IDENTIFICATION
      14                 :  *    src/backend/utils/mmgr/slab.c
      15                 :  *
      16                 :  *
      17                 :  * NOTE:
      18                 :  *  The constant allocation size allows significant simplification and various
      19                 :  *  optimizations over more general purpose allocators. The blocks are carved
      20                 :  *  into chunks of exactly the right size, wasting only the space required to
      21                 :  *  MAXALIGN the allocated chunks.
      22                 :  *
      23                 :  *  Slab can also help reduce memory fragmentation in cases where longer-lived
      24                 :  *  chunks remain stored on blocks while most of the other chunks have already
      25                 :  *  been pfree'd.  We give priority to putting new allocations into the
      26                 :  *  "fullest" block.  This help avoid having too many sparsely used blocks
      27                 :  *  around and allows blocks to more easily become completely unused which
      28                 :  *  allows them to be eventually free'd.
      29                 :  *
      30                 :  *  We identify the "fullest" block to put new allocations on by using a block
      31                 :  *  from the lowest populated element of the context's "blocklist" array.
      32                 :  *  This is an array of dlists containing blocks which we partition by the
      33                 :  *  number of free chunks which block has.  Blocks with fewer free chunks are
      34                 :  *  stored in a lower indexed dlist array slot.  Full blocks go on the 0th
      35                 :  *  element of the blocklist array.  So that we don't have to have too many
      36                 :  *  elements in the array, each dlist in the array is responsible for a range
      37                 :  *  of free chunks.  When a chunk is palloc'd or pfree'd we may need to move
      38                 :  *  the block onto another dlist if the number of free chunks crosses the
      39                 :  *  range boundary that the current list is responsible for.  Having just a
      40                 :  *  few blocklist elements reduces the number of times we must move the block
      41                 :  *  onto another dlist element.
      42                 :  *
      43                 :  *  We keep track of free chunks within each block by using a block-level free
      44                 :  *  list.  We consult this list when we allocate a new chunk in the block.
      45                 :  *  The free list is a linked list, the head of which is pointed to with
      46                 :  *  SlabBlock's freehead field.  Each subsequent list item is stored in the
      47                 :  *  free chunk's memory.  We ensure chunks are large enough to store this
      48                 :  *  address.
      49                 :  *
      50                 :  *  When we allocate a new block, technically all chunks are free, however, to
      51                 :  *  avoid having to write out the entire block to set the linked list for the
      52                 :  *  free chunks for every chunk in the block, we instead store a pointer to
      53                 :  *  the next "unused" chunk on the block and keep track of how many of these
      54                 :  *  unused chunks there are.  When a new block is malloc'd, all chunks are
      55                 :  *  unused.  The unused pointer starts with the first chunk on the block and
      56                 :  *  as chunks are allocated, the unused pointer is incremented.  As chunks are
      57                 :  *  pfree'd, the unused pointer never goes backwards.  The unused pointer can
      58                 :  *  be thought of as a high watermark for the maximum number of chunks in the
      59                 :  *  block which have been in use concurrently.  When a chunk is pfree'd the
      60                 :  *  chunk is put onto the head of the free list and the unused pointer is not
      61                 :  *  changed.  We only consume more unused chunks if we run out of free chunks
      62                 :  *  on the free list.  This method effectively gives priority to using
      63                 :  *  previously used chunks over previously unused chunks, which should perform
      64                 :  *  better due to CPU caching effects.
      65                 :  *
      66                 :  *-------------------------------------------------------------------------
      67                 :  */
      68                 : 
      69                 : #include "postgres.h"
      70                 : 
      71                 : #include "lib/ilist.h"
      72                 : #include "utils/memdebug.h"
      73                 : #include "utils/memutils.h"
      74                 : #include "utils/memutils_memorychunk.h"
      75                 : #include "utils/memutils_internal.h"
      76                 : 
      77                 : #define Slab_BLOCKHDRSZ MAXALIGN(sizeof(SlabBlock))
      78                 : 
      79                 : #ifdef MEMORY_CONTEXT_CHECKING
      80                 : /*
      81                 :  * Size of the memory required to store the SlabContext.
      82                 :  * MEMORY_CONTEXT_CHECKING builds need some extra memory for the isChunkFree
      83                 :  * array.
      84                 :  */
      85                 : #define Slab_CONTEXT_HDRSZ(chunksPerBlock)  \
      86                 :     (sizeof(SlabContext) + ((chunksPerBlock) * sizeof(bool)))
      87                 : #else
      88                 : #define Slab_CONTEXT_HDRSZ(chunksPerBlock)  sizeof(SlabContext)
      89                 : #endif
      90                 : 
      91                 : /*
      92                 :  * The number of partitions to divide the blocklist into based their number of
      93                 :  * free chunks.  There must be at least 2.
      94                 :  */
      95                 : #define SLAB_BLOCKLIST_COUNT 3
      96                 : 
      97                 : /* The maximum number of completely empty blocks to keep around for reuse. */
      98                 : #define SLAB_MAXIMUM_EMPTY_BLOCKS 10
      99                 : 
     100                 : /*
     101                 :  * SlabContext is a specialized implementation of MemoryContext.
     102                 :  */
     103                 : typedef struct SlabContext
     104                 : {
     105                 :     MemoryContextData header;   /* Standard memory-context fields */
     106                 :     /* Allocation parameters for this context: */
     107                 :     Size        chunkSize;      /* the requested (non-aligned) chunk size */
     108                 :     Size        fullChunkSize;  /* chunk size with chunk header and alignment */
     109                 :     Size        blockSize;      /* the size to make each block of chunks */
     110                 :     int32       chunksPerBlock; /* number of chunks that fit in 1 block */
     111                 :     int32       curBlocklistIndex;  /* index into the blocklist[] element
     112                 :                                      * containing the fullest, blocks */
     113                 : #ifdef MEMORY_CONTEXT_CHECKING
     114                 :     bool       *isChunkFree;    /* array to mark free chunks in a block during
     115                 :                                  * SlabCheck */
     116                 : #endif
     117                 : 
     118                 :     int32       blocklist_shift;    /* number of bits to shift the nfree count
     119                 :                                      * by to get the index into blocklist[] */
     120                 :     dclist_head emptyblocks;    /* empty blocks to use up first instead of
     121                 :                                  * mallocing new blocks */
     122                 : 
     123                 :     /*
     124                 :      * Blocks with free space, grouped by the number of free chunks they
     125                 :      * contain.  Completely full blocks are stored in the 0th element.
     126                 :      * Completely empty blocks are stored in emptyblocks or free'd if we have
     127                 :      * enough empty blocks already.
     128                 :      */
     129                 :     dlist_head  blocklist[SLAB_BLOCKLIST_COUNT];
     130                 : } SlabContext;
     131                 : 
     132                 : /*
     133                 :  * SlabBlock
     134                 :  *      Structure of a single slab block.
     135                 :  *
     136                 :  * slab: pointer back to the owning MemoryContext
     137                 :  * nfree: number of chunks on the block which are unallocated
     138                 :  * nunused: number of chunks on the block unallocated and not on the block's
     139                 :  * freelist.
     140                 :  * freehead: linked-list header storing a pointer to the first free chunk on
     141                 :  * the block.  Subsequent pointers are stored in the chunk's memory.  NULL
     142                 :  * indicates the end of the list.
     143                 :  * unused: pointer to the next chunk which has yet to be used.
     144                 :  * node: doubly-linked list node for the context's blocklist
     145                 :  */
     146                 : typedef struct SlabBlock
     147                 : {
     148                 :     SlabContext *slab;          /* owning context */
     149                 :     int32       nfree;          /* number of chunks on free + unused chunks */
     150                 :     int32       nunused;        /* number of unused chunks */
     151                 :     MemoryChunk *freehead;      /* pointer to the first free chunk */
     152                 :     MemoryChunk *unused;        /* pointer to the next unused chunk */
     153                 :     dlist_node  node;           /* doubly-linked list for blocklist[] */
     154                 : } SlabBlock;
     155                 : 
     156                 : 
     157                 : #define Slab_CHUNKHDRSZ sizeof(MemoryChunk)
     158                 : #define SlabChunkGetPointer(chk)    \
     159                 :     ((void *) (((char *) (chk)) + sizeof(MemoryChunk)))
     160                 : 
     161                 : /*
     162                 :  * SlabBlockGetChunk
     163                 :  *      Obtain a pointer to the nth (0-based) chunk in the block
     164                 :  */
     165                 : #define SlabBlockGetChunk(slab, block, n) \
     166                 :     ((MemoryChunk *) ((char *) (block) + Slab_BLOCKHDRSZ    \
     167                 :                     + ((n) * (slab)->fullChunkSize)))
     168                 : 
     169                 : #if defined(MEMORY_CONTEXT_CHECKING) || defined(USE_ASSERT_CHECKING)
     170                 : 
     171                 : /*
     172                 :  * SlabChunkIndex
     173                 :  *      Get the 0-based index of how many chunks into the block the given
     174                 :  *      chunk is.
     175                 : */
     176                 : #define SlabChunkIndex(slab, block, chunk)  \
     177                 :     (((char *) (chunk) - (char *) SlabBlockGetChunk(slab, block, 0)) / \
     178                 :     (slab)->fullChunkSize)
     179                 : 
     180                 : /*
     181                 :  * SlabChunkMod
     182                 :  *      A MemoryChunk should always be at an address which is a multiple of
     183                 :  *      fullChunkSize starting from the 0th chunk position.  This will return
     184                 :  *      non-zero if it's not.
     185                 :  */
     186                 : #define SlabChunkMod(slab, block, chunk)    \
     187                 :     (((char *) (chunk) - (char *) SlabBlockGetChunk(slab, block, 0)) % \
     188                 :     (slab)->fullChunkSize)
     189                 : 
     190                 : #endif
     191                 : 
     192                 : /*
     193                 :  * SlabIsValid
     194                 :  *      True iff set is a valid slab allocation set.
     195                 :  */
     196                 : #define SlabIsValid(set) (PointerIsValid(set) && IsA(set, SlabContext))
     197                 : 
     198                 : /*
     199                 :  * SlabBlockIsValid
     200                 :  *      True iff block is a valid block of slab allocation set.
     201                 :  */
     202                 : #define SlabBlockIsValid(block) \
     203                 :     (PointerIsValid(block) && SlabIsValid((block)->slab))
     204                 : 
     205                 : /*
     206                 :  * SlabBlocklistIndex
     207                 :  *      Determine the blocklist index that a block should be in for the given
     208                 :  *      number of free chunks.
     209                 :  */
     210                 : static inline int32
     211 GNC     9560935 : SlabBlocklistIndex(SlabContext *slab, int nfree)
     212                 : {
     213                 :     int32       index;
     214         9560935 :     int32       blocklist_shift = slab->blocklist_shift;
     215                 : 
     216         9560935 :     Assert(nfree >= 0 && nfree <= slab->chunksPerBlock);
     217                 : 
     218                 :     /*
     219                 :      * Determine the blocklist index based on the number of free chunks.  We
     220                 :      * must ensure that 0 free chunks is dedicated to index 0.  Everything
     221                 :      * else must be >= 1 and < SLAB_BLOCKLIST_COUNT.
     222                 :      *
     223                 :      * To make this as efficient as possible, we exploit some two's complement
     224                 :      * arithmetic where we reverse the sign before bit shifting.  This results
     225                 :      * in an nfree of 0 using index 0 and anything non-zero staying non-zero.
     226                 :      * This is exploiting 0 and -0 being the same in two's complement.  When
     227                 :      * we're done, we just need to flip the sign back over again for a
     228                 :      * positive index.
     229                 :      */
     230         9560935 :     index = -((-nfree) >> blocklist_shift);
     231                 : 
     232         9560935 :     if (nfree == 0)
     233           46454 :         Assert(index == 0);
     234                 :     else
     235         9514481 :         Assert(index >= 1 && index < SLAB_BLOCKLIST_COUNT);
     236                 : 
     237         9560935 :     return index;
     238                 : }
     239                 : 
     240                 : /*
     241                 :  * SlabFindNextBlockListIndex
     242                 :  *      Search blocklist for blocks which have free chunks and return the
     243                 :  *      index of the blocklist found containing at least 1 block with free
     244                 :  *      chunks.  If no block can be found we return 0.
     245                 :  *
     246                 :  * Note: We give priority to fuller blocks so that these are filled before
     247                 :  * emptier blocks.  This is done to increase the chances that mostly-empty
     248                 :  * blocks will eventually become completely empty so they can be free'd.
     249                 :  */
     250                 : static int32
     251          106553 : SlabFindNextBlockListIndex(SlabContext *slab)
     252                 : {
     253                 :     /* start at 1 as blocklist[0] is for full blocks. */
     254          175236 :     for (int i = 1; i < SLAB_BLOCKLIST_COUNT; i++)
     255                 :     {
     256                 :         /* return the first found non-empty index */
     257          144636 :         if (!dlist_is_empty(&slab->blocklist[i]))
     258           75953 :             return i;
     259                 :     }
     260                 : 
     261                 :     /* no blocks with free space */
     262           30600 :     return 0;
     263                 : }
     264                 : 
     265                 : /*
     266                 :  * SlabGetNextFreeChunk
     267                 :  *      Return the next free chunk in block and update the block to account
     268                 :  *      for the returned chunk now being used.
     269                 :  */
     270                 : static inline MemoryChunk *
     271         1915610 : SlabGetNextFreeChunk(SlabContext *slab, SlabBlock *block)
     272                 : {
     273                 :     MemoryChunk *chunk;
     274                 : 
     275         1915610 :     Assert(block->nfree > 0);
     276                 : 
     277         1915610 :     if (block->freehead != NULL)
     278                 :     {
     279         1736375 :         chunk = block->freehead;
     280                 : 
     281                 :         /*
     282                 :          * Pop the chunk from the linked list of free chunks.  The pointer to
     283                 :          * the next free chunk is stored in the chunk itself.
     284                 :          */
     285                 :         VALGRIND_MAKE_MEM_DEFINED(SlabChunkGetPointer(chunk), sizeof(MemoryChunk *));
     286         1736375 :         block->freehead = *(MemoryChunk **) SlabChunkGetPointer(chunk);
     287                 : 
     288                 :         /* check nothing stomped on the free chunk's memory */
     289         1736375 :         Assert(block->freehead == NULL ||
     290                 :                (block->freehead >= SlabBlockGetChunk(slab, block, 0) &&
     291                 :                 block->freehead <= SlabBlockGetChunk(slab, block, slab->chunksPerBlock - 1) &&
     292                 :                 SlabChunkMod(slab, block, block->freehead) == 0));
     293                 :     }
     294                 :     else
     295                 :     {
     296          179235 :         Assert(block->nunused > 0);
     297                 : 
     298          179235 :         chunk = block->unused;
     299          179235 :         block->unused = (MemoryChunk *) (((char *) block->unused) + slab->fullChunkSize);
     300          179235 :         block->nunused--;
     301                 :     }
     302                 : 
     303         1915610 :     block->nfree--;
     304                 : 
     305         1915610 :     return chunk;
     306                 : }
     307                 : 
     308                 : /*
     309                 :  * SlabContextCreate
     310                 :  *      Create a new Slab context.
     311                 :  *
     312                 :  * parent: parent context, or NULL if top-level context
     313                 :  * name: name of context (must be statically allocated)
     314                 :  * blockSize: allocation block size
     315                 :  * chunkSize: allocation chunk size
     316                 :  *
     317                 :  * The MAXALIGN(chunkSize) may not exceed MEMORYCHUNK_MAX_VALUE
     318                 :  */
     319                 : MemoryContext
     320 GIC        1646 : SlabContextCreate(MemoryContext parent,
     321                 :                   const char *name,
     322                 :                   Size blockSize,
     323                 :                   Size chunkSize)
     324                 : {
     325                 :     int         chunksPerBlock;
     326                 :     Size        fullChunkSize;
     327                 :     SlabContext *slab;
     328                 :     int         i;
     329                 : 
     330                 :     /* ensure MemoryChunk's size is properly maxaligned */
     331                 :     StaticAssertDecl(Slab_CHUNKHDRSZ == MAXALIGN(Slab_CHUNKHDRSZ),
     332                 :                      "sizeof(MemoryChunk) is not maxaligned");
     333 GNC        1646 :     Assert(MAXALIGN(chunkSize) <= MEMORYCHUNK_MAX_VALUE);
     334                 : 
     335 ECB             :     /*
     336                 :      * Ensure there's enough space to store the pointer to the next free chunk
     337                 :      * in the memory of the (otherwise) unused allocation.
     338                 :      */
     339 GNC        1646 :     if (chunkSize < sizeof(MemoryChunk *))
     340 UNC           0 :         chunkSize = sizeof(MemoryChunk *);
     341                 : 
     342                 :     /* length of the maxaligned chunk including the chunk header  */
     343                 : #ifdef MEMORY_CONTEXT_CHECKING
     344                 :     /* ensure there's always space for the sentinel byte */
     345 GNC        1646 :     fullChunkSize = Slab_CHUNKHDRSZ + MAXALIGN(chunkSize + 1);
     346                 : #else
     347                 :     fullChunkSize = Slab_CHUNKHDRSZ + MAXALIGN(chunkSize);
     348 ECB             : #endif
     349                 : 
     350                 :     /* compute the number of chunks that will fit on each block */
     351 GNC        1646 :     chunksPerBlock = (blockSize - Slab_BLOCKHDRSZ) / fullChunkSize;
     352                 : 
     353                 :     /* Make sure the block can store at least one chunk. */
     354            1646 :     if (chunksPerBlock == 0)
     355 UNC           0 :         elog(ERROR, "block size %zu for slab is too small for %zu-byte chunks",
     356                 :              blockSize, chunkSize);
     357                 : 
     358                 : 
     359                 : 
     360 GNC        1646 :     slab = (SlabContext *) malloc(Slab_CONTEXT_HDRSZ(chunksPerBlock));
     361 CBC        1646 :     if (slab == NULL)
     362                 :     {
     363 LBC           0 :         MemoryContextStats(TopMemoryContext);
     364 UIC           0 :         ereport(ERROR,
     365 ECB             :                 (errcode(ERRCODE_OUT_OF_MEMORY),
     366                 :                  errmsg("out of memory"),
     367                 :                  errdetail("Failed while creating memory context \"%s\".",
     368                 :                            name)));
     369                 :     }
     370                 : 
     371                 :     /*
     372                 :      * Avoid writing code that can fail between here and MemoryContextCreate;
     373                 :      * we'd leak the header if we ereport in this stretch.
     374                 :      */
     375                 : 
     376                 :     /* Fill in SlabContext-specific header fields */
     377 GIC        1646 :     slab->chunkSize = chunkSize;
     378            1646 :     slab->fullChunkSize = fullChunkSize;
     379 CBC        1646 :     slab->blockSize = blockSize;
     380 GIC        1646 :     slab->chunksPerBlock = chunksPerBlock;
     381 GNC        1646 :     slab->curBlocklistIndex = 0;
     382                 : 
     383                 :     /*
     384                 :      * Compute a shift that guarantees that shifting chunksPerBlock with it is
     385                 :      * < SLAB_BLOCKLIST_COUNT - 1.  The reason that we subtract 1 from
     386                 :      * SLAB_BLOCKLIST_COUNT in this calculation is that we reserve the 0th
     387                 :      * blocklist element for blocks which have no free chunks.
     388                 :      *
     389                 :      * We calculate the number of bits to shift by rather than a divisor to
     390                 :      * divide by as performing division each time we need to find the
     391                 :      * blocklist index would be much slower.
     392                 :      */
     393            1646 :     slab->blocklist_shift = 0;
     394            9876 :     while ((slab->chunksPerBlock >> slab->blocklist_shift) >= (SLAB_BLOCKLIST_COUNT - 1))
     395            8230 :         slab->blocklist_shift++;
     396                 : 
     397                 :     /* initialize the list to store empty blocks to be reused */
     398            1646 :     dclist_init(&slab->emptyblocks);
     399                 : 
     400                 :     /* initialize each blocklist slot */
     401            6584 :     for (i = 0; i < SLAB_BLOCKLIST_COUNT; i++)
     402            4938 :         dlist_init(&slab->blocklist[i]);
     403                 : 
     404                 : #ifdef MEMORY_CONTEXT_CHECKING
     405                 :     /* set the isChunkFree pointer right after the end of the context */
     406            1646 :     slab->isChunkFree = (bool *) ((char *) slab + sizeof(SlabContext));
     407                 : #endif
     408                 : 
     409                 :     /* Finally, do the type-independent part of context creation */
     410 GIC        1646 :     MemoryContextCreate((MemoryContext) slab,
     411                 :                         T_SlabContext,
     412                 :                         MCTX_SLAB_ID,
     413 ECB             :                         parent,
     414                 :                         name);
     415                 : 
     416 GIC        1646 :     return (MemoryContext) slab;
     417 ECB             : }
     418                 : 
     419                 : /*
     420                 :  * SlabReset
     421                 :  *      Frees all memory which is allocated in the given set.
     422                 :  *
     423                 :  * The code simply frees all the blocks in the context - we don't keep any
     424                 :  * keeper blocks or anything like that.
     425                 :  */
     426                 : void
     427 GIC        1390 : SlabReset(MemoryContext context)
     428 ECB             : {
     429 GNC        1390 :     SlabContext *slab = (SlabContext *) context;
     430                 :     dlist_mutable_iter miter;
     431                 :     int         i;
     432 ECB             : 
     433 GNC        1390 :     Assert(SlabIsValid(slab));
     434                 : 
     435                 : #ifdef MEMORY_CONTEXT_CHECKING
     436                 :     /* Check for corruption and leaks before freeing */
     437 GIC        1390 :     SlabCheck(context);
     438                 : #endif
     439 ECB             : 
     440                 :     /* release any retained empty blocks */
     441 GNC        2098 :     dclist_foreach_modify(miter, &slab->emptyblocks)
     442 ECB             :     {
     443 GNC         708 :         SlabBlock  *block = dlist_container(SlabBlock, node, miter.cur);
     444                 : 
     445             708 :         dclist_delete_from(&slab->emptyblocks, miter.cur);
     446                 : 
     447                 : #ifdef CLOBBER_FREED_MEMORY
     448             708 :         wipe_mem(block, slab->blockSize);
     449                 : #endif
     450             708 :         free(block);
     451             708 :         context->mem_allocated -= slab->blockSize;
     452                 :     }
     453                 : 
     454                 :     /* walk over blocklist and free the blocks */
     455            5560 :     for (i = 0; i < SLAB_BLOCKLIST_COUNT; i++)
     456                 :     {
     457            4222 :         dlist_foreach_modify(miter, &slab->blocklist[i])
     458 ECB             :         {
     459 GIC          52 :             SlabBlock  *block = dlist_container(SlabBlock, node, miter.cur);
     460 ECB             : 
     461 GIC          52 :             dlist_delete(miter.cur);
     462                 : 
     463                 : #ifdef CLOBBER_FREED_MEMORY
     464              52 :             wipe_mem(block, slab->blockSize);
     465                 : #endif
     466              52 :             free(block);
     467              52 :             context->mem_allocated -= slab->blockSize;
     468                 :         }
     469                 :     }
     470                 : 
     471 GNC        1390 :     slab->curBlocklistIndex = 0;
     472                 : 
     473 CBC        1390 :     Assert(context->mem_allocated == 0);
     474 GIC        1390 : }
     475                 : 
     476                 : /*
     477                 :  * SlabDelete
     478                 :  *      Free all memory which is allocated in the given context.
     479                 :  */
     480                 : void
     481            1390 : SlabDelete(MemoryContext context)
     482                 : {
     483                 :     /* Reset to release all the SlabBlocks */
     484            1390 :     SlabReset(context);
     485                 :     /* And free the context header */
     486 CBC        1390 :     free(context);
     487 GIC        1390 : }
     488                 : 
     489                 : /*
     490                 :  * SlabAlloc
     491                 :  *      Returns a pointer to allocated memory of given size or NULL if
     492 ECB             :  *      request could not be completed; memory is added to the slab.
     493 EUB             :  */
     494                 : void *
     495 GIC     1918429 : SlabAlloc(MemoryContext context, Size size)
     496                 : {
     497 GNC     1918429 :     SlabContext *slab = (SlabContext *) context;
     498 ECB             :     SlabBlock  *block;
     499                 :     MemoryChunk *chunk;
     500                 : 
     501 GNC     1918429 :     Assert(SlabIsValid(slab));
     502                 : 
     503                 :     /* sanity check that this is pointing to a valid blocklist */
     504         1918429 :     Assert(slab->curBlocklistIndex >= 0);
     505         1918429 :     Assert(slab->curBlocklistIndex <= SlabBlocklistIndex(slab, slab->chunksPerBlock));
     506                 : 
     507 ECB             :     /* make sure we only allow correct request size */
     508 GNC     1918429 :     if (unlikely(size != slab->chunkSize))
     509 UIC           0 :         elog(ERROR, "unexpected alloc chunk size %zu (expected %zu)",
     510                 :              size, slab->chunkSize);
     511                 : 
     512                 :     /*
     513                 :      * Handle the case when there are no partially filled blocks available.
     514                 :      * SlabFree() will have updated the curBlocklistIndex setting it to zero
     515                 :      * to indicate that it has freed the final block.  Also later in
     516                 :      * SlabAlloc() we will set the curBlocklistIndex to zero if we end up
     517                 :      * filling the final block.
     518                 :      */
     519 GNC     1918429 :     if (unlikely(slab->curBlocklistIndex == 0))
     520                 :     {
     521                 :         dlist_head *blocklist;
     522                 :         int         blocklist_idx;
     523                 : 
     524                 :         /* to save allocating a new one, first check the empty blocks list */
     525           30642 :         if (dclist_count(&slab->emptyblocks) > 0)
     526 ECB             :         {
     527 GNC       27823 :             dlist_node *node = dclist_pop_head_node(&slab->emptyblocks);
     528                 : 
     529           27823 :             block = dlist_container(SlabBlock, node, node);
     530                 : 
     531                 :             /*
     532                 :              * SlabFree() should have left this block in a valid state with
     533                 :              * all chunks free.  Ensure that's the case.
     534                 :              */
     535           27823 :             Assert(block->nfree == slab->chunksPerBlock);
     536                 : 
     537                 :             /* fetch the next chunk from this block */
     538           27823 :             chunk = SlabGetNextFreeChunk(slab, block);
     539                 :         }
     540                 :         else
     541                 :         {
     542            2819 :             block = (SlabBlock *) malloc(slab->blockSize);
     543                 : 
     544            2819 :             if (unlikely(block == NULL))
     545 UNC           0 :                 return NULL;
     546                 : 
     547 GNC        2819 :             block->slab = slab;
     548            2819 :             context->mem_allocated += slab->blockSize;
     549                 : 
     550                 :             /* use the first chunk in the new block */
     551            2819 :             chunk = SlabBlockGetChunk(slab, block, 0);
     552                 : 
     553            2819 :             block->nfree = slab->chunksPerBlock - 1;
     554            2819 :             block->unused = SlabBlockGetChunk(slab, block, 1);
     555            2819 :             block->freehead = NULL;
     556            2819 :             block->nunused = slab->chunksPerBlock - 1;
     557                 :         }
     558                 : 
     559                 :         /* find the blocklist element for storing blocks with 1 used chunk */
     560           30642 :         blocklist_idx = SlabBlocklistIndex(slab, block->nfree);
     561           30642 :         blocklist = &slab->blocklist[blocklist_idx];
     562 ECB             : 
     563                 :         /* this better be empty.  We just added a block thinking it was */
     564 GNC       30642 :         Assert(dlist_is_empty(blocklist));
     565 ECB             : 
     566 GNC       30642 :         dlist_push_head(blocklist, &block->node);
     567                 : 
     568           30642 :         slab->curBlocklistIndex = blocklist_idx;
     569                 :     }
     570                 :     else
     571                 :     {
     572         1887787 :         dlist_head *blocklist = &slab->blocklist[slab->curBlocklistIndex];
     573                 :         int         new_blocklist_idx;
     574                 : 
     575         1887787 :         Assert(!dlist_is_empty(blocklist));
     576                 : 
     577                 :         /* grab the block from the blocklist */
     578         1887787 :         block = dlist_head_element(SlabBlock, node, blocklist);
     579                 : 
     580                 :         /* make sure we actually got a valid block, with matching nfree */
     581         1887787 :         Assert(block != NULL);
     582         1887787 :         Assert(slab->curBlocklistIndex == SlabBlocklistIndex(slab, block->nfree));
     583         1887787 :         Assert(block->nfree > 0);
     584                 : 
     585                 :         /* fetch the next chunk from this block */
     586         1887787 :         chunk = SlabGetNextFreeChunk(slab, block);
     587                 : 
     588                 :         /* get the new blocklist index based on the new free chunk count */
     589         1887787 :         new_blocklist_idx = SlabBlocklistIndex(slab, block->nfree);
     590                 : 
     591                 :         /*
     592                 :          * Handle the case where the blocklist index changes.  This also deals
     593                 :          * with blocks becoming full as only full blocks go at index 0.
     594                 :          */
     595         1887787 :         if (unlikely(slab->curBlocklistIndex != new_blocklist_idx))
     596                 :         {
     597           49342 :             dlist_delete_from(blocklist, &block->node);
     598           49342 :             dlist_push_head(&slab->blocklist[new_blocklist_idx], &block->node);
     599                 : 
     600           49342 :             if (dlist_is_empty(blocklist))
     601           49277 :                 slab->curBlocklistIndex = SlabFindNextBlockListIndex(slab);
     602                 :         }
     603                 :     }
     604 ECB             : 
     605                 :     /*
     606                 :      * Check that the chunk pointer is actually somewhere on the block and is
     607                 :      * aligned as expected.
     608                 :      */
     609 GNC     1918429 :     Assert(chunk >= SlabBlockGetChunk(slab, block, 0));
     610         1918429 :     Assert(chunk <= SlabBlockGetChunk(slab, block, slab->chunksPerBlock - 1));
     611         1918429 :     Assert(SlabChunkMod(slab, block, chunk) == 0);
     612 ECB             : 
     613                 :     /* Prepare to initialize the chunk header. */
     614                 :     VALGRIND_MAKE_MEM_UNDEFINED(chunk, Slab_CHUNKHDRSZ);
     615                 : 
     616 GNC     1918429 :     MemoryChunkSetHdrMask(chunk, block, MAXALIGN(slab->chunkSize),
     617                 :                           MCTX_SLAB_ID);
     618 ECB             : #ifdef MEMORY_CONTEXT_CHECKING
     619                 :     /* slab mark to catch clobber of "unused" space */
     620 GNC     1918429 :     Assert(slab->chunkSize < (slab->fullChunkSize - Slab_CHUNKHDRSZ));
     621         1918429 :     set_sentinel(MemoryChunkGetPointer(chunk), size);
     622                 :     VALGRIND_MAKE_MEM_NOACCESS(((char *) chunk) +
     623                 :                                Slab_CHUNKHDRSZ + slab->chunkSize,
     624                 :                                slab->fullChunkSize -
     625                 :                                (slab->chunkSize + Slab_CHUNKHDRSZ));
     626 ECB             : #endif
     627                 : 
     628                 : #ifdef RANDOMIZE_ALLOCATED_MEMORY
     629                 :     /* fill the allocated space with junk */
     630                 :     randomize_mem((char *) MemoryChunkGetPointer(chunk), size);
     631                 : #endif
     632                 : 
     633 GNC     1918429 :     return MemoryChunkGetPointer(chunk);
     634                 : }
     635                 : 
     636 ECB             : /*
     637                 :  * SlabFree
     638                 :  *      Frees allocated memory; memory is removed from the slab.
     639                 :  */
     640                 : void
     641 GNC     1918119 : SlabFree(void *pointer)
     642 ECB             : {
     643 GNC     1918119 :     MemoryChunk *chunk = PointerGetMemoryChunk(pointer);
     644         1918119 :     SlabBlock  *block = MemoryChunkGetBlock(chunk);
     645                 :     SlabContext *slab;
     646                 :     int         curBlocklistIdx;
     647                 :     int         newBlocklistIdx;
     648                 : 
     649                 :     /*
     650                 :      * For speed reasons we just Assert that the referenced block is good.
     651                 :      * Future field experience may show that this Assert had better become a
     652                 :      * regular runtime test-and-elog check.
     653                 :      */
     654         1918119 :     Assert(SlabBlockIsValid(block));
     655         1918119 :     slab = block->slab;
     656                 : 
     657                 : #ifdef MEMORY_CONTEXT_CHECKING
     658 ECB             :     /* Test for someone scribbling on unused space in chunk */
     659 GNC     1918119 :     Assert(slab->chunkSize < (slab->fullChunkSize - Slab_CHUNKHDRSZ));
     660         1918119 :     if (!sentinel_ok(pointer, slab->chunkSize))
     661 UNC           0 :         elog(WARNING, "detected write past chunk end in %s %p",
     662                 :              slab->header.name, chunk);
     663                 : #endif
     664                 : 
     665                 :     /* push this chunk onto the head of the block's free list */
     666 GNC     1918119 :     *(MemoryChunk **) pointer = block->freehead;
     667         1918119 :     block->freehead = chunk;
     668                 : 
     669 GIC     1918119 :     block->nfree++;
     670                 : 
     671         1918119 :     Assert(block->nfree > 0);
     672         1918119 :     Assert(block->nfree <= slab->chunksPerBlock);
     673 ECB             : 
     674                 : #ifdef CLOBBER_FREED_MEMORY
     675                 :     /* don't wipe the free list MemoryChunk pointer stored in the chunk */
     676 GNC     1918119 :     wipe_mem((char *) pointer + sizeof(MemoryChunk *),
     677         1918119 :              slab->chunkSize - sizeof(MemoryChunk *));
     678                 : #endif
     679                 : 
     680         1918119 :     curBlocklistIdx = SlabBlocklistIndex(slab, block->nfree - 1);
     681         1918119 :     newBlocklistIdx = SlabBlocklistIndex(slab, block->nfree);
     682                 : 
     683 ECB             :     /*
     684                 :      * Check if the block needs to be moved to another element on the
     685                 :      * blocklist based on it now having 1 more free chunk.
     686                 :      */
     687 GNC     1918119 :     if (unlikely(curBlocklistIdx != newBlocklistIdx))
     688 ECB             :     {
     689                 :         /* do the move */
     690 GNC       49336 :         dlist_delete_from(&slab->blocklist[curBlocklistIdx], &block->node);
     691           49336 :         dlist_push_head(&slab->blocklist[newBlocklistIdx], &block->node);
     692                 : 
     693                 :         /*
     694                 :          * The blocklist[curBlocklistIdx] may now be empty or we may now be
     695                 :          * able to use a lower-element blocklist.  We'll need to redetermine
     696                 :          * what the slab->curBlocklistIndex is if the current blocklist was
     697                 :          * changed or if a lower element one was changed.  We must ensure we
     698                 :          * use the list with the fullest block(s).
     699                 :          */
     700           49336 :         if (slab->curBlocklistIndex >= curBlocklistIdx)
     701 ECB             :         {
     702 GNC       49336 :             slab->curBlocklistIndex = SlabFindNextBlockListIndex(slab);
     703                 : 
     704                 :             /*
     705                 :              * We know there must be a block with at least 1 unused chunk as
     706                 :              * we just pfree'd one.  Ensure curBlocklistIndex reflects this.
     707                 :              */
     708           49336 :             Assert(slab->curBlocklistIndex > 0);
     709                 :         }
     710                 :     }
     711                 : 
     712                 :     /* Handle when a block becomes completely empty */
     713         1918119 :     if (unlikely(block->nfree == slab->chunksPerBlock))
     714                 :     {
     715                 :         /* remove the block */
     716           30571 :         dlist_delete_from(&slab->blocklist[newBlocklistIdx], &block->node);
     717                 : 
     718                 :         /*
     719                 :          * To avoid thrashing malloc/free, we keep a list of empty blocks that
     720                 :          * we can reuse again instead of having to malloc a new one.
     721                 :          */
     722           30571 :         if (dclist_count(&slab->emptyblocks) < SLAB_MAXIMUM_EMPTY_BLOCKS)
     723           28858 :             dclist_push_head(&slab->emptyblocks, &block->node);
     724                 :         else
     725                 :         {
     726                 :             /*
     727                 :              * When we have enough empty blocks stored already, we actually
     728                 :              * free the block.
     729                 :              */
     730                 : #ifdef CLOBBER_FREED_MEMORY
     731            1713 :             wipe_mem(block, slab->blockSize);
     732                 : #endif
     733            1713 :             free(block);
     734            1713 :             slab->header.mem_allocated -= slab->blockSize;
     735                 :         }
     736                 : 
     737                 :         /*
     738                 :          * Check if we need to reset the blocklist index.  This is required
     739                 :          * when the blocklist this block is on has become completely empty.
     740                 :          */
     741           42926 :         if (slab->curBlocklistIndex == newBlocklistIdx &&
     742           12355 :             dlist_is_empty(&slab->blocklist[newBlocklistIdx]))
     743            7940 :             slab->curBlocklistIndex = SlabFindNextBlockListIndex(slab);
     744                 :     }
     745 CBC     1918119 : }
     746                 : 
     747                 : /*
     748 ECB             :  * SlabRealloc
     749                 :  *      Change the allocated size of a chunk.
     750                 :  *
     751                 :  * As Slab is designed for allocating equally-sized chunks of memory, it can't
     752                 :  * do an actual chunk size change.  We try to be gentle and allow calls with
     753                 :  * exactly the same size, as in that case we can simply return the same
     754                 :  * chunk.  When the size differs, we throw an error.
     755                 :  *
     756                 :  * We could also allow requests with size < chunkSize.  That however seems
     757                 :  * rather pointless - Slab is meant for chunks of constant size, and moreover
     758                 :  * realloc is usually used to enlarge the chunk.
     759                 :  */
     760                 : void *
     761 UNC           0 : SlabRealloc(void *pointer, Size size)
     762 ECB             : {
     763 UNC           0 :     MemoryChunk *chunk = PointerGetMemoryChunk(pointer);
     764               0 :     SlabBlock  *block = MemoryChunkGetBlock(chunk);
     765                 :     SlabContext *slab;
     766                 : 
     767                 :     /*
     768                 :      * Try to verify that we have a sane block pointer: the block header
     769                 :      * should reference a slab context.  (We use a test-and-elog, not just
     770                 :      * Assert, because it seems highly likely that we're here in error in the
     771                 :      * first place.)
     772                 :      */
     773               0 :     if (!SlabBlockIsValid(block))
     774               0 :         elog(ERROR, "could not find block containing chunk %p", chunk);
     775               0 :     slab = block->slab;
     776                 : 
     777                 :     /* can't do actual realloc with slab, but let's try to be gentle */
     778 LBC           0 :     if (size == slab->chunkSize)
     779 UIC           0 :         return pointer;
     780 ECB             : 
     781 LBC           0 :     elog(ERROR, "slab allocator does not support realloc()");
     782                 :     return NULL;                /* keep compiler quiet */
     783 ECB             : }
     784                 : 
     785                 : /*
     786                 :  * SlabGetChunkContext
     787                 :  *      Return the MemoryContext that 'pointer' belongs to.
     788                 :  */
     789                 : MemoryContext
     790 UNC           0 : SlabGetChunkContext(void *pointer)
     791                 : {
     792               0 :     MemoryChunk *chunk = PointerGetMemoryChunk(pointer);
     793               0 :     SlabBlock  *block = MemoryChunkGetBlock(chunk);
     794                 : 
     795               0 :     Assert(SlabBlockIsValid(block));
     796               0 :     return &block->slab->header;
     797                 : }
     798                 : 
     799                 : /*
     800                 :  * SlabGetChunkSpace
     801                 :  *      Given a currently-allocated chunk, determine the total space
     802                 :  *      it occupies (including all memory-allocation overhead).
     803                 :  */
     804                 : Size
     805               0 : SlabGetChunkSpace(void *pointer)
     806 ECB             : {
     807 UNC           0 :     MemoryChunk *chunk = PointerGetMemoryChunk(pointer);
     808               0 :     SlabBlock  *block = MemoryChunkGetBlock(chunk);
     809                 :     SlabContext *slab;
     810 ECB             : 
     811 UNC           0 :     Assert(SlabBlockIsValid(block));
     812               0 :     slab = block->slab;
     813                 : 
     814 UIC           0 :     return slab->fullChunkSize;
     815                 : }
     816 ECB             : 
     817                 : /*
     818                 :  * SlabIsEmpty
     819                 :  *      Is the slab empty of any allocated space?
     820                 :  */
     821                 : bool
     822 UIC           0 : SlabIsEmpty(MemoryContext context)
     823                 : {
     824 UNC           0 :     Assert(SlabIsValid((SlabContext *) context));
     825                 : 
     826               0 :     return (context->mem_allocated == 0);
     827                 : }
     828                 : 
     829                 : /*
     830                 :  * SlabStats
     831 ECB             :  *      Compute stats about memory consumption of a Slab context.
     832                 :  *
     833                 :  * printfunc: if not NULL, pass a human-readable stats string to this.
     834                 :  * passthru: pass this pointer through to printfunc.
     835                 :  * totals: if not NULL, add stats about this context into *totals.
     836                 :  * print_to_stderr: print stats to stderr if true, elog otherwise.
     837                 :  */
     838                 : void
     839 LBC           0 : SlabStats(MemoryContext context,
     840                 :           MemoryStatsPrintFunc printfunc, void *passthru,
     841 ECB             :           MemoryContextCounters *totals,
     842                 :           bool print_to_stderr)
     843                 : {
     844 UNC           0 :     SlabContext *slab = (SlabContext *) context;
     845 UIC           0 :     Size        nblocks = 0;
     846               0 :     Size        freechunks = 0;
     847                 :     Size        totalspace;
     848               0 :     Size        freespace = 0;
     849                 :     int         i;
     850                 : 
     851 UNC           0 :     Assert(SlabIsValid(slab));
     852 ECB             : 
     853                 :     /* Include context header in totalspace */
     854 UNC           0 :     totalspace = Slab_CONTEXT_HDRSZ(slab->chunksPerBlock);
     855                 : 
     856                 :     /* Add the space consumed by blocks in the emptyblocks list */
     857               0 :     totalspace += dclist_count(&slab->emptyblocks) * slab->blockSize;
     858                 : 
     859               0 :     for (i = 0; i < SLAB_BLOCKLIST_COUNT; i++)
     860                 :     {
     861                 :         dlist_iter  iter;
     862 ECB             : 
     863 UNC           0 :         dlist_foreach(iter, &slab->blocklist[i])
     864 EUB             :         {
     865 UIC           0 :             SlabBlock  *block = dlist_container(SlabBlock, node, iter.cur);
     866                 : 
     867               0 :             nblocks++;
     868               0 :             totalspace += slab->blockSize;
     869 LBC           0 :             freespace += slab->fullChunkSize * block->nfree;
     870               0 :             freechunks += block->nfree;
     871                 :         }
     872 ECB             :     }
     873                 : 
     874 LBC           0 :     if (printfunc)
     875 ECB             :     {
     876                 :         char        stats_string[200];
     877                 : 
     878                 :         /* XXX should we include free chunks on empty blocks? */
     879 UIC           0 :         snprintf(stats_string, sizeof(stats_string),
     880                 :                  "%zu total in %zu blocks; %u empty blocks; %zu free (%zu chunks); %zu used",
     881 UNC           0 :                  totalspace, nblocks, dclist_count(&slab->emptyblocks),
     882                 :                  freespace, freechunks, totalspace - freespace);
     883 UIC           0 :         printfunc(context, passthru, stats_string, print_to_stderr);
     884 ECB             :     }
     885                 : 
     886 UIC           0 :     if (totals)
     887                 :     {
     888               0 :         totals->nblocks += nblocks;
     889               0 :         totals->freechunks += freechunks;
     890               0 :         totals->totalspace += totalspace;
     891 LBC           0 :         totals->freespace += freespace;
     892                 :     }
     893 UIC           0 : }
     894 ECB             : 
     895                 : 
     896                 : #ifdef MEMORY_CONTEXT_CHECKING
     897                 : 
     898                 : /*
     899                 :  * SlabCheck
     900                 :  *      Walk through all blocks looking for inconsistencies.
     901                 :  *
     902                 :  * NOTE: report errors as WARNING, *not* ERROR or FATAL.  Otherwise you'll
     903                 :  * find yourself in an infinite loop when trouble occurs, because this
     904                 :  * routine will be entered again when elog cleanup tries to release memory!
     905                 :  */
     906                 : void
     907 GIC        1390 : SlabCheck(MemoryContext context)
     908                 : {
     909 GNC        1390 :     SlabContext *slab = (SlabContext *) context;
     910                 :     int         i;
     911            1390 :     int         nblocks = 0;
     912 GIC        1390 :     const char *name = slab->header.name;
     913                 :     dlist_iter  iter;
     914 ECB             : 
     915 GNC        1390 :     Assert(SlabIsValid(slab));
     916 GIC        1390 :     Assert(slab->chunksPerBlock > 0);
     917                 : 
     918                 :     /*
     919                 :      * Have a look at the empty blocks.  These should have all their chunks
     920                 :      * marked as free.  Ensure that's the case.
     921                 :      */
     922 GNC        2098 :     dclist_foreach(iter, &slab->emptyblocks)
     923                 :     {
     924             708 :         SlabBlock  *block = dlist_container(SlabBlock, node, iter.cur);
     925                 : 
     926             708 :         if (block->nfree != slab->chunksPerBlock)
     927 UNC           0 :             elog(WARNING, "problem in slab %s: empty block %p should have %d free chunks but has %d chunks free",
     928                 :                  name, block, slab->chunksPerBlock, block->nfree);
     929                 :     }
     930                 : 
     931                 :     /* walk the non-empty block lists */
     932 GNC        5560 :     for (i = 0; i < SLAB_BLOCKLIST_COUNT; i++)
     933                 :     {
     934                 :         int         j,
     935 ECB             :                     nfree;
     936                 : 
     937                 :         /* walk all blocks on this blocklist */
     938 GNC        4222 :         dlist_foreach(iter, &slab->blocklist[i])
     939                 :         {
     940 CBC          52 :             SlabBlock  *block = dlist_container(SlabBlock, node, iter.cur);
     941                 :             MemoryChunk *cur_chunk;
     942                 : 
     943                 :             /*
     944                 :              * Make sure the number of free chunks (in the block header)
     945                 :              * matches the position in the blocklist.
     946                 :              */
     947 GNC          52 :             if (SlabBlocklistIndex(slab, block->nfree) != i)
     948 UNC           0 :                 elog(WARNING, "problem in slab %s: block %p is on blocklist %d but should be on blocklist %d",
     949                 :                      name, block, i, SlabBlocklistIndex(slab, block->nfree));
     950                 : 
     951                 :             /* make sure the block is not empty */
     952 GNC          52 :             if (block->nfree >= slab->chunksPerBlock)
     953 UNC           0 :                 elog(WARNING, "problem in slab %s: empty block %p incorrectly stored on blocklist element %d",
     954                 :                      name, block, i);
     955                 : 
     956                 :             /* make sure the slab pointer correctly points to this context */
     957 GNC          52 :             if (block->slab != slab)
     958 UNC           0 :                 elog(WARNING, "problem in slab %s: bogus slab link in block %p",
     959                 :                      name, block);
     960                 : 
     961                 :             /* reset the array of free chunks for this block */
     962 GNC          52 :             memset(slab->isChunkFree, 0, (slab->chunksPerBlock * sizeof(bool)));
     963              52 :             nfree = 0;
     964                 : 
     965                 :             /* walk through the block's free list chunks */
     966              52 :             cur_chunk = block->freehead;
     967             633 :             while (cur_chunk != NULL)
     968                 :             {
     969             581 :                 int         chunkidx = SlabChunkIndex(slab, block, cur_chunk);
     970                 : 
     971                 :                 /*
     972                 :                  * Ensure the free list link points to something on the block
     973                 :                  * at an address aligned according to the full chunk size.
     974                 :                  */
     975             581 :                 if (cur_chunk < SlabBlockGetChunk(slab, block, 0) ||
     976             581 :                     cur_chunk > SlabBlockGetChunk(slab, block, slab->chunksPerBlock - 1) ||
     977             581 :                     SlabChunkMod(slab, block, cur_chunk) != 0)
     978 UNC           0 :                     elog(WARNING, "problem in slab %s: bogus free list link %p in block %p",
     979                 :                          name, cur_chunk, block);
     980                 : 
     981                 :                 /* count the chunk and mark it free on the free chunk array */
     982 GNC         581 :                 nfree++;
     983             581 :                 slab->isChunkFree[chunkidx] = true;
     984                 : 
     985                 :                 /* read pointer of the next free chunk */
     986                 :                 VALGRIND_MAKE_MEM_DEFINED(MemoryChunkGetPointer(cur_chunk), sizeof(MemoryChunk *));
     987             581 :                 cur_chunk = *(MemoryChunk **) SlabChunkGetPointer(cur_chunk);
     988                 :             }
     989                 : 
     990                 :             /* check that the unused pointer matches what nunused claims */
     991              52 :             if (SlabBlockGetChunk(slab, block, slab->chunksPerBlock - block->nunused) !=
     992              52 :                 block->unused)
     993 UNC           0 :                 elog(WARNING, "problem in slab %s: mismatch detected between nunused chunks and unused pointer in block %p",
     994                 :                      name, block);
     995                 : 
     996                 :             /*
     997                 :              * count the remaining free chunks that have yet to make it onto
     998                 :              * the block's free list.
     999 ECB             :              */
    1000 GNC          52 :             cur_chunk = block->unused;
    1001            1672 :             for (j = 0; j < block->nunused; j++)
    1002                 :             {
    1003            1620 :                 int         chunkidx = SlabChunkIndex(slab, block, cur_chunk);
    1004                 : 
    1005                 : 
    1006                 :                 /* count the chunk as free and mark it as so in the array */
    1007 GIC        1620 :                 nfree++;
    1008 GNC        1620 :                 if (chunkidx < slab->chunksPerBlock)
    1009            1620 :                     slab->isChunkFree[chunkidx] = true;
    1010                 : 
    1011                 :                 /* move forward 1 chunk */
    1012            1620 :                 cur_chunk = (MemoryChunk *) (((char *) cur_chunk) + slab->fullChunkSize);
    1013                 :             }
    1014                 : 
    1015 GIC        2542 :             for (j = 0; j < slab->chunksPerBlock; j++)
    1016                 :             {
    1017 GNC        2490 :                 if (!slab->isChunkFree[j])
    1018 EUB             :                 {
    1019 GNC         289 :                     MemoryChunk *chunk = SlabBlockGetChunk(slab, block, j);
    1020             289 :                     SlabBlock  *chunkblock = (SlabBlock *) MemoryChunkGetBlock(chunk);
    1021                 : 
    1022                 :                     /*
    1023                 :                      * check the chunk's blockoffset correctly points back to
    1024                 :                      * the block
    1025                 :                      */
    1026             289 :                     if (chunkblock != block)
    1027 UIC           0 :                         elog(WARNING, "problem in slab %s: bogus block link in block %p, chunk %p",
    1028                 :                              name, block, chunk);
    1029                 : 
    1030                 :                     /* check the sentinel byte is intact */
    1031 GNC         289 :                     Assert(slab->chunkSize < (slab->fullChunkSize - Slab_CHUNKHDRSZ));
    1032             289 :                     if (!sentinel_ok(chunk, Slab_CHUNKHDRSZ + slab->chunkSize))
    1033 UNC           0 :                         elog(WARNING, "problem in slab %s: detected write past chunk end in block %p, chunk %p",
    1034 EUB             :                              name, block, chunk);
    1035                 :                 }
    1036                 :             }
    1037                 : 
    1038                 :             /*
    1039                 :              * Make sure we got the expected number of free chunks (as tracked
    1040                 :              * in the block header).
    1041                 :              */
    1042 GIC          52 :             if (nfree != block->nfree)
    1043 UNC           0 :                 elog(WARNING, "problem in slab %s: nfree in block %p is %d but %d chunk were found as free",
    1044                 :                      name, block, block->nfree, nfree);
    1045                 : 
    1046 GNC          52 :             nblocks++;
    1047 EUB             :         }
    1048                 :     }
    1049                 : 
    1050                 :     /* the stored empty blocks are tracked in mem_allocated too */
    1051 GNC        1390 :     nblocks += dclist_count(&slab->emptyblocks);
    1052                 : 
    1053            1390 :     Assert(nblocks * slab->blockSize == context->mem_allocated);
    1054 GIC        1390 : }
    1055 EUB             : 
    1056                 : #endif                          /* MEMORY_CONTEXT_CHECKING */
        

Generated by: LCOV version v1.16-55-g56c0a2a