LCOV - code coverage report
Current view: top level - utilities/include/mcrl2/utilities - memory_pool.h (source / functions) Hit Total Coverage
Test: mcrl2_coverage.info.cleaned Lines: 73 99 73.7 %
Date: 2024-04-26 03:18:02 Functions: 57 70 81.4 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : // Author(s): Maurice Laveaux
       2             : // Copyright: see the accompanying file COPYING or copy at
       3             : // https://github.com/mCRL2org/mCRL2/blob/master/COPYING
       4             : //
       5             : // Distributed under the Boost Software License, Version 1.0.
       6             : // (See accompanying file LICENSE_1_0.txt or copy at
       7             : // http://www.boost.org/LICENSE_1_0.txt)
       8             : //
       9             : 
      10             : #ifndef MCRL2_UTILITIES_MEMORY_POOL_H_
      11             : #define MCRL2_UTILITIES_MEMORY_POOL_H_
      12             : 
      13             : #include "mcrl2/utilities/detail/free_list.h"
      14             : #include "mcrl2/utilities/noncopyable.h"
      15             : #include "mcrl2/utilities/mutex.h"
      16             : 
      17             : #include <array>
      18             : #include <cstdint>
      19             : #include <forward_list>
      20             : #include <type_traits>
      21             : #include <mutex>
      22             : 
      23             : namespace mcrl2
      24             : {
      25             : namespace utilities
      26             : {
      27             : 
      28             : /// \brief The memory pool allocates elements of size T from blocks.
      29             : /// \details When ThreadSafe is true then the thread-safe guarantees will be satisfied.
      30             : template <class T, 
      31             :           std::size_t ElementsPerBlock = 1024, 
      32             :           bool ThreadSafe = false>
      33             : class memory_pool : private mcrl2::utilities::noncopyable
      34             : {
      35             : public:
      36        1623 :   memory_pool() = default;
      37             : 
      38             :   /// \brief Triggers the (possibly non-trivial) destructor of all elements stored
      39             :   ///        in the pool.
      40         193 :   ~memory_pool()
      41             :   {
      42         193 :     m_freelist.destructive_mark();
      43             : 
      44             :     /// For all actual elements stored in the pool trigger the destructor.
      45         193 :     bool first_block = true;
      46         385 :     for (auto& block : m_blocks)
      47             :     {
      48         192 :       if (first_block)
      49             :       {
      50             :         // This is the first block, for that one only m_current_index elements were inserted.
      51        1770 :         for (std::size_t index = 0; index < m_current_index; ++index)
      52             :         {
      53        1578 :           auto& slot = block[index];
      54        1578 :           if (!slot.is_marked())
      55             :           {
      56           0 :             reinterpret_cast<T*>(&slot)->~T();
      57             :           }
      58             :         }
      59         192 :         first_block = false;
      60             :       }
      61             :       else
      62             :       {
      63           0 :         for (auto& slot : block)
      64             :         {
      65           0 :           if (!slot.is_marked())
      66             :           {
      67           0 :             reinterpret_cast<T*>(&slot)->~T();
      68             :           }
      69             :         }
      70             :       }
      71             :     }
      72             : 
      73         193 :     assert(m_freelist.empty());
      74         193 :   }
      75             : 
      76             :   /// \brief Reuses memory from block and allocates a new block when
      77             :   ///        no slots are free.
      78             :   /// \returns A pointer to a block of memory that can store an object of type T.
      79          30 :   /// \threadsafe
      80     1172312 :   T* allocate()
      81             :   {
      82          30 :     // Only allow one thread to allocate at the time.
      83     1172312 :     m_block_mutex.lock();
      84          30 : 
      85     1172312 :     if (!m_freelist.empty())
      86           0 :     {
      87       62720 :       T& element = m_freelist.pop_front();
      88       62720 :       m_block_mutex.unlock();
      89       62720 :       return &element;
      90             :     }
      91          30 : 
      92     1109592 :     if (m_current_index >= ElementsPerBlock)
      93             :     {
      94           1 :       // The whole buffer was used so allocate a new one.
      95        2265 :       m_blocks.emplace_front();
      96        2265 :       ++m_number_of_blocks;
      97        2264 :       m_current_index = 0;
      98             :     }
      99             : 
     100          30 :     // The object was last written as this slot is not part of the freelist.
     101     1109592 :     T& slot = (m_blocks.front()[m_current_index++]).element();
     102          30 : 
     103     1109622 :     assert(contains(&slot));
     104     1109622 :     m_block_mutex.unlock();
     105     1109592 :     return &slot;
     106             :   }
     107             : 
     108           0 :   /// \brief Free the memory used by the given pointer that has been allocated by this pool.
     109      240725 :   void deallocate(T* pointer)
     110           0 :   {    
     111      240725 :     m_block_mutex.lock();
     112           0 : 
     113      240725 :     assert(contains(pointer));
     114      240725 :     m_freelist.push_front(reinterpret_cast<Slot&>(*pointer));
     115           0 : 
     116      240725 :     m_block_mutex.unlock();
     117      240725 :   }
     118             : 
     119             :   /// \brief Frees blocks that are no longer storing elements of T.
     120           0 :   /// \returns The number of blocks that have been removed.
     121        9139 :   std::size_t consolidate()
     122           0 :   {
     123        9139 :     m_block_mutex.lock();
     124           0 : 
     125        9139 :     m_freelist.destructive_mark();
     126        9139 :     std::size_t old_number_of_blocks = m_number_of_blocks;
     127             : 
     128           0 :     // Keep track of the last block to be able to erase the current block.
     129        9139 :     auto block_before_it = m_blocks.before_begin();
     130       11763 :     for (auto it = m_blocks.begin(); it != m_blocks.end();)
     131           0 :     {
     132        2624 :       Block& block = *it;
     133             : 
     134           0 :       // Keep track of the head of the freelist.
     135        2624 :       FreelistIt old_freelist_head = m_freelist.begin();
     136             : 
     137           0 :       // Indicate that current block only contains slots in the freelist.
     138        2624 :       bool block_only_freelist = true;
     139     2494048 :       for (Slot& slot : block)
     140           0 :       {
     141     2491424 :         if (slot.is_marked())
     142           0 :         {
     143      351901 :           m_freelist.push_front(slot);
     144             :         }
     145             :         else
     146             :         {
     147           0 :           // There is one slot that was not part of the freelist.
     148     2139523 :           block_only_freelist = false;
     149             :         }
     150             :       }
     151             : 
     152           0 :       // Erase blocks that only have elements in the freelist.
     153        2624 :       if (block_only_freelist)
     154             :       {
     155           0 :         // Remove the slots in the freelist that point into this block.
     156         208 :         m_freelist.erase_after(m_freelist.before_begin(), old_freelist_head);
     157             : 
     158           0 :         // The current block only has elements in the freelist, so erase it.
     159         208 :         it = m_blocks.erase_after(block_before_it);
     160         208 :         --m_number_of_blocks;
     161             :       }
     162             :       else
     163           0 :       {
     164        2416 :         block_before_it = it;
     165        2416 :         ++it;
     166             :       }
     167             :     }
     168           0 : 
     169        9139 :     m_block_mutex.unlock();
     170        9139 :     return old_number_of_blocks - m_number_of_blocks;
     171             :   }
     172             : 
     173             :   /// \returns True when thi memory pool has space for at least one more element without allocating
     174             :   ///          new memory.
     175             :   bool has_free_slots() const noexcept
     176             :   {
     177             :     // The freelist is not full, i.e. points to some slot or the index has not yet reached the end.
     178             :     return m_current_index < ElementsPerBlock || m_freelist.empty();
     179             :   }
     180             : 
     181             :   /// \returns The total number of elements that could be stored in this memory pool.
     182             :   std::size_t capacity() const noexcept
     183             :   {
     184             :     return m_number_of_blocks * ElementsPerBlock;
     185             :   }
     186             : 
     187             :   // Move constructor and assignment are possible.
     188             :   memory_pool(memory_pool&& other) = default;
     189             :   memory_pool& operator=(memory_pool&& other) = default;
     190             : 
     191             : private:
     192             :   using Freelist = typename detail::free_list<T>;
     193             :   using FreelistIt = typename Freelist::iterator;
     194             :   using Slot = typename Freelist::slot;
     195             : 
     196             :   /// \brief An array that stores ElementsPerBlock number of objects of type T.
     197             :   using Block = std::array<Slot, ElementsPerBlock>;
     198             : 
     199             :   /// \brief The last slot in the first block that has never been returned by allocate.
     200             :   using SizeType = typename std::conditional<ThreadSafe, std::atomic<std::size_t>, std::size_t>::type;
     201             :   SizeType m_current_index = ElementsPerBlock;
     202             : 
     203             :   /// \brief Equal to the size of the blocks array to prevent iterating over the block list.
     204             :   SizeType m_number_of_blocks = 0;
     205             : 
     206             :   /// \brief The list of blocks allocated by this pool.
     207             :   std::forward_list<Block> m_blocks;
     208             : 
     209             :   /// \brief Ensures that the block list is only modified by a single thread.
     210             :   mcrl2::utilities::mutex m_block_mutex = {};
     211             : 
     212             :   /// \brief Indicates the head of the freelist.
     213             :   Freelist m_freelist;
     214             : 
     215         164 :   /// \returns Check whether the pointer is contained in this memory pool.
     216     1350183 :   bool contains(T* p)
     217         164 :   {
     218     1350183 :     assert(p != nullptr);
     219         164 : 
     220     1350347 :     std::uintptr_t pointer = reinterpret_cast<std::uintptr_t>(p);
     221     6995547 :     for (auto& block : m_blocks)
     222         164 :     {
     223     6995711 :       std::uintptr_t firstSlot = reinterpret_cast<std::uintptr_t>(block.data());
     224     6995547 :       if (firstSlot <= pointer && pointer < firstSlot + sizeof(Slot) * ElementsPerBlock)
     225         164 :       {
     226     1350347 :         assert((pointer - firstSlot) % sizeof(Slot) == 0);
     227     1350183 :         return true;
     228             :       }
     229             :     }
     230           0 : 
     231           0 :     return false;
     232             :   }
     233             : 
     234             : };
     235             : 
     236             : } // namespace utilities
     237             : } // namespace mcrl2
     238             : 
     239             : #endif // MCRL2_UTILITIES_MEMORY_POOL_H_

Generated by: LCOV version 1.14