mCRL2
Loading...
Searching...
No Matches
memory_pool.h
Go to the documentation of this file.
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
16
17#include <array>
18#include <cstdint>
19#include <forward_list>
20#include <type_traits>
21#include <mutex>
22
23namespace mcrl2
24{
25namespace utilities
26{
27
30template <class T,
31 std::size_t ElementsPerBlock = 1024,
32 bool ThreadSafe = false>
34{
35public:
36 memory_pool() = default;
37
41 {
42 m_freelist.destructive_mark();
43
45 bool first_block = true;
46 for (auto& block : m_blocks)
47 {
48 if (first_block)
49 {
50 // This is the first block, for that one only m_current_index elements were inserted.
51 for (std::size_t index = 0; index < m_current_index; ++index)
52 {
53 auto& slot = block[index];
54 if (!slot.is_marked())
55 {
56 reinterpret_cast<T*>(&slot)->~T();
57 }
58 }
59 first_block = false;
60 }
61 else
62 {
63 for (auto& slot : block)
64 {
65 if (!slot.is_marked())
66 {
67 reinterpret_cast<T*>(&slot)->~T();
68 }
69 }
70 }
71 }
72
73 assert(m_freelist.empty());
74 }
75
81 {
82 // Only allow one thread to allocate at the time.
84
85 if (!m_freelist.empty())
86 {
87 T& element = m_freelist.pop_front();
89 return &element;
90 }
91
92 if (m_current_index >= ElementsPerBlock)
93 {
94 // The whole buffer was used so allocate a new one.
95 m_blocks.emplace_front();
98 }
99
100 // The object was last written as this slot is not part of the freelist.
101 T& slot = (m_blocks.front()[m_current_index++]).element();
102
103 assert(contains(&slot));
105 return &slot;
106 }
107
110 {
112
113 assert(contains(pointer));
114 m_freelist.push_front(reinterpret_cast<Slot&>(*pointer));
115
117 }
118
121 std::size_t consolidate()
122 {
124
125 m_freelist.destructive_mark();
126 std::size_t old_number_of_blocks = m_number_of_blocks;
127
128 // Keep track of the last block to be able to erase the current block.
129 auto block_before_it = m_blocks.before_begin();
130 for (auto it = m_blocks.begin(); it != m_blocks.end();)
131 {
132 Block& block = *it;
133
134 // Keep track of the head of the freelist.
135 FreelistIt old_freelist_head = m_freelist.begin();
136
137 // Indicate that current block only contains slots in the freelist.
138 bool block_only_freelist = true;
139 for (Slot& slot : block)
140 {
141 if (slot.is_marked())
142 {
143 m_freelist.push_front(slot);
144 }
145 else
146 {
147 // There is one slot that was not part of the freelist.
148 block_only_freelist = false;
149 }
150 }
151
152 // Erase blocks that only have elements in the freelist.
153 if (block_only_freelist)
154 {
155 // Remove the slots in the freelist that point into this block.
156 m_freelist.erase_after(m_freelist.before_begin(), old_freelist_head);
157
158 // The current block only has elements in the freelist, so erase it.
159 it = m_blocks.erase_after(block_before_it);
161 }
162 else
163 {
164 block_before_it = it;
165 ++it;
166 }
167 }
168
170 return old_number_of_blocks - m_number_of_blocks;
171 }
172
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
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
191private:
193 using FreelistIt = typename Freelist::iterator;
194 using Slot = typename Freelist::slot;
195
197 using Block = std::array<Slot, ElementsPerBlock>;
198
200 using SizeType = typename std::conditional<ThreadSafe, std::atomic<std::size_t>, std::size_t>::type;
201 SizeType m_current_index = ElementsPerBlock;
202
205
207 std::forward_list<Block> m_blocks;
208
211
214
216 bool contains(T* p)
217 {
218 assert(p != nullptr);
219
220 std::uintptr_t pointer = reinterpret_cast<std::uintptr_t>(p);
221 for (auto& block : m_blocks)
222 {
223 std::uintptr_t firstSlot = reinterpret_cast<std::uintptr_t>(block.data());
224 if (firstSlot <= pointer && pointer < firstSlot + sizeof(Slot) * ElementsPerBlock)
225 {
226 assert((pointer - firstSlot) % sizeof(Slot) == 0);
227 return true;
228 }
229 }
230
231 return false;
232 }
233
234};
235
236} // namespace utilities
237} // namespace mcrl2
238
239#endif // MCRL2_UTILITIES_MEMORY_POOL_H_
This essentially implements the std::forward_list, with the difference that it does not own the nodes...
Definition free_list.h:32
The memory pool allocates elements of size T from blocks.
Definition memory_pool.h:34
typename Freelist::slot Slot
~memory_pool()
Triggers the (possibly non-trivial) destructor of all elements stored in the pool.
Definition memory_pool.h:40
std::size_t capacity() const noexcept
memory_pool & operator=(memory_pool &&other)=default
memory_pool(memory_pool &&other)=default
T * allocate()
Reuses memory from block and allocates a new block when no slots are free.
Definition memory_pool.h:80
std::array< Slot, ElementsPerBlock > Block
An array that stores ElementsPerBlock number of objects of type T.
void deallocate(T *pointer)
Free the memory used by the given pointer that has been allocated by this pool.
std::size_t consolidate()
Frees blocks that are no longer storing elements of T.
mcrl2::utilities::mutex m_block_mutex
Ensures that the block list is only modified by a single thread.
typename Freelist::iterator FreelistIt
typename std::conditional< ThreadSafe, std::atomic< std::size_t >, std::size_t >::type SizeType
The last slot in the first block that has never been returned by allocate.
typename detail::free_list< T > Freelist
bool has_free_slots() const noexcept
Freelist m_freelist
Indicates the head of the freelist.
std::forward_list< Block > m_blocks
The list of blocks allocated by this pool.
SizeType m_number_of_blocks
Equal to the size of the blocks array to prevent iterating over the block list.
This is simply an exclusive lock based on the standard library with the ability to perform no locks w...
Definition mutex.h:23
Inherit from this class to prevent it from being copyable.
Definition noncopyable.h:21
T * pointer(const T *p)
A class that takes a linear process specification and checks all tau-summands of that LPS for conflue...
Definition indexed_set.h:72