LCOV - code coverage report
Current view: top level - src/base - memory_pool.cc (source / functions) Hit Total Coverage
Test: vlink Lines: 318 323 98.5 %
Date: 2026-06-27 19:56:04 Functions: 34 35 97.1 %
Branches: 172 238 72.3 %

           Branch data     Line data    Source code
       1                 :            : /*
       2                 :            :  * Copyright (C) 2026 by Thun Lu. All rights reserved.
       3                 :            :  * Author: Thun Lu <thun.lu@zohomail.cn>
       4                 :            :  * Repo:   https://github.com/thun-res/vlink
       5                 :            :  *  _    __   __      _           __
       6                 :            :  * | |  / /  / /     (_) ____    / /__
       7                 :            :  * | | / /  / /     / / / __ \  / //_/
       8                 :            :  * | |/ /  / /___  / / / / / / / ,<
       9                 :            :  * |___/  /_____/ /_/ /_/ /_/ /_/|_|
      10                 :            :  *
      11                 :            :  * Licensed under the Apache License, Version 2.0 (the "License");
      12                 :            :  * you may not use this file except in compliance with the License.
      13                 :            :  * You may obtain a copy of the License at
      14                 :            :  *
      15                 :            :  *     http://www.apache.org/licenses/LICENSE-2.0
      16                 :            :  *
      17                 :            :  * Unless required by applicable law or agreed to in writing, software
      18                 :            :  * distributed under the License is distributed on an "AS IS" BASIS,
      19                 :            :  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      20                 :            :  * See the License for the specific language governing permissions and
      21                 :            :  * limitations under the License.
      22                 :            :  */
      23                 :            : 
      24                 :            : #include "./base/memory_pool.h"
      25                 :            : 
      26                 :            : #include <algorithm>
      27                 :            : #include <atomic>
      28                 :            : #include <charconv>
      29                 :            : #include <cstddef>
      30                 :            : #include <cstdint>
      31                 :            : #include <exception>
      32                 :            : #include <new>
      33                 :            : #include <string>
      34                 :            : #include <system_error>
      35                 :            : #include <thread>
      36                 :            : #include <utility>
      37                 :            : #include <vector>
      38                 :            : 
      39                 :            : #include "./base/logger.h"
      40                 :            : #include "./base/spin_lock.h"
      41                 :            : #include "./base/utils.h"
      42                 :            : 
      43                 :            : #define MEMORY_POOL_NERVER_DELETE 0
      44                 :            : 
      45                 :            : namespace vlink {
      46                 :            : 
      47                 :            : static constexpr int kMinMemoryLevel = 0;
      48                 :            : static constexpr int kMaxMemoryLevel = 9;
      49                 :            : static constexpr int kDefaultMemoryLevel = 3;
      50                 :            : static constexpr size_t kMaxTierCount = 20U;
      51                 :            : static constexpr size_t kMaxLevelCount = 10U;
      52                 :            : static constexpr size_t kInitialBlocksPerChunk = 1U;
      53                 :            : static constexpr size_t kInitialChunksReserve = 16U;
      54                 :            : static constexpr size_t kInitialChunkBytesTarget = 64U * 1024U;
      55                 :            : 
      56                 :            : // clang-format off
      57                 :            : static constexpr MemoryPool::Tier kDefaultTierTable[kMaxLevelCount][kMaxTierCount] = {
      58                 :            :     // L0 ~ 0 MiB. (bypass; all entries are sentinels)
      59                 :            :     {
      60                 :            :         {32U, 0U},
      61                 :            :         {64U, 0U},
      62                 :            :         {128U, 0U},
      63                 :            :         {256U, 0U},
      64                 :            :         {512U, 0U},
      65                 :            :         {1U * 1024U, 0U},
      66                 :            :         {2U * 1024U, 0U},
      67                 :            :         {4U * 1024U, 0U},
      68                 :            :         {8U * 1024U, 0U},
      69                 :            :         {16U * 1024U, 0U},
      70                 :            :         {32U * 1024U, 0U},
      71                 :            :         {64U * 1024U, 0U},
      72                 :            :         {128U * 1024U, 0U},
      73                 :            :         {256U * 1024U, 0U},
      74                 :            :         {512U * 1024U, 0U},
      75                 :            :         {1U * 1024U * 1024U, 0U},
      76                 :            :         {4U * 1024U * 1024U, 0U},
      77                 :            :         {8U * 1024U * 1024U, 0U},
      78                 :            :         {16U * 1024U * 1024U, 0U},
      79                 :            :     },
      80                 :            :     // L1 ~ 4 MiB.
      81                 :            :     {
      82                 :            :         {32U, 8U * 1024U},
      83                 :            :         {64U, 4U * 1024U},
      84                 :            :         {128U, 2U * 1024U},
      85                 :            :         {256U, 1U * 1024U},
      86                 :            :         {512U, 512U},
      87                 :            :         {1U * 1024U, 256U},
      88                 :            :         {2U * 1024U, 128U},
      89                 :            :         {4U * 1024U, 64U},
      90                 :            :         {8U * 1024U, 32U},
      91                 :            :         {16U * 1024U, 16U},
      92                 :            :         {32U * 1024U, 8U},
      93                 :            :         {64U * 1024U, 4U},
      94                 :            :         {128U * 1024U, 2U},
      95                 :            :         {256U * 1024U, 1U},
      96                 :            :         {512U * 1024U, 1U},
      97                 :            :         {1U * 1024U * 1024U, 0U},
      98                 :            :         {4U * 1024U * 1024U, 0U},
      99                 :            :         {8U * 1024U * 1024U, 0U},
     100                 :            :         {16U * 1024U * 1024U, 0U},
     101                 :            :     },
     102                 :            :     // L2 ~ 8.5 MiB.
     103                 :            :     {
     104                 :            :         {32U, 16U * 1024U},
     105                 :            :         {64U, 8U * 1024U},
     106                 :            :         {128U, 4U * 1024U},
     107                 :            :         {256U, 2U * 1024U},
     108                 :            :         {512U, 1U * 1024U},
     109                 :            :         {1U * 1024U, 512U},
     110                 :            :         {2U * 1024U, 256U},
     111                 :            :         {4U * 1024U, 128U},
     112                 :            :         {8U * 1024U, 64U},
     113                 :            :         {16U * 1024U, 32U},
     114                 :            :         {32U * 1024U, 16U},
     115                 :            :         {64U * 1024U, 8U},
     116                 :            :         {128U * 1024U, 4U},
     117                 :            :         {256U * 1024U, 2U},
     118                 :            :         {512U * 1024U, 1U},
     119                 :            :         {1U * 1024U * 1024U, 1U},
     120                 :            :         {4U * 1024U * 1024U, 0U},
     121                 :            :         {8U * 1024U * 1024U, 0U},
     122                 :            :         {16U * 1024U * 1024U, 0U},
     123                 :            :     },
     124                 :            :     // L3 ~ 16 MiB. (Default)
     125                 :            :     {
     126                 :            :         {32U, 32U * 1024U},
     127                 :            :         {64U, 16U * 1024U},
     128                 :            :         {128U, 8U * 1024U},
     129                 :            :         {256U, 4U * 1024U},
     130                 :            :         {512U, 2U * 1024U},
     131                 :            :         {1U * 1024U, 1U * 1024U},
     132                 :            :         {2U * 1024U, 512U},
     133                 :            :         {4U * 1024U, 256U},
     134                 :            :         {8U * 1024U, 128U},
     135                 :            :         {16U * 1024U, 64U},
     136                 :            :         {32U * 1024U, 32U},
     137                 :            :         {64U * 1024U, 16U},
     138                 :            :         {128U * 1024U, 8U},
     139                 :            :         {256U * 1024U, 4U},
     140                 :            :         {512U * 1024U, 2U},
     141                 :            :         {1U * 1024U * 1024U, 1U},
     142                 :            :         {4U * 1024U * 1024U, 0U},
     143                 :            :         {8U * 1024U * 1024U, 0U},
     144                 :            :         {16U * 1024U * 1024U, 0U},
     145                 :            :     },
     146                 :            :     // L4 ~ 42 MiB.
     147                 :            :     {
     148                 :            :         {32U, 64U * 1024U},
     149                 :            :         {64U, 32U * 1024U},
     150                 :            :         {128U, 16U * 1024U},
     151                 :            :         {256U, 8U * 1024U},
     152                 :            :         {512U, 4U * 1024U},
     153                 :            :         {1U * 1024U, 2U * 1024U},
     154                 :            :         {2U * 1024U, 1U * 1024U},
     155                 :            :         {4U * 1024U, 512U},
     156                 :            :         {8U * 1024U, 256U},
     157                 :            :         {16U * 1024U, 128U},
     158                 :            :         {32U * 1024U, 64U},
     159                 :            :         {64U * 1024U, 32U},
     160                 :            :         {128U * 1024U, 16U},
     161                 :            :         {256U * 1024U, 8U},
     162                 :            :         {512U * 1024U, 4U},
     163                 :            :         {1U * 1024U * 1024U, 4U},
     164                 :            :         {4U * 1024U * 1024U, 2U},
     165                 :            :         {8U * 1024U * 1024U, 0U},
     166                 :            :         {16U * 1024U * 1024U, 0U},
     167                 :            :     },
     168                 :            :     // L5 ~ 92 MiB.
     169                 :            :     {
     170                 :            :         {32U, 128U * 1024U},
     171                 :            :         {64U, 64U * 1024U},
     172                 :            :         {128U, 32U * 1024U},
     173                 :            :         {256U, 16U * 1024U},
     174                 :            :         {512U, 8U * 1024U},
     175                 :            :         {1U * 1024U, 4U * 1024U},
     176                 :            :         {2U * 1024U, 2U * 1024U},
     177                 :            :         {4U * 1024U, 1U * 1024U},
     178                 :            :         {8U * 1024U, 512U},
     179                 :            :         {16U * 1024U, 256U},
     180                 :            :         {32U * 1024U, 128U},
     181                 :            :         {64U * 1024U, 64U},
     182                 :            :         {128U * 1024U, 32U},
     183                 :            :         {256U * 1024U, 16U},
     184                 :            :         {512U * 1024U, 8U},
     185                 :            :         {1U * 1024U * 1024U, 8U},
     186                 :            :         {4U * 1024U * 1024U, 4U},
     187                 :            :         {8U * 1024U * 1024U, 1U},
     188                 :            :         {16U * 1024U * 1024U, 0U},
     189                 :            :     },
     190                 :            :     // L6 ~ 200 MiB.
     191                 :            :     {
     192                 :            :         {32U, 256U * 1024U},
     193                 :            :         {64U, 128U * 1024U},
     194                 :            :         {128U, 64U * 1024U},
     195                 :            :         {256U, 32U * 1024U},
     196                 :            :         {512U, 16U * 1024U},
     197                 :            :         {1U * 1024U, 8U * 1024U},
     198                 :            :         {2U * 1024U, 4U * 1024U},
     199                 :            :         {4U * 1024U, 2U * 1024U},
     200                 :            :         {8U * 1024U, 1U * 1024U},
     201                 :            :         {16U * 1024U, 512U},
     202                 :            :         {32U * 1024U, 256U},
     203                 :            :         {64U * 1024U, 128U},
     204                 :            :         {128U * 1024U, 64U},
     205                 :            :         {256U * 1024U, 32U},
     206                 :            :         {512U * 1024U, 16U},
     207                 :            :         {1U * 1024U * 1024U, 16U},
     208                 :            :         {4U * 1024U * 1024U, 8U},
     209                 :            :         {8U * 1024U * 1024U, 2U},
     210                 :            :         {16U * 1024U * 1024U, 1U},
     211                 :            :     },
     212                 :            :     // L7 ~ 264 MiB.
     213                 :            :     {
     214                 :            :         {32U, 256U * 1024U},
     215                 :            :         {64U, 128U * 1024U},
     216                 :            :         {128U, 64U * 1024U},
     217                 :            :         {256U, 32U * 1024U},
     218                 :            :         {512U, 16U * 1024U},
     219                 :            :         {1U * 1024U, 8U * 1024U},
     220                 :            :         {2U * 1024U, 4U * 1024U},
     221                 :            :         {4U * 1024U, 2U * 1024U},
     222                 :            :         {8U * 1024U, 1U * 1024U},
     223                 :            :         {16U * 1024U, 512U},
     224                 :            :         {32U * 1024U, 256U},
     225                 :            :         {64U * 1024U, 128U},
     226                 :            :         {128U * 1024U, 64U},
     227                 :            :         {256U * 1024U, 32U},
     228                 :            :         {512U * 1024U, 16U},
     229                 :            :         {1U * 1024U * 1024U, 16U},
     230                 :            :         {4U * 1024U * 1024U, 16U},
     231                 :            :         {8U * 1024U * 1024U, 4U},
     232                 :            :         {16U * 1024U * 1024U, 2U},
     233                 :            :     },
     234                 :            :     // L8 ~ 528 MiB.
     235                 :            :     {
     236                 :            :         {32U, 512U * 1024U},
     237                 :            :         {64U, 256U * 1024U},
     238                 :            :         {128U, 128U * 1024U},
     239                 :            :         {256U, 64U * 1024U},
     240                 :            :         {512U, 32U * 1024U},
     241                 :            :         {1U * 1024U, 16U * 1024U},
     242                 :            :         {2U * 1024U, 8U * 1024U},
     243                 :            :         {4U * 1024U, 4U * 1024U},
     244                 :            :         {8U * 1024U, 2U * 1024U},
     245                 :            :         {16U * 1024U, 1U * 1024U},
     246                 :            :         {32U * 1024U, 512U},
     247                 :            :         {64U * 1024U, 256U},
     248                 :            :         {128U * 1024U, 128U},
     249                 :            :         {256U * 1024U, 64U},
     250                 :            :         {512U * 1024U, 32U},
     251                 :            :         {1U * 1024U * 1024U, 32U},
     252                 :            :         {4U * 1024U * 1024U, 32U},
     253                 :            :         {8U * 1024U * 1024U, 8U},
     254                 :            :         {16U * 1024U * 1024U, 4U},
     255                 :            :     },
     256                 :            :     // L9 ~ 656 MiB.
     257                 :            :     {
     258                 :            :         {32U, 512U * 1024U},
     259                 :            :         {64U, 256U * 1024U},
     260                 :            :         {128U, 128U * 1024U},
     261                 :            :         {256U, 64U * 1024U},
     262                 :            :         {512U, 32U * 1024U},
     263                 :            :         {1U * 1024U, 16U * 1024U},
     264                 :            :         {2U * 1024U, 8U * 1024U},
     265                 :            :         {4U * 1024U, 4U * 1024U},
     266                 :            :         {8U * 1024U, 2U * 1024U},
     267                 :            :         {16U * 1024U, 1U * 1024U},
     268                 :            :         {32U * 1024U, 512U},
     269                 :            :         {64U * 1024U, 256U},
     270                 :            :         {128U * 1024U, 128U},
     271                 :            :         {256U * 1024U, 64U},
     272                 :            :         {512U * 1024U, 32U},
     273                 :            :         {1U * 1024U * 1024U, 32U},
     274                 :            :         {4U * 1024U * 1024U, 32U},
     275                 :            :         {8U * 1024U * 1024U, 16U},
     276                 :            :         {16U * 1024U * 1024U, 8U},
     277                 :            :     }
     278                 :            : };
     279                 :            : // clang-format on
     280                 :            : 
     281                 :            : struct MemoryFreeNode final {
     282                 :            :   MemoryFreeNode* next{nullptr};
     283                 :            : };
     284                 :            : 
     285                 :            : struct MemoryChunk final {
     286                 :            :   void* ptr{nullptr};
     287                 :            :   size_t bytes{0};
     288                 :            : };
     289                 :            : 
     290                 :            : // NOLINTNEXTLINE(clang-analyzer-optin.performance.Padding)
     291                 :            : struct alignas(64) MemoryTierState final {
     292                 :            :   size_t max_size{0};
     293                 :            :   size_t block_size{0};
     294                 :            :   size_t blocks_per_chunk{0};
     295                 :            :   size_t next_chunk_blocks{0};
     296                 :            :   size_t initial_chunk_blocks{0};
     297                 :            : 
     298                 :            :   MemoryFreeNode* free_list_head{nullptr};
     299                 :            :   std::vector<MemoryChunk> chunks;
     300                 :            : 
     301                 :            :   alignas(64) std::atomic<bool> growing{false};
     302                 :            :   alignas(64) SpinLock mtx;
     303                 :            :   alignas(64) std::atomic<uint64_t> hit_count{0};
     304                 :            :   alignas(64) std::atomic<uint64_t> deallocate_count{0};
     305                 :            : 
     306                 :            :   alignas(64) std::atomic<uint64_t> chunk_count{0};
     307                 :            :   alignas(64) std::atomic<uint64_t> upstream_alloc_count{0};
     308                 :            :   std::atomic<uint64_t> upstream_alloc_bytes{0};
     309                 :            : };
     310                 :            : 
     311                 :            : struct alignas(64) MemoryAllocCounters final {
     312                 :            :   std::atomic<uint64_t> count{0};
     313                 :            :   std::atomic<uint64_t> bytes{0};
     314                 :            : };
     315                 :            : 
     316   [ +  #  +  # ]:      60264 : static constexpr bool is_power_of_two(size_t x) noexcept { return x != 0 && ((x & (x - 1U)) == 0U); }
     317                 :            : 
     318                 :       3280 : static constexpr size_t round_up(size_t value, size_t alignment) noexcept {
     319                 :       3280 :   return (value + alignment - 1U) & ~(alignment - 1U);
     320                 :            : }
     321                 :            : 
     322                 :            : static constexpr bool default_tier_table_well_formed() noexcept {
     323                 :            :   // NOLINTNEXTLINE(modernize-loop-convert)
     324                 :            :   for (size_t level = 0; level < kMaxLevelCount; ++level) {
     325                 :            :     size_t prev_max_size = 0U;
     326                 :            : 
     327                 :            :     for (size_t t = 0; t < kMaxTierCount; ++t) {
     328                 :            :       const size_t max_size = kDefaultTierTable[level][t].max_size;
     329                 :            : 
     330                 :            :       if (max_size == 0U) {
     331                 :            :         break;
     332                 :            :       }
     333                 :            : 
     334                 :            :       if (max_size < sizeof(MemoryFreeNode)) {
     335                 :            :         return false;
     336                 :            :       }
     337                 :            : 
     338                 :            :       if (t > 0U && max_size <= prev_max_size) {
     339                 :            :         return false;
     340                 :            :       }
     341                 :            : 
     342                 :            :       prev_max_size = max_size;
     343                 :            :     }
     344                 :            :   }
     345                 :            : 
     346                 :            :   return true;
     347                 :            : }
     348                 :            : 
     349                 :            : static_assert(default_tier_table_well_formed(),
     350                 :            :               "MemoryPool: kDefaultTierTable contains a malformed row "
     351                 :            :               "(undersized tier or non-monotonic max_size)");
     352                 :            : 
     353                 :        425 : static bool grow_tier_chunk(MemoryTierState& state) noexcept {
     354                 :        425 :   size_t blocks = state.next_chunk_blocks;
     355                 :            : 
     356         [ -  + ]:        425 :   if VUNLIKELY (blocks > state.blocks_per_chunk) {
     357                 :            :     blocks = state.blocks_per_chunk;  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     358                 :            :   }
     359                 :            : 
     360                 :        425 :   const size_t block_size = state.block_size;
     361                 :        425 :   const size_t chunk_bytes = block_size * blocks;
     362                 :            : 
     363         [ -  + ]:        425 :   if VUNLIKELY (chunk_bytes / block_size != blocks) {
     364                 :            :     return false;  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     365                 :            :   }
     366                 :            : 
     367                 :        425 :   state.mtx.unlock();
     368                 :            : 
     369                 :        425 :   void* ptr = ::operator new(chunk_bytes, std::align_val_t{MemoryPool::kBlockAlignment}, std::nothrow);
     370                 :            : 
     371         [ -  + ]:        425 :   if VUNLIKELY (ptr == nullptr) {
     372                 :            :     state.mtx.lock();  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     373                 :            :     return false;      // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     374                 :            :   }
     375                 :            : 
     376                 :        425 :   auto* base = static_cast<std::byte*>(ptr);
     377                 :        425 :   auto* local_tail = ::new (base + (blocks - 1U) * block_size) MemoryFreeNode{nullptr};
     378                 :        425 :   MemoryFreeNode* local_head = local_tail;
     379                 :            : 
     380         [ +  + ]:     249248 :   for (size_t i = blocks - 1U; i > 0; --i) {
     381                 :     248825 :     local_head = ::new (base + (i - 1U) * block_size) MemoryFreeNode{local_head};
     382                 :            :   }
     383                 :            : 
     384                 :        423 :   state.mtx.lock();
     385                 :            : 
     386                 :            :   try {
     387         [ +  - ]:        425 :     state.chunks.push_back(MemoryChunk{ptr, chunk_bytes});
     388         [ -  - ]:          0 :   } catch (std::exception&) {
     389                 :            :     // LCOV_EXCL_START GCOVR_EXCL_START
     390                 :            :     state.mtx.unlock();
     391                 :            :     ::operator delete(ptr, chunk_bytes, std::align_val_t{MemoryPool::kBlockAlignment});
     392                 :            :     state.mtx.lock();
     393                 :            : 
     394                 :            :     return false;
     395                 :            :     // LCOV_EXCL_STOP GCOVR_EXCL_STOP
     396                 :          0 :   }
     397                 :            : 
     398                 :        425 :   state.upstream_alloc_count.fetch_add(1, std::memory_order_relaxed);
     399                 :        425 :   state.upstream_alloc_bytes.fetch_add(chunk_bytes, std::memory_order_relaxed);
     400                 :        425 :   state.chunk_count.fetch_add(1, std::memory_order_relaxed);
     401                 :            : 
     402                 :        425 :   local_tail->next = state.free_list_head;
     403                 :        425 :   state.free_list_head = local_head;
     404                 :            : 
     405                 :        425 :   const size_t doubled = blocks * 2U;
     406         [ +  + ]:        425 :   const size_t target = (doubled < blocks || doubled > state.blocks_per_chunk)
     407         [ +  - ]:        850 :                             ? state.blocks_per_chunk
     408                 :            :                             : doubled;  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     409                 :            : 
     410         [ +  + ]:        425 :   if (target > state.next_chunk_blocks) {
     411                 :        378 :     state.next_chunk_blocks = target;
     412                 :            :   }
     413                 :            : 
     414                 :        425 :   return true;
     415                 :            : }
     416                 :            : 
     417                 :      30317 : static void* tier_allocate(MemoryTierState& state) noexcept {
     418                 :            :   for (;;) {
     419                 :            :     {
     420                 :      30317 :       uint16_t grow_spins = 0;
     421                 :            : 
     422         [ +  + ]:      32849 :       while (state.growing.load(std::memory_order_acquire)) {
     423         [ +  + ]:       2520 :         if (grow_spins < 128) {
     424                 :       2375 :           Utils::yield_cpu();
     425                 :       2387 :           ++grow_spins;
     426                 :            :         } else {
     427                 :        145 :           std::this_thread::yield();
     428                 :            :         }
     429                 :            :       }
     430                 :            :     }
     431                 :            : 
     432                 :      30460 :     state.mtx.lock();
     433                 :            : 
     434         [ +  + ]:      31177 :     if VLIKELY (state.free_list_head != nullptr) {
     435                 :      30606 :       MemoryFreeNode* node = state.free_list_head;
     436                 :      30606 :       state.free_list_head = node->next;
     437                 :      30606 :       state.mtx.unlock();
     438                 :            : 
     439                 :      30762 :       return node;
     440                 :            :     }
     441                 :            : 
     442         [ -  + ]:        571 :     if (state.growing.load(std::memory_order_relaxed)) {
     443                 :          0 :       state.mtx.unlock();
     444                 :          5 :       continue;
     445                 :            :     }
     446                 :            : 
     447                 :        422 :     state.growing.store(true, std::memory_order_release);
     448                 :        422 :     const bool ok = grow_tier_chunk(state);
     449                 :        422 :     state.growing.store(false, std::memory_order_release);
     450                 :            : 
     451         [ -  + ]:        422 :     if VUNLIKELY (!ok) {
     452                 :            :       // LCOV_EXCL_START GCOVR_EXCL_START
     453                 :            :       if (state.free_list_head != nullptr) {
     454                 :            :         MemoryFreeNode* node = state.free_list_head;
     455                 :            :         state.free_list_head = node->next;
     456                 :            :         state.mtx.unlock();
     457                 :            : 
     458                 :            :         return node;
     459                 :            :       }
     460                 :            : 
     461                 :            :       state.mtx.unlock();
     462                 :            :       return nullptr;
     463                 :            :       // LCOV_EXCL_STOP GCOVR_EXCL_STOP
     464                 :            :     }
     465                 :            : 
     466                 :        422 :     MemoryFreeNode* node = state.free_list_head;
     467                 :        422 :     state.free_list_head = node->next;
     468                 :        422 :     state.mtx.unlock();
     469                 :            : 
     470                 :        422 :     return node;
     471                 :          5 :   }
     472                 :            : }
     473                 :            : 
     474                 :      30284 : static void tier_deallocate(MemoryTierState& state, void* p) noexcept {
     475                 :      30284 :   SpinLockGuard lock(state.mtx);
     476                 :            : 
     477                 :      31133 :   auto* node = ::new (p) MemoryFreeNode{state.free_list_head};
     478                 :            : 
     479                 :      31149 :   state.free_list_head = node;
     480                 :      31149 : }
     481                 :            : 
     482                 :          3 : static void prealloc_full_quota(MemoryTierState& state) noexcept {
     483                 :          3 :   state.mtx.lock();
     484                 :            : 
     485                 :          3 :   state.growing.store(true, std::memory_order_release);
     486                 :          3 :   state.next_chunk_blocks = state.blocks_per_chunk;
     487                 :          3 :   const bool ok = grow_tier_chunk(state);
     488                 :            : 
     489         [ -  + ]:          3 :   if VUNLIKELY (!ok) {
     490                 :            :     state.next_chunk_blocks = state.initial_chunk_blocks;  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     491                 :            :   }
     492                 :            : 
     493                 :          3 :   state.growing.store(false, std::memory_order_release);
     494                 :            : 
     495                 :          3 :   state.mtx.unlock();
     496                 :            : 
     497         [ -  + ]:          3 :   if VUNLIKELY (!ok) {
     498                 :          0 :     CLOG_W("MemoryPool: prealloc failed for tier (max_size=%zu, blocks_per_chunk=%zu); tier reverts to lazy growth.",
     499                 :            :            state.max_size, state.blocks_per_chunk);
     500                 :            :   }
     501                 :          3 : }
     502                 :            : 
     503                 :        228 : static bool validate_tiers_log(const std::vector<MemoryPool::Tier>& tiers) noexcept {
     504                 :            :   static constexpr size_t kMaxTierSize = SIZE_MAX - MemoryPool::kBlockAlignment + 1U;
     505                 :            : 
     506         [ +  + ]:        228 :   if VUNLIKELY (tiers.size() > kMaxTierCount) {
     507                 :          2 :     CLOG_E("MemoryPool: tier count %zu exceeds max %zu; falling back to default pyramid.", tiers.size(), kMaxTierCount);
     508                 :          1 :     return false;
     509                 :            :   }
     510                 :            : 
     511         [ +  + ]:       4037 :   for (size_t i = 0; i < tiers.size(); ++i) {
     512         [ +  + ]:       3815 :     if VUNLIKELY (tiers[i].max_size == 0) {
     513                 :          2 :       CLOG_E("MemoryPool: tier %zu has max_size == 0; falling back to default pyramid.", i);
     514                 :          5 :       return false;
     515                 :            :     }
     516                 :            : 
     517         [ +  + ]:       3814 :     if VUNLIKELY (tiers[i].max_size < sizeof(MemoryFreeNode)) {
     518                 :          2 :       CLOG_E(
     519                 :            :           "MemoryPool: tier %zu max_size (%zu) is below the minimum block size %zu; "
     520                 :            :           "falling back to default pyramid.",
     521                 :            :           i, tiers[i].max_size, sizeof(MemoryFreeNode));
     522                 :          1 :       return false;
     523                 :            :     }
     524                 :            : 
     525         [ +  + ]:       3813 :     if VUNLIKELY (tiers[i].max_size > kMaxTierSize) {
     526                 :          2 :       CLOG_E("MemoryPool: tier %zu max_size overflows after alignment rounding; falling back.", i);
     527                 :          1 :       return false;
     528                 :            :     }
     529                 :            : 
     530   [ +  +  +  +  :       3812 :     if VUNLIKELY (i > 0 && tiers[i].max_size <= tiers[i - 1].max_size) {
                   +  + ]
     531                 :          4 :       CLOG_E("MemoryPool: tier %zu max_size is not strictly increasing; falling back to default pyramid.", i);
     532                 :          2 :       return false;
     533                 :            :     }
     534                 :            :   }
     535                 :            : 
     536                 :        222 :   return true;
     537                 :            : }
     538                 :            : 
     539                 :        199 : static MemoryPool::Config create_memory_config(int level, bool prealloc) {
     540   [ +  -  +  +  :        199 :   if VUNLIKELY (level < kMinMemoryLevel || level > kMaxMemoryLevel) {
                   +  + ]
     541   [ +  -  +  - ]:          2 :     CLOG_W("MemoryPool: level %d out of range [%d, %d], clamped.", level, kMinMemoryLevel, kMaxMemoryLevel);
     542         [ -  + ]:          1 :     level = (level < kMinMemoryLevel) ? kMinMemoryLevel : kMaxMemoryLevel;
     543                 :            :   }
     544                 :            : 
     545                 :        199 :   const auto row_index = static_cast<size_t>(level - kMinMemoryLevel);
     546                 :        199 :   const auto& row = kDefaultTierTable[row_index];
     547                 :            : 
     548                 :        199 :   MemoryPool::Config config;
     549                 :        199 :   config.prealloc = prealloc;
     550         [ +  - ]:        199 :   config.tiers.reserve(kMaxTierCount);
     551                 :            : 
     552   [ +  -  +  + ]:       3980 :   for (size_t i = 0; i < kMaxTierCount && row[i].max_size != 0; ++i) {
     553         [ +  - ]:       3781 :     config.tiers.emplace_back(row[i]);
     554                 :            :   }
     555                 :            : 
     556                 :        199 :   return config;
     557                 :            : }  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     558                 :            : 
     559                 :            : struct MemoryPool::Impl final {  // NOLINT(clang-analyzer-optin.performance.Padding)
     560                 :            :   alignas(64) size_t dispatch_max_sizes[kMaxTierCount]{};
     561                 :            :   MemoryTierState* dispatch_states[kMaxTierCount]{};
     562                 :            :   size_t dispatch_count{0};
     563                 :            : 
     564                 :            :   alignas(64) size_t tier_max_sizes[kMaxTierCount]{};
     565                 :            : 
     566                 :            :   MemoryTierState* tier_states[kMaxTierCount]{};
     567                 :            :   size_t tier_count{0};
     568                 :            :   std::vector<std::unique_ptr<MemoryTierState>> owned_states;
     569                 :            :   MemoryAllocCounters oversized_alloc;
     570                 :            : 
     571                 :            :   alignas(64) std::atomic<uint64_t> oversized_dealloc_count{0};
     572                 :            : };
     573                 :            : 
     574         [ +  - ]:          4 : MemoryPool::MemoryPool() : MemoryPool(Config{}) {}
     575                 :            : 
     576         [ +  - ]:         16 : MemoryPool::MemoryPool(int level, bool prealloc) : MemoryPool(create_memory_config(level, prealloc)) {}
     577                 :            : 
     578                 :        234 : MemoryPool::MemoryPool(const Config& config) : impl_(std::make_unique<Impl>()) {
     579         [ +  + ]:        234 :   if (config.tiers.empty()) {
     580                 :          6 :     impl_->tier_count = 0;
     581                 :          6 :     return;
     582                 :            :   }
     583                 :            : 
     584                 :        228 :   std::vector<Tier> fallback;
     585                 :        228 :   const bool use_caller = validate_tiers_log(config.tiers);
     586                 :            : 
     587         [ +  + ]:        228 :   if VUNLIKELY (!use_caller) {
     588                 :          6 :     const auto& row = kDefaultTierTable[kDefaultMemoryLevel - kMinMemoryLevel];
     589         [ +  - ]:          6 :     fallback.assign(row, row + kMaxTierCount);
     590                 :            :   }
     591                 :            : 
     592         [ +  + ]:        228 :   const std::vector<Tier>& source = use_caller ? config.tiers : fallback;
     593                 :            : 
     594         [ +  - ]:        228 :   impl_->owned_states.reserve(source.size());
     595                 :            : 
     596                 :        228 :   size_t live = 0;
     597                 :        228 :   size_t dispatch = 0;
     598                 :            : 
     599         [ +  + ]:       4156 :   for (const auto& cfg : source) {
     600         [ +  + ]:       3928 :     if VUNLIKELY (cfg.max_size == 0U) {
     601                 :        648 :       continue;
     602                 :            :     }
     603                 :            : 
     604                 :       3922 :     impl_->dispatch_max_sizes[dispatch] = cfg.max_size;
     605                 :            : 
     606         [ +  + ]:       3922 :     if VUNLIKELY (cfg.blocks_per_chunk == 0U) {
     607                 :        642 :       impl_->dispatch_states[dispatch] = nullptr;
     608                 :        642 :       ++dispatch;
     609                 :        642 :       continue;
     610                 :            :     }
     611                 :            : 
     612         [ +  - ]:       3280 :     auto state = std::make_unique<MemoryTierState>();
     613                 :       3280 :     state->max_size = cfg.max_size;
     614                 :       3280 :     state->blocks_per_chunk = cfg.blocks_per_chunk;
     615         [ +  - ]:       3280 :     state->chunks.reserve(kInitialChunksReserve);
     616                 :       3280 :     state->block_size = round_up(cfg.max_size, kBlockAlignment);
     617                 :            : 
     618         [ +  - ]:       3280 :     size_t initial = (state->block_size > 0U) ? (kInitialChunkBytesTarget / state->block_size) : kInitialBlocksPerChunk;
     619                 :            : 
     620         [ +  + ]:       3280 :     if (initial < kInitialBlocksPerChunk) {
     621                 :        811 :       initial = kInitialBlocksPerChunk;
     622                 :            :     }
     623                 :            : 
     624         [ +  + ]:       3280 :     if (initial > state->blocks_per_chunk) {
     625                 :         45 :       initial = state->blocks_per_chunk;
     626                 :            :     }
     627                 :            : 
     628                 :       3280 :     state->initial_chunk_blocks = initial;
     629                 :       3280 :     state->next_chunk_blocks = initial;
     630                 :            : 
     631                 :       3280 :     impl_->tier_max_sizes[live] = cfg.max_size;
     632                 :       3280 :     impl_->tier_states[live] = state.get();
     633                 :       3280 :     impl_->dispatch_states[dispatch] = state.get();
     634         [ +  - ]:       3280 :     impl_->owned_states.emplace_back(std::move(state));
     635                 :            : 
     636                 :       3280 :     ++live;
     637                 :       3280 :     ++dispatch;
     638                 :       3280 :   }
     639                 :            : 
     640                 :        228 :   impl_->dispatch_count = dispatch;
     641                 :        228 :   impl_->tier_count = live;
     642                 :            : 
     643         [ +  + ]:        228 :   if (config.prealloc) {
     644         [ +  + ]:          5 :     for (auto& state : impl_->owned_states) {
     645                 :          3 :       prealloc_full_quota(*state);
     646                 :            :     }
     647                 :            :   }
     648                 :        228 : }
     649                 :            : 
     650                 :        234 : MemoryPool::~MemoryPool() {
     651         [ +  + ]:       3514 :   for (auto& state : impl_->owned_states) {
     652                 :            :     // SpinLockGuard lock(state->mtx);
     653                 :            : 
     654         [ +  + ]:       3695 :     for (const MemoryChunk& chunk : state->chunks) {
     655                 :        415 :       ::operator delete(chunk.ptr, chunk.bytes, std::align_val_t{kBlockAlignment});
     656                 :            :     }
     657                 :            : 
     658                 :       3280 :     state->chunks.clear();
     659                 :       3280 :     state->free_list_head = nullptr;
     660                 :            :   }
     661                 :        234 : }
     662                 :            : 
     663                 :      30456 : void* MemoryPool::allocate(size_t bytes, size_t alignment) noexcept {
     664         [ +  + ]:      30456 :   if VUNLIKELY (!is_power_of_two(alignment)) {
     665                 :          2 :     CLOG_E("MemoryPool::allocate: alignment %zu is not a power of two; returning nullptr.", alignment);
     666                 :          1 :     return nullptr;
     667                 :            :   }
     668                 :            : 
     669                 :      30424 :   const size_t idx = find_tier(bytes);
     670                 :            : 
     671   [ +  #  +  +  :      30618 :   if VUNLIKELY (idx == kMaxTierCount || alignment > kBlockAlignment || impl_->dispatch_states[idx] == nullptr) {
          +  +  #  +  +  
                      + ]
     672                 :        115 :     void* p = ::operator new(bytes, std::align_val_t{alignment}, std::nothrow);
     673                 :            : 
     674         [ -  + ]:        115 :     if VUNLIKELY (p == nullptr) {
     675                 :            :       return nullptr;  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     676                 :            :     }
     677                 :            : 
     678                 :        115 :     impl_->oversized_alloc.count.fetch_add(1, std::memory_order_relaxed);
     679                 :        115 :     impl_->oversized_alloc.bytes.fetch_add(bytes, std::memory_order_relaxed);
     680                 :            : 
     681                 :        115 :     return p;
     682                 :            :   }
     683                 :            : 
     684                 :      30470 :   MemoryTierState& state = *impl_->dispatch_states[idx];
     685                 :      30360 :   void* block = tier_allocate(state);
     686                 :            : 
     687         [ -  + ]:      30987 :   if VUNLIKELY (block == nullptr) {
     688                 :            :     return nullptr;  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     689                 :            :   }
     690                 :            : 
     691                 :      30987 :   state.hit_count.fetch_add(1, std::memory_order_relaxed);
     692                 :            : 
     693                 :      30987 :   return block;
     694                 :            : }
     695                 :            : 
     696                 :      30479 : void MemoryPool::deallocate(void* p, size_t bytes, size_t alignment) noexcept {
     697         [ #  + ]:      30479 :   if VUNLIKELY (!is_power_of_two(alignment)) {
     698                 :          0 :     CLOG_E("MemoryPool::deallocate: alignment %zu is not a power of two; leaking %p.", alignment, p);
     699                 :          1 :     return;
     700                 :            :   }
     701                 :            : 
     702         [ +  + ]:      30409 :   if VUNLIKELY (p == nullptr) {
     703                 :          2 :     return;
     704                 :            :   }
     705                 :            : 
     706                 :      30407 :   const size_t idx = find_tier(bytes);
     707                 :            : 
     708   [ +  #  +  +  :      30553 :   if VUNLIKELY (idx == kMaxTierCount || alignment > kBlockAlignment || impl_->dispatch_states[idx] == nullptr) {
          +  +  #  +  +  
                      + ]
     709                 :        114 :     ::operator delete(p, bytes, std::align_val_t{alignment});
     710                 :        114 :     impl_->oversized_dealloc_count.fetch_add(1, std::memory_order_relaxed);
     711                 :            : 
     712                 :        114 :     return;
     713                 :            :   }
     714                 :            : 
     715                 :      30343 :   MemoryTierState& state = *impl_->dispatch_states[idx];
     716                 :      30288 :   tier_deallocate(state, p);
     717                 :      30970 :   state.deallocate_count.fetch_add(1, std::memory_order_relaxed);
     718                 :            : }
     719                 :            : 
     720                 :         17 : size_t MemoryPool::get_tier_count() const noexcept { return impl_->tier_count; }
     721                 :            : 
     722                 :         39 : std::vector<MemoryPool::TierStats> MemoryPool::get_stats() const noexcept {
     723                 :         39 :   const size_t count = impl_->tier_count;
     724                 :            : 
     725                 :         39 :   std::vector<TierStats> result;
     726                 :            : 
     727                 :            :   try {
     728         [ +  - ]:         39 :     result.reserve(count);
     729                 :            :   } catch (...) {  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     730                 :            :     return {};     // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     731                 :            :   }  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     732                 :            : 
     733         [ +  + ]:        334 :   for (size_t i = 0; i < count; ++i) {
     734                 :        295 :     const MemoryTierState& state = *impl_->tier_states[i];
     735                 :        295 :     const uint64_t hits = state.hit_count.load(std::memory_order_relaxed);
     736                 :        295 :     const uint64_t deallocs = state.deallocate_count.load(std::memory_order_relaxed);
     737                 :            : 
     738                 :        295 :     TierStats item;
     739                 :        295 :     item.max_size = state.max_size;
     740                 :        295 :     item.blocks_per_chunk = state.blocks_per_chunk;
     741                 :        295 :     item.block_size = state.block_size;
     742                 :        295 :     item.hit_count = hits;
     743                 :        295 :     item.deallocate_count = deallocs;
     744         [ +  - ]:        295 :     item.in_use_blocks = (hits >= deallocs) ? (hits - deallocs) : 0U;
     745                 :        295 :     item.upstream_alloc_count = state.upstream_alloc_count.load(std::memory_order_relaxed);
     746                 :        295 :     item.upstream_alloc_bytes = state.upstream_alloc_bytes.load(std::memory_order_relaxed);
     747                 :        295 :     item.chunk_count = state.chunk_count.load(std::memory_order_relaxed);
     748                 :            : 
     749                 :        295 :     result.emplace_back(item);
     750                 :            :   }
     751                 :            : 
     752                 :         39 :   return result;
     753                 :         39 : }
     754                 :            : 
     755                 :         34 : MemoryPool::OversizedStats MemoryPool::get_oversized_stats() const noexcept {
     756                 :         34 :   OversizedStats result;
     757                 :            : 
     758                 :         34 :   result.alloc_count = impl_->oversized_alloc.count.load(std::memory_order_relaxed);
     759                 :         34 :   result.alloc_bytes = impl_->oversized_alloc.bytes.load(std::memory_order_relaxed);
     760                 :         34 :   result.dealloc_count = impl_->oversized_dealloc_count.load(std::memory_order_relaxed);
     761                 :            : 
     762                 :         34 :   return result;
     763                 :            : }
     764                 :            : 
     765                 :          1 : void MemoryPool::reset_stats() noexcept {
     766                 :          1 :   const size_t count = impl_->tier_count;
     767                 :            : 
     768         [ +  + ]:          2 :   for (size_t i = 0; i < count; ++i) {
     769                 :          1 :     MemoryTierState& state = *impl_->tier_states[i];
     770                 :          1 :     state.hit_count.store(0, std::memory_order_relaxed);
     771                 :          1 :     state.deallocate_count.store(0, std::memory_order_relaxed);
     772                 :            :   }
     773                 :            : 
     774                 :          1 :   impl_->oversized_alloc.count.store(0, std::memory_order_relaxed);
     775                 :          1 :   impl_->oversized_alloc.bytes.store(0, std::memory_order_relaxed);
     776                 :          1 :   impl_->oversized_dealloc_count.store(0, std::memory_order_relaxed);
     777                 :          1 : }
     778                 :            : 
     779                 :          8 : void MemoryPool::clear() noexcept {
     780                 :          8 :   constexpr size_t kStackSlots = 64U;
     781                 :            : 
     782         [ +  + ]:         48 :   for (auto& state : impl_->owned_states) {
     783                 :         40 :     size_t stack_free_counts[kStackSlots] = {};
     784                 :         40 :     MemoryChunk stack_to_delete[kStackSlots];
     785                 :            : 
     786                 :         40 :     std::vector<size_t> heap_free_counts;
     787                 :         40 :     std::vector<MemoryChunk> heap_to_delete;
     788                 :            : 
     789                 :         40 :     const size_t chunks_hint = state->chunk_count.load(std::memory_order_relaxed);
     790                 :            : 
     791         [ -  + ]:         40 :     if VUNLIKELY (chunks_hint > kStackSlots) {
     792                 :            :       try {
     793                 :            :         // LCOV_EXCL_START GCOVR_EXCL_START
     794                 :            :         heap_free_counts.reserve(chunks_hint);
     795                 :            :         heap_to_delete.reserve(chunks_hint);
     796                 :            :       } catch (std::exception&) {
     797                 :            :       }
     798                 :            :       // LCOV_EXCL_STOP GCOVR_EXCL_STOP
     799                 :            :     }
     800                 :            : 
     801                 :         40 :     MemoryChunk* to_delete = nullptr;
     802                 :         40 :     size_t to_delete_count = 0U;
     803                 :            : 
     804                 :            :     {
     805                 :         40 :       SpinLockGuard lock(state->mtx);
     806                 :            : 
     807                 :         40 :       const size_t chunk_count = state->chunks.size();
     808                 :            : 
     809         [ +  + ]:         40 :       if VUNLIKELY (chunk_count == 0U) {
     810                 :         30 :         continue;
     811                 :            :       }
     812                 :            : 
     813                 :         10 :       size_t* free_counts = stack_free_counts;
     814                 :            : 
     815                 :         10 :       const bool spill_to_heap = (chunk_count > kStackSlots);
     816                 :            : 
     817         [ -  + ]:         10 :       if VUNLIKELY (spill_to_heap) {
     818                 :            :         try {
     819                 :            :           // LCOV_EXCL_START GCOVR_EXCL_START
     820                 :            :           heap_free_counts.assign(chunk_count, 0U);
     821                 :            :         } catch (std::exception&) {
     822                 :            :           continue;
     823                 :            :         }
     824                 :            : 
     825                 :            :         free_counts = heap_free_counts.data();
     826                 :            :         // LCOV_EXCL_STOP GCOVR_EXCL_STOP
     827                 :            :       }
     828                 :            : 
     829                 :         10 :       const size_t block_size = state->block_size;
     830                 :            : 
     831                 :         10 :       std::sort(state->chunks.begin(), state->chunks.end(), [](const MemoryChunk& a, const MemoryChunk& b) noexcept {
     832                 :          3 :         return reinterpret_cast<std::uintptr_t>(a.ptr) < reinterpret_cast<std::uintptr_t>(b.ptr);
     833                 :            :       });
     834                 :            : 
     835                 :      12238 :       const auto find_chunk_idx = [&chunk_count, &state](std::uintptr_t addr) noexcept -> size_t {
     836                 :       4070 :         size_t lo = 0;
     837                 :       4070 :         size_t hi = chunk_count;
     838                 :            : 
     839         [ +  - ]:       4084 :         while (lo < hi) {
     840                 :       4084 :           const size_t mid = lo + (hi - lo) / 2U;
     841                 :       4084 :           const auto cs = reinterpret_cast<std::uintptr_t>(state->chunks[mid].ptr);
     842                 :       4084 :           const auto ce = cs + state->chunks[mid].bytes;
     843                 :            : 
     844         [ +  + ]:       4084 :           if (addr < cs) {
     845                 :         14 :             hi = mid;
     846         [ -  + ]:       4070 :           } else if (addr >= ce) {
     847                 :            :             lo = mid + 1U;  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     848                 :            :           } else {
     849                 :       4070 :             return mid;
     850                 :            :           }
     851                 :            :         }
     852                 :            : 
     853                 :            :         return SIZE_MAX;  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     854                 :         10 :       };
     855                 :            : 
     856         [ +  + ]:       2045 :       for (MemoryFreeNode* node = state->free_list_head; node != nullptr; node = node->next) {
     857                 :       2035 :         const size_t idx = find_chunk_idx(reinterpret_cast<std::uintptr_t>(node));
     858                 :            : 
     859         [ +  - ]:       2035 :         if VLIKELY (idx != SIZE_MAX) {
     860                 :       2035 :           ++free_counts[idx];
     861                 :            :         }
     862                 :            :       }
     863                 :            : 
     864                 :         10 :       MemoryFreeNode* new_head = nullptr;
     865                 :         10 :       MemoryFreeNode* current = state->free_list_head;
     866                 :            : 
     867         [ +  + ]:       2045 :       while (current != nullptr) {
     868                 :       2035 :         MemoryFreeNode* next = current->next;
     869                 :       2035 :         const size_t idx = find_chunk_idx(reinterpret_cast<std::uintptr_t>(current));
     870                 :            : 
     871                 :       2035 :         bool keep = false;
     872                 :            : 
     873         [ +  - ]:       2035 :         if VLIKELY (idx != SIZE_MAX) {
     874                 :       2035 :           const size_t total_blocks = state->chunks[idx].bytes / block_size;
     875                 :       2035 :           keep = (free_counts[idx] != total_blocks);
     876                 :            :         }
     877                 :            : 
     878         [ +  + ]:       2035 :         if (keep) {
     879                 :         34 :           current->next = new_head;
     880                 :         34 :           new_head = current;
     881                 :            :         }
     882                 :            : 
     883                 :       2035 :         current = next;
     884                 :            :       }
     885                 :            : 
     886                 :         10 :       state->free_list_head = new_head;
     887                 :            : 
     888                 :         10 :       size_t released = 0U;
     889                 :         10 :       size_t write = 0U;
     890                 :            : 
     891         [ +  + ]:         22 :       for (size_t read = 0U; read < chunk_count; ++read) {
     892                 :         12 :         const size_t total_blocks = state->chunks[read].bytes / block_size;
     893                 :            : 
     894         [ +  + ]:         12 :         if (free_counts[read] == total_blocks) {
     895         [ +  - ]:         10 :           if VLIKELY (!spill_to_heap) {
     896                 :         10 :             stack_to_delete[released] = state->chunks[read];
     897                 :            :           } else {
     898                 :            :             try {
     899                 :            :               // LCOV_EXCL_START GCOVR_EXCL_START
     900                 :            :               heap_to_delete.push_back(state->chunks[read]);
     901                 :            :             } catch (std::exception&) {
     902                 :            :               ::operator delete(state->chunks[read].ptr, state->chunks[read].bytes, std::align_val_t{kBlockAlignment});
     903                 :            :             }
     904                 :            :             // LCOV_EXCL_STOP GCOVR_EXCL_STOP
     905                 :            :           }
     906                 :            : 
     907                 :         10 :           ++released;
     908                 :            :         } else {
     909         [ -  + ]:          2 :           if (read != write) {
     910                 :            :             state->chunks[write] = state->chunks[read];  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     911                 :            :           }
     912                 :            : 
     913                 :          2 :           ++write;
     914                 :            :         }
     915                 :            :       }
     916                 :            : 
     917         [ +  + ]:         10 :       if VLIKELY (released > 0U) {
     918                 :          9 :         state->chunks.resize(write);
     919                 :          9 :         state->chunk_count.fetch_sub(released, std::memory_order_relaxed);
     920                 :            :       }
     921                 :            : 
     922         [ +  - ]:         10 :       if VLIKELY (!spill_to_heap) {
     923                 :         10 :         to_delete = stack_to_delete;
     924                 :         10 :         to_delete_count = released;
     925                 :            :       } else {
     926                 :            :         to_delete = heap_to_delete.data();        // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     927                 :            :         to_delete_count = heap_to_delete.size();  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     928                 :            :       }
     929         [ +  + ]:         40 :     }
     930                 :            : 
     931         [ +  + ]:         20 :     for (size_t i = 0; i < to_delete_count; ++i) {
     932                 :         10 :       ::operator delete(to_delete[i].ptr, to_delete[i].bytes, std::align_val_t{kBlockAlignment});
     933                 :            :     }
     934   [ +  +  +  + ]:         70 :   }
     935                 :          8 : }
     936                 :            : 
     937                 :          2 : void MemoryPool::trim() noexcept { clear(); }
     938                 :            : 
     939                 :        183 : MemoryPool::Config MemoryPool::get_default_config() {
     940                 :        181 :   static int level = []() noexcept {
     941                 :        362 :     const std::string env_value = Utils::get_env("VLINK_MEMORY_LEVEL", "3");
     942                 :            : 
     943                 :        181 :     int parsed = kDefaultMemoryLevel;
     944                 :            : 
     945                 :        181 :     const char* first = env_value.data();
     946                 :        181 :     const char* last = first + env_value.size();
     947                 :            : 
     948                 :        181 :     auto [ptr, ec] = std::from_chars(first, last, parsed);
     949                 :            : 
     950   [ +  -  -  +  :        181 :     if VUNLIKELY (ec != std::errc() || ptr != last) {
                   -  + ]
     951                 :            :       // LCOV_EXCL_START GCOVR_EXCL_START
     952                 :            :       CLOG_W("MemoryPool: VLINK_MEMORY_LEVEL=\"%s\" is not a valid integer, fallback to %d.", env_value.c_str(),
     953                 :            :              kDefaultMemoryLevel);
     954                 :            : 
     955                 :            :       return kDefaultMemoryLevel;
     956                 :            :       // LCOV_EXCL_STOP GCOVR_EXCL_STOP
     957                 :            :     }
     958                 :            : 
     959   [ +  -  -  +  :        181 :     if VUNLIKELY (parsed < kMinMemoryLevel || parsed > kMaxMemoryLevel) {
                   -  + ]
     960                 :            :       // LCOV_EXCL_START GCOVR_EXCL_START
     961                 :            :       CLOG_W("MemoryPool: VLINK_MEMORY_LEVEL=%d out of range [%d, %d], clamped.", parsed, kMinMemoryLevel,
     962                 :            :              kMaxMemoryLevel);
     963                 :            : 
     964                 :            :       return parsed < kMinMemoryLevel ? kMinMemoryLevel : kMaxMemoryLevel;
     965                 :            :       // LCOV_EXCL_STOP GCOVR_EXCL_STOP
     966                 :            :     }
     967                 :            : 
     968                 :        181 :     return parsed;
     969   [ +  +  +  - ]:        364 :   }();
     970                 :            : 
     971   [ +  +  +  -  :        183 :   static bool prealloc_env = (Utils::get_env("VLINK_MEMORY_PREALLOC") == "1");
          +  -  +  -  -  
                      - ]
     972                 :            : 
     973                 :        183 :   Config config = create_memory_config(level, prealloc_env);
     974                 :            : 
     975                 :        183 :   return config;
     976                 :            : }
     977                 :            : 
     978                 :      10183 : MemoryPool& MemoryPool::global_instance(bool use_env_level) {
     979                 :            : #if MEMORY_POOL_NERVER_DELETE
     980                 :            :   alignas(MemoryPool) static char buf[sizeof(MemoryPool)];
     981                 :            : 
     982                 :            :   static auto* instance =
     983                 :            :       new (buf) MemoryPool(use_env_level ? get_default_config() : create_memory_config(kDefaultMemoryLevel, false));
     984                 :            : 
     985                 :            :   return *instance;
     986                 :            : #else
     987   [ +  +  +  -  :      10183 :   static MemoryPool instance(use_env_level ? get_default_config() : create_memory_config(kDefaultMemoryLevel, false));
          +  -  +  -  -  
             -  +  -  -  
                      - ]
     988                 :            : 
     989                 :      10183 :   return instance;
     990                 :            : #endif
     991                 :            : }
     992                 :            : 
     993                 :      60236 : size_t MemoryPool::find_tier(size_t bytes) const noexcept {
     994                 :      60236 :   const size_t count = impl_->dispatch_count;
     995                 :      59639 :   const size_t* const sizes = impl_->dispatch_max_sizes;
     996                 :            : 
     997         [ +  + ]:     292343 :   for (size_t i = 0; i < count; ++i) {
     998         [ +  + ]:     292329 :     if (bytes <= sizes[i]) {
     999                 :      60446 :       return i;
    1000                 :            :     }
    1001                 :            :   }
    1002                 :            : 
    1003                 :         14 :   return kMaxTierCount;
    1004                 :            : }
    1005                 :            : 
    1006                 :            : }  // namespace vlink

Generated by: LCOV version 1.14