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 17:13:01 Functions: 64.3 % 14 9 3 2 5 4 4 6 1 3
Baseline: 15 Line coverage date bins:
Baseline Date: 2023-04-08 15:09:40 (60,120] days: 89.5 % 153 137 16 137 2
Legend: Lines: hit not hit (120,180] days: 57.1 % 7 4 3 4
(180,240] days: 55.0 % 40 22 18 22 1 2
(240..) days: 55.7 % 61 34 8 19 28 6 8 58
Function coverage date bins:
(60,120] days: 100.0 % 3 3 3
(180,240] days: 25.0 % 4 1 3 1
(240..) days: 38.5 % 13 5 2 5 1 5

 Age         Owner                  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
  110 drowley                   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
 2232 andres                    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");
  223 drowley                   333 GNC        1646 :     Assert(MAXALIGN(chunkSize) <= MEMORYCHUNK_MAX_VALUE);
                                334                 : 
  110 drowley                   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                 :      */
  110 drowley                   339 GNC        1646 :     if (chunkSize < sizeof(MemoryChunk *))
  110 drowley                   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 */
  214 drowley                   345 GNC        1646 :     fullChunkSize = Slab_CHUNKHDRSZ + MAXALIGN(chunkSize + 1);
                                346                 : #else
                                347                 :     fullChunkSize = Slab_CHUNKHDRSZ + MAXALIGN(chunkSize);
  214 drowley                   348 ECB             : #endif
                                349                 : 
                                350                 :     /* compute the number of chunks that will fit on each block */
  222 drowley                   351 GNC        1646 :     chunksPerBlock = (blockSize - Slab_BLOCKHDRSZ) / fullChunkSize;
                                352                 : 
                                353                 :     /* Make sure the block can store at least one chunk. */
  110                           354            1646 :     if (chunksPerBlock == 0)
  110 drowley                   355 UNC           0 :         elog(ERROR, "block size %zu for slab is too small for %zu-byte chunks",
                                356                 :              blockSize, chunkSize);
                                357                 : 
                                358                 : 
                                359                 : 
  110 drowley                   360 GNC        1646 :     slab = (SlabContext *) malloc(Slab_CONTEXT_HDRSZ(chunksPerBlock));
 1943 tgl                       361 CBC        1646 :     if (slab == NULL)
                                362                 :     {
 1943 tgl                       363 LBC           0 :         MemoryContextStats(TopMemoryContext);
 1943 tgl                       364 UIC           0 :         ereport(ERROR,
 1943 tgl                       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 */
 2232 andres                    377 GIC        1646 :     slab->chunkSize = chunkSize;
                                378            1646 :     slab->fullChunkSize = fullChunkSize;
 1943 tgl                       379 CBC        1646 :     slab->blockSize = blockSize;
 2232 andres                    380 GIC        1646 :     slab->chunksPerBlock = chunksPerBlock;
  110 drowley                   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 */
 1943 tgl                       410 GIC        1646 :     MemoryContextCreate((MemoryContext) slab,
                                411                 :                         T_SlabContext,
                                412                 :                         MCTX_SLAB_ID,
 1943 tgl                       413 ECB             :                         parent,
                                414                 :                         name);
                                415                 : 
 1943 tgl                       416 GIC        1646 :     return (MemoryContext) slab;
 2232 andres                    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
 2232 andres                    427 GIC        1390 : SlabReset(MemoryContext context)
 2232 andres                    428 ECB             : {
  181 tgl                       429 GNC        1390 :     SlabContext *slab = (SlabContext *) context;
                                430                 :     dlist_mutable_iter miter;
                                431                 :     int         i;
 2232 andres                    432 ECB             : 
  163 peter                     433 GNC        1390 :     Assert(SlabIsValid(slab));
                                434                 : 
                                435                 : #ifdef MEMORY_CONTEXT_CHECKING
                                436                 :     /* Check for corruption and leaks before freeing */
 2232 andres                    437 GIC        1390 :     SlabCheck(context);
                                438                 : #endif
 2232 andres                    439 ECB             : 
                                440                 :     /* release any retained empty blocks */
  110 drowley                   441 GNC        2098 :     dclist_foreach_modify(miter, &slab->emptyblocks)
 2232 andres                    442 ECB             :     {
  110 drowley                   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])
 2232 andres                    458 ECB             :         {
 2232 andres                    459 GIC          52 :             SlabBlock  *block = dlist_container(SlabBlock, node, miter.cur);
 2232 andres                    460 ECB             : 
 2232 andres                    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);
 1116 jdavis                    467              52 :             context->mem_allocated -= slab->blockSize;
                                468                 :         }
                                469                 :     }
                                470                 : 
  110 drowley                   471 GNC        1390 :     slab->curBlocklistIndex = 0;
                                472                 : 
 1116 jdavis                    473 CBC        1390 :     Assert(context->mem_allocated == 0);
 2232 andres                    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 */
 1943 tgl                       486 CBC        1390 :     free(context);
 2232 andres                    487 GIC        1390 : }
                                488                 : 
                                489                 : /*
                                490                 :  * SlabAlloc
                                491                 :  *      Returns a pointer to allocated memory of given size or NULL if
 2232 andres                    492 ECB             :  *      request could not be completed; memory is added to the slab.
 2232 andres                    493 EUB             :  */
                                494                 : void *
 2232 andres                    495 GIC     1918429 : SlabAlloc(MemoryContext context, Size size)
                                496                 : {
  181 tgl                       497 GNC     1918429 :     SlabContext *slab = (SlabContext *) context;
 2232 andres                    498 ECB             :     SlabBlock  *block;
                                499                 :     MemoryChunk *chunk;
                                500                 : 
  163 peter                     501 GNC     1918429 :     Assert(SlabIsValid(slab));
                                502                 : 
                                503                 :     /* sanity check that this is pointing to a valid blocklist */
  110 drowley                   504         1918429 :     Assert(slab->curBlocklistIndex >= 0);
                                505         1918429 :     Assert(slab->curBlocklistIndex <= SlabBlocklistIndex(slab, slab->chunksPerBlock));
                                506                 : 
 2232 andres                    507 ECB             :     /* make sure we only allow correct request size */
  110 drowley                   508 GNC     1918429 :     if (unlikely(size != slab->chunkSize))
 2232 andres                    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                 :      */
  110 drowley                   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)
  110 drowley                   526 ECB             :         {
  110 drowley                   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))
  110 drowley                   545 UNC           0 :                 return NULL;
                                546                 : 
  110 drowley                   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];
 2232 andres                    562 ECB             : 
                                563                 :         /* this better be empty.  We just added a block thinking it was */
  110 drowley                   564 GNC       30642 :         Assert(dlist_is_empty(blocklist));
 2232 andres                    565 ECB             : 
  110 drowley                   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                 :     }
 2232 andres                    604 ECB             : 
                                605                 :     /*
                                606                 :      * Check that the chunk pointer is actually somewhere on the block and is
                                607                 :      * aligned as expected.
                                608                 :      */
  110 drowley                   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);
 2232 andres                    612 ECB             : 
                                613                 :     /* Prepare to initialize the chunk header. */
                                614                 :     VALGRIND_MAKE_MEM_UNDEFINED(chunk, Slab_CHUNKHDRSZ);
                                615                 : 
  223 drowley                   616 GNC     1918429 :     MemoryChunkSetHdrMask(chunk, block, MAXALIGN(slab->chunkSize),
                                617                 :                           MCTX_SLAB_ID);
 2232 andres                    618 ECB             : #ifdef MEMORY_CONTEXT_CHECKING
                                619                 :     /* slab mark to catch clobber of "unused" space */
  214 drowley                   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));
 2232 andres                    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                 : 
  223 drowley                   633 GNC     1918429 :     return MemoryChunkGetPointer(chunk);
                                634                 : }
                                635                 : 
 2232 andres                    636 ECB             : /*
                                637                 :  * SlabFree
                                638                 :  *      Frees allocated memory; memory is removed from the slab.
                                639                 :  */
                                640                 : void
  223 drowley                   641 GNC     1918119 : SlabFree(void *pointer)
 2232 andres                    642 ECB             : {
  223 drowley                   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                 :      */
  163 peter                     654         1918119 :     Assert(SlabBlockIsValid(block));
  181 tgl                       655         1918119 :     slab = block->slab;
                                656                 : 
                                657                 : #ifdef MEMORY_CONTEXT_CHECKING
 2232 andres                    658 ECB             :     /* Test for someone scribbling on unused space in chunk */
  214 drowley                   659 GNC     1918119 :     Assert(slab->chunkSize < (slab->fullChunkSize - Slab_CHUNKHDRSZ));
                                660         1918119 :     if (!sentinel_ok(pointer, slab->chunkSize))
  214 drowley                   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 */
  110 drowley                   666 GNC     1918119 :     *(MemoryChunk **) pointer = block->freehead;
                                667         1918119 :     block->freehead = chunk;
                                668                 : 
 2232 andres                    669 GIC     1918119 :     block->nfree++;
                                670                 : 
                                671         1918119 :     Assert(block->nfree > 0);
                                672         1918119 :     Assert(block->nfree <= slab->chunksPerBlock);
 2232 andres                    673 ECB             : 
                                674                 : #ifdef CLOBBER_FREED_MEMORY
                                675                 :     /* don't wipe the free list MemoryChunk pointer stored in the chunk */
  110 drowley                   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                 : 
 2232 andres                    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                 :      */
  110 drowley                   687 GNC     1918119 :     if (unlikely(curBlocklistIdx != newBlocklistIdx))
 2232 andres                    688 ECB             :     {
                                689                 :         /* do the move */
  110 drowley                   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                 :          */
  108                           700           49336 :         if (slab->curBlocklistIndex >= curBlocklistIdx)
 2232 andres                    701 ECB             :         {
  110 drowley                   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                 :     }
 2232 andres                    745 CBC     1918119 : }
                                746                 : 
                                747                 : /*
 2232 andres                    748 ECB             :  * SlabRealloc
                                749                 :  *      Change the allocated size of a chunk.
                                750                 :  *
 2223 tgl                       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.
 2232 andres                    755                 :  *
 2223 tgl                       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.
 2232 andres                    759                 :  */
                                760                 : void *
  223 drowley                   761 UNC           0 : SlabRealloc(void *pointer, Size size)
 2232 andres                    762 ECB             : {
  223 drowley                   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                 :      */
  181 tgl                       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 */
 2232 andres                    778 LBC           0 :     if (size == slab->chunkSize)
 2232 andres                    779 UIC           0 :         return pointer;
 2232 andres                    780 ECB             : 
 2232 andres                    781 LBC           0 :     elog(ERROR, "slab allocator does not support realloc()");
                                782                 :     return NULL;                /* keep compiler quiet */
 2232 andres                    783 ECB             : }
                                784                 : 
                                785                 : /*
                                786                 :  * SlabGetChunkContext
                                787                 :  *      Return the MemoryContext that 'pointer' belongs to.
                                788                 :  */
                                789                 : MemoryContext
  223 drowley                   790 UNC           0 : SlabGetChunkContext(void *pointer)
                                791                 : {
                                792               0 :     MemoryChunk *chunk = PointerGetMemoryChunk(pointer);
                                793               0 :     SlabBlock  *block = MemoryChunkGetBlock(chunk);
                                794                 : 
  163 peter                     795               0 :     Assert(SlabBlockIsValid(block));
  181 tgl                       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
  223 drowley                   805               0 : SlabGetChunkSpace(void *pointer)
 2232 andres                    806 ECB             : {
  223 drowley                   807 UNC           0 :     MemoryChunk *chunk = PointerGetMemoryChunk(pointer);
                                808               0 :     SlabBlock  *block = MemoryChunkGetBlock(chunk);
                                809                 :     SlabContext *slab;
 2232 andres                    810 ECB             : 
  163 peter                     811 UNC           0 :     Assert(SlabBlockIsValid(block));
  181 tgl                       812               0 :     slab = block->slab;
                                813                 : 
 2232 andres                    814 UIC           0 :     return slab->fullChunkSize;
                                815                 : }
 2232 andres                    816 ECB             : 
                                817                 : /*
                                818                 :  * SlabIsEmpty
                                819                 :  *      Is the slab empty of any allocated space?
                                820                 :  */
                                821                 : bool
 2232 andres                    822 UIC           0 : SlabIsEmpty(MemoryContext context)
                                823                 : {
  110 drowley                   824 UNC           0 :     Assert(SlabIsValid((SlabContext *) context));
                                825                 : 
                                826               0 :     return (context->mem_allocated == 0);
                                827                 : }
                                828                 : 
                                829                 : /*
                                830                 :  * SlabStats
 1943 tgl                       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
 1839 tgl                       839 LBC           0 : SlabStats(MemoryContext context,
                                840                 :           MemoryStatsPrintFunc printfunc, void *passthru,
  733 fujii                     841 ECB             :           MemoryContextCounters *totals,
                                842                 :           bool print_to_stderr)
                                843                 : {
  181 tgl                       844 UNC           0 :     SlabContext *slab = (SlabContext *) context;
 2232 andres                    845 UIC           0 :     Size        nblocks = 0;
                                846               0 :     Size        freechunks = 0;
                                847                 :     Size        totalspace;
                                848               0 :     Size        freespace = 0;
                                849                 :     int         i;
                                850                 : 
  163 peter                     851 UNC           0 :     Assert(SlabIsValid(slab));
  181 tgl                       852 ECB             : 
                                853                 :     /* Include context header in totalspace */
  110 drowley                   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;
 2232 andres                    862 ECB             : 
  110 drowley                   863 UNC           0 :         dlist_foreach(iter, &slab->blocklist[i])
 2232 andres                    864 EUB             :         {
 2232 andres                    865 UIC           0 :             SlabBlock  *block = dlist_container(SlabBlock, node, iter.cur);
                                866                 : 
                                867               0 :             nblocks++;
                                868               0 :             totalspace += slab->blockSize;
 2232 andres                    869 LBC           0 :             freespace += slab->fullChunkSize * block->nfree;
                                870               0 :             freechunks += block->nfree;
                                871                 :         }
 2232 andres                    872 ECB             :     }
                                873                 : 
 1839 tgl                       874 LBC           0 :     if (printfunc)
 2232 andres                    875 ECB             :     {
                                876                 :         char        stats_string[200];
                                877                 : 
                                878                 :         /* XXX should we include free chunks on empty blocks? */
 1839 tgl                       879 UIC           0 :         snprintf(stats_string, sizeof(stats_string),
                                880                 :                  "%zu total in %zu blocks; %u empty blocks; %zu free (%zu chunks); %zu used",
  110 drowley                   881 UNC           0 :                  totalspace, nblocks, dclist_count(&slab->emptyblocks),
                                882                 :                  freespace, freechunks, totalspace - freespace);
  733 fujii                     883 UIC           0 :         printfunc(context, passthru, stats_string, print_to_stderr);
 2232 andres                    884 ECB             :     }
                                885                 : 
 2232 andres                    886 UIC           0 :     if (totals)
                                887                 :     {
                                888               0 :         totals->nblocks += nblocks;
                                889               0 :         totals->freechunks += freechunks;
                                890               0 :         totals->totalspace += totalspace;
 2232 andres                    891 LBC           0 :         totals->freespace += freespace;
                                892                 :     }
 2232 andres                    893 UIC           0 : }
 2232 andres                    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
 2232 andres                    907 GIC        1390 : SlabCheck(MemoryContext context)
                                908                 : {
  181 tgl                       909 GNC        1390 :     SlabContext *slab = (SlabContext *) context;
                                910                 :     int         i;
  110 drowley                   911            1390 :     int         nblocks = 0;
 1943 tgl                       912 GIC        1390 :     const char *name = slab->header.name;
                                913                 :     dlist_iter  iter;
 2232 andres                    914 ECB             : 
  163 peter                     915 GNC        1390 :     Assert(SlabIsValid(slab));
 2232 andres                    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                 :      */
  110 drowley                   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)
  110 drowley                   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 */
  110 drowley                   932 GNC        5560 :     for (i = 0; i < SLAB_BLOCKLIST_COUNT; i++)
                                933                 :     {
                                934                 :         int         j,
 2232 andres                    935 ECB             :                     nfree;
                                936                 : 
                                937                 :         /* walk all blocks on this blocklist */
  110 drowley                   938 GNC        4222 :         dlist_foreach(iter, &slab->blocklist[i])
                                939                 :         {
 2232 andres                    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                 :              */
  110 drowley                   947 GNC          52 :             if (SlabBlocklistIndex(slab, block->nfree) != i)
  110 drowley                   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 */
  110 drowley                   952 GNC          52 :             if (block->nfree >= slab->chunksPerBlock)
  110 drowley                   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 */
  223 drowley                   957 GNC          52 :             if (block->slab != slab)
  223 drowley                   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 */
  110 drowley                   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)
  110 drowley                   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 */
  110 drowley                   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)
  110 drowley                   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.
 2232 andres                    999 ECB             :              */
  110 drowley                  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 */
 2232 andres                   1007 GIC        1620 :                 nfree++;
  110 drowley                  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                 : 
 2232 andres                   1015 GIC        2542 :             for (j = 0; j < slab->chunksPerBlock; j++)
                               1016                 :             {
  110 drowley                  1017 GNC        2490 :                 if (!slab->isChunkFree[j])
 2232 andres                   1018 EUB             :                 {
  223 drowley                  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)
 2232 andres                   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 */
  214 drowley                  1031 GNC         289 :                     Assert(slab->chunkSize < (slab->fullChunkSize - Slab_CHUNKHDRSZ));
                               1032             289 :                     if (!sentinel_ok(chunk, Slab_CHUNKHDRSZ + slab->chunkSize))
  214 drowley                  1033 UNC           0 :                         elog(WARNING, "problem in slab %s: detected write past chunk end in block %p, chunk %p",
  214 drowley                  1034 EUB             :                              name, block, chunk);
                               1035                 :                 }
 2232 andres                   1036                 :             }
                               1037                 : 
                               1038                 :             /*
                               1039                 :              * Make sure we got the expected number of free chunks (as tracked
                               1040                 :              * in the block header).
                               1041                 :              */
 2232 andres                   1042 GIC          52 :             if (nfree != block->nfree)
  110 drowley                  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                 : 
  110 drowley                  1046 GNC          52 :             nblocks++;
 2232 andres                   1047 EUB             :         }
                               1048                 :     }
 1286 tomas.vondra             1049                 : 
                               1050                 :     /* the stored empty blocks are tracked in mem_allocated too */
  110 drowley                  1051 GNC        1390 :     nblocks += dclist_count(&slab->emptyblocks);
                               1052                 : 
                               1053            1390 :     Assert(nblocks * slab->blockSize == context->mem_allocated);
 2232 andres                   1054 GIC        1390 : }
 2232 andres                   1055 EUB             : 
 2118 tgl                      1056                 : #endif                          /* MEMORY_CONTEXT_CHECKING */
        

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