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