/src/PcapPlusPlus/Common++/header/ObjectPool.h
Line | Count | Source (jump to first uncovered line) |
1 | | #pragma once |
2 | | |
3 | | #include <stack> |
4 | | #include <mutex> |
5 | | #include <memory> |
6 | | #include <stdexcept> |
7 | | #include <limits> |
8 | | #include <type_traits> |
9 | | |
10 | | namespace pcpp |
11 | | { |
12 | | namespace internal |
13 | | { |
14 | | /// @brief A generic object pool implementation. |
15 | | /// |
16 | | /// This class provides a generic object pool that can be used to efficiently manage and reuse objects of any |
17 | | /// type. Objects can be acquired from the pool using the `acquireObject` method, and released back to the pool |
18 | | /// using the `releaseObject` method. If the pool is empty when acquiring an object, a new object will be |
19 | | /// created. If the pool is full when releasing an object, the object will be deleted. |
20 | | /// |
21 | | /// @tparam T The type of objects managed by the pool. Must be default constructable. |
22 | | template <class T, std::enable_if_t<std::is_default_constructible<T>::value, bool> = true> |
23 | | class DynamicObjectPool |
24 | | { |
25 | | public: |
26 | | constexpr static std::size_t DEFAULT_POOL_SIZE = 100; |
27 | | #pragma push_macro("max") // Undefine max to avoid conflict with std::numeric_limits<std::size_t>::max() |
28 | | #undef max |
29 | | constexpr static std::size_t INFINITE_POOL_SIZE = std::numeric_limits<std::size_t>::max(); |
30 | | #pragma pop_macro("max") |
31 | | |
32 | | /// A constructor for this class that creates a pool of objects |
33 | | /// @param[in] maxPoolSize The maximum number of objects in the pool |
34 | | /// @param[in] initialSize The number of objects to preallocate in the pool |
35 | | explicit DynamicObjectPool(std::size_t maxPoolSize = DEFAULT_POOL_SIZE, std::size_t initialSize = 0) |
36 | 1 | : m_MaxPoolSize(maxPoolSize) |
37 | 1 | { |
38 | 1 | if (initialSize > maxPoolSize) |
39 | 0 | { |
40 | 0 | throw std::invalid_argument("Preallocated objects cannot exceed the maximum pool size"); |
41 | 0 | } |
42 | | |
43 | 1 | if (initialSize > 0) |
44 | 1 | { |
45 | 1 | this->preallocate(initialSize); |
46 | 1 | } |
47 | 1 | } |
48 | | |
49 | | // These don't strictly need to be deleted, but don't need to be implemented for now either. |
50 | | DynamicObjectPool(const DynamicObjectPool&) = delete; |
51 | | DynamicObjectPool(DynamicObjectPool&&) = delete; |
52 | | DynamicObjectPool& operator=(const DynamicObjectPool&) = delete; |
53 | | DynamicObjectPool& operator=(DynamicObjectPool&&) = delete; |
54 | | |
55 | | /// A destructor for this class that deletes all objects in the pool |
56 | | ~DynamicObjectPool() |
57 | 1 | { |
58 | 1 | clear(); |
59 | 1 | } |
60 | | |
61 | | /// @brief Acquires a unique pointer to an object from the pool. |
62 | | /// |
63 | | /// This method acquires a unique pointer to an object from the pool. |
64 | | /// If the pool is empty, a new object will be created. |
65 | | /// |
66 | | /// @return A unique pointer to an object from the pool. |
67 | | std::unique_ptr<T> acquireObject() |
68 | 774 | { |
69 | 774 | return std::unique_ptr<T>(acquireObjectRaw()); |
70 | 774 | } |
71 | | |
72 | | /// @brief Acquires a raw pointer to an object from the pool. |
73 | | /// |
74 | | /// This method acquires a raw pointer to an object from the pool. |
75 | | /// If the pool is empty, a new object will be created. |
76 | | /// |
77 | | /// @return A raw pointer to an object from the pool. |
78 | | T* acquireObjectRaw() |
79 | 774 | { |
80 | 774 | std::unique_lock<std::mutex> lock(m_Mutex); |
81 | | |
82 | 774 | if (m_Pool.empty()) |
83 | 0 | { |
84 | | // We don't need the lock anymore, so release it. |
85 | 0 | lock.unlock(); |
86 | 0 | return new T(); |
87 | 0 | } |
88 | | |
89 | 774 | T* obj = m_Pool.top(); |
90 | 774 | m_Pool.pop(); |
91 | 774 | return obj; |
92 | 774 | } |
93 | | |
94 | | /// @brief Releases a unique pointer to an object back to the pool. |
95 | | /// |
96 | | /// This method releases a unique pointer to an object back to the pool. |
97 | | /// If the pool is full, the object will be deleted. |
98 | | /// |
99 | | /// @param[in] obj The unique pointer to the object to release. |
100 | | void releaseObject(std::unique_ptr<T> obj) |
101 | 774 | { |
102 | 774 | releaseObjectRaw(obj.release()); |
103 | 774 | } |
104 | | |
105 | | /// @brief Releases a raw pointer to an object back to the pool. |
106 | | /// |
107 | | /// This method releases a raw pointer to an object back to the pool. |
108 | | /// If the pool is full, the object will be deleted. |
109 | | /// |
110 | | /// @param[in] obj The raw pointer to the object to release. |
111 | | void releaseObjectRaw(T* obj) |
112 | 774 | { |
113 | 774 | std::unique_lock<std::mutex> lock(m_Mutex); |
114 | | |
115 | 774 | if (m_MaxPoolSize == INFINITE_POOL_SIZE || m_Pool.size() < m_MaxPoolSize) |
116 | 774 | { |
117 | 774 | m_Pool.push(obj); |
118 | 774 | } |
119 | 0 | else |
120 | 0 | { |
121 | | // We don't need the lock anymore, so release it. |
122 | 0 | lock.unlock(); |
123 | 0 | delete obj; |
124 | 0 | } |
125 | 774 | } |
126 | | |
127 | | /// @brief Gets the current number of objects in the pool. |
128 | | std::size_t size() const |
129 | | { |
130 | | std::lock_guard<std::mutex> lock(m_Mutex); |
131 | | return m_Pool.size(); |
132 | | } |
133 | | |
134 | | /// @brief Gets the maximum number of objects in the pool. |
135 | | std::size_t maxSize() const |
136 | | { |
137 | | std::lock_guard<std::mutex> lock(m_Mutex); |
138 | | return m_MaxPoolSize; |
139 | | } |
140 | | |
141 | | /// @brief Sets the maximum number of objects in the pool. |
142 | | void setMaxSize(std::size_t maxSize) |
143 | 0 | { |
144 | 0 | std::lock_guard<std::mutex> lock(m_Mutex); |
145 | 0 | m_MaxPoolSize = maxSize; |
146 | 0 |
|
147 | 0 | // If the new max size is less than the current size, we need to remove some objects from the pool. |
148 | 0 | while (m_Pool.size() > m_MaxPoolSize) |
149 | 0 | { |
150 | 0 | delete m_Pool.top(); |
151 | 0 | m_Pool.pop(); |
152 | 0 | } |
153 | 0 | } |
154 | | |
155 | | /// @brief Pre-allocates up to a minimum number of objects in the pool. |
156 | | /// @param count The number of objects to pre-allocate. |
157 | | void preallocate(std::size_t count) |
158 | 1 | { |
159 | 1 | std::unique_lock<std::mutex> lock(m_Mutex); |
160 | | |
161 | 1 | if (m_MaxPoolSize < count) |
162 | 0 | { |
163 | 0 | throw std::invalid_argument("Preallocated objects cannot exceed the maximum pool size"); |
164 | 0 | } |
165 | | |
166 | | // If the pool is already larger than the requested count, we don't need to do anything. |
167 | 3 | for (std::size_t i = m_Pool.size(); i < count; i++) |
168 | 2 | { |
169 | 2 | m_Pool.push(new T()); |
170 | 2 | } |
171 | 1 | } |
172 | | |
173 | | /// @brief Deallocates and releases all objects currently held by the pool. |
174 | | void clear() |
175 | 1 | { |
176 | 1 | std::unique_lock<std::mutex> lock(m_Mutex); |
177 | 3 | while (!m_Pool.empty()) |
178 | 2 | { |
179 | 2 | delete m_Pool.top(); |
180 | 2 | m_Pool.pop(); |
181 | 2 | } |
182 | 1 | } |
183 | | |
184 | | private: |
185 | | std::size_t m_MaxPoolSize; ///< The maximum number of objects in the pool |
186 | | mutable std::mutex m_Mutex; ///< Mutex for thread safety |
187 | | std::stack<T*> m_Pool; ///< The pool of objects |
188 | | }; |
189 | | } // namespace internal |
190 | | } // namespace pcpp |