1 | // |
2 | // ObjectPool.h |
3 | // |
4 | // Library: Foundation |
5 | // Package: Core |
6 | // Module: ObjectPool |
7 | // |
8 | // Definition of the ObjectPool template class and friends. |
9 | // |
10 | // Copyright (c) 2010-2012, Applied Informatics Software Engineering GmbH. |
11 | // and Contributors. |
12 | // |
13 | // SPDX-License-Identifier: BSL-1.0 |
14 | // |
15 | |
16 | |
17 | #ifndef Foundation_ObjectPool_INCLUDED |
18 | #define Foundation_ObjectPool_INCLUDED |
19 | |
20 | |
21 | #include "Poco/Poco.h" |
22 | #include "Poco/Mutex.h" |
23 | #include "Poco/Condition.h" |
24 | #include "Poco/AutoPtr.h" |
25 | #include "Poco/SharedPtr.h" |
26 | #include <vector> |
27 | #include <cctype> |
28 | |
29 | |
30 | namespace Poco { |
31 | |
32 | |
33 | template <class C, class P = C*> |
34 | class PoolableObjectFactory |
35 | /// A PoolableObjectFactory is responsible for creating and resetting |
36 | /// objects managed by an ObjectPool. |
37 | /// |
38 | /// Together with an ObjectPool, a PoolableObjectFactory is used as |
39 | /// a policy class to change the behavior of the ObjectPool when |
40 | /// creating new objects, returning used objects back to the pool |
41 | /// and destroying objects, when the pool itself is destroyed or |
42 | /// shrunk. |
43 | { |
44 | public: |
45 | P createObject() |
46 | /// Create and return a new object. |
47 | { |
48 | return new C; |
49 | } |
50 | |
51 | bool validateObject(P pObject) |
52 | /// Checks whether the object is still valid |
53 | /// and can be reused. |
54 | /// |
55 | /// Returns true if the object is valid, |
56 | /// false otherwise. |
57 | /// |
58 | /// To maintain the integrity of the pool, this method |
59 | /// must not throw an exception. |
60 | { |
61 | return true; |
62 | } |
63 | |
64 | void activateObject(P pObject) |
65 | /// Called before an object is handed out by the pool. |
66 | /// Also called for newly created objects, before |
67 | /// they are given out for the first time. |
68 | { |
69 | } |
70 | |
71 | void deactivateObject(P pObject) |
72 | /// Called after an object has been given back to the |
73 | /// pool and the object is still valid (a prior call |
74 | /// to validateObject() returned true). |
75 | /// |
76 | /// To maintain the integrity of the pool, this method |
77 | /// must not throw an exception. |
78 | { |
79 | } |
80 | |
81 | void destroyObject(P pObject) |
82 | /// Destroy an object. |
83 | /// |
84 | /// To maintain the integrity of the pool, this method |
85 | /// must not throw an exception. |
86 | { |
87 | delete pObject; |
88 | } |
89 | }; |
90 | |
91 | |
92 | template <class C> |
93 | class PoolableObjectFactory <C, Poco::AutoPtr<C> > |
94 | { |
95 | public: |
96 | Poco::AutoPtr<C> createObject() |
97 | { |
98 | return new C; |
99 | } |
100 | |
101 | bool validateObject(Poco::AutoPtr<C> pObject) |
102 | { |
103 | return true; |
104 | } |
105 | |
106 | void activateObject(Poco::AutoPtr<C> pObject) |
107 | { |
108 | } |
109 | |
110 | void deactivateObject(Poco::AutoPtr<C> pObject) |
111 | { |
112 | } |
113 | |
114 | void destroyObject(Poco::AutoPtr<C> pObject) |
115 | { |
116 | } |
117 | }; |
118 | |
119 | |
120 | template <class C> |
121 | class PoolableObjectFactory <C, Poco::SharedPtr<C> > |
122 | { |
123 | public: |
124 | Poco::SharedPtr<C> createObject() |
125 | { |
126 | return new C; |
127 | } |
128 | |
129 | bool validateObject(Poco::SharedPtr<C> pObject) |
130 | { |
131 | return true; |
132 | } |
133 | |
134 | void activateObject(Poco::SharedPtr<C> pObject) |
135 | { |
136 | } |
137 | |
138 | void deactivateObject(Poco::SharedPtr<C> pObject) |
139 | { |
140 | } |
141 | |
142 | void destroyObject(Poco::SharedPtr<C> pObject) |
143 | { |
144 | } |
145 | }; |
146 | |
147 | |
148 | template <class C, class P = C*, class F = PoolableObjectFactory<C, P> > |
149 | class ObjectPool |
150 | /// An ObjectPool manages a pool of objects of a certain class. |
151 | /// |
152 | /// The number of objects managed by the pool can be restricted. |
153 | /// |
154 | /// When an object is requested from the pool: |
155 | /// - If an object is available from the pool, an object from the pool is |
156 | /// removed from the pool, activated (using the factory) and returned. |
157 | /// - Otherwise, if the peak capacity of the pool has not yet been reached, |
158 | /// a new object is created and activated, using the object factory, and returned. |
159 | /// - If the peak capacity has already been reached, null is returned after timeout. |
160 | /// |
161 | /// When an object is returned to the pool: |
162 | /// - If the object is valid (checked by calling validateObject() |
163 | /// from the object factory), the object is deactivated. If the |
164 | /// number of objects in the pool is below the capacity, |
165 | /// the object is added to the pool. Otherwise it is destroyed. |
166 | /// - If the object is not valid, it is destroyed immediately. |
167 | { |
168 | public: |
169 | ObjectPool(std::size_t capacity, std::size_t peakCapacity): |
170 | /// Creates a new ObjectPool with the given capacity |
171 | /// and peak capacity. |
172 | /// |
173 | /// The PoolableObjectFactory must have a public default constructor. |
174 | _capacity(capacity), |
175 | _peakCapacity(peakCapacity), |
176 | _size(0) |
177 | { |
178 | poco_assert (capacity <= peakCapacity); |
179 | } |
180 | |
181 | ObjectPool(const F& factory, std::size_t capacity, std::size_t peakCapacity): |
182 | /// Creates a new ObjectPool with the given PoolableObjectFactory, |
183 | /// capacity and peak capacity. The PoolableObjectFactory must have |
184 | /// a public copy constructor. |
185 | _factory(factory), |
186 | _capacity(capacity), |
187 | _peakCapacity(peakCapacity), |
188 | _size(0) |
189 | { |
190 | poco_assert (capacity <= peakCapacity); |
191 | } |
192 | |
193 | ~ObjectPool() |
194 | /// Destroys the ObjectPool. |
195 | { |
196 | try |
197 | { |
198 | for (typename std::vector<P>::iterator it = _pool.begin(); it != _pool.end(); ++it) |
199 | { |
200 | _factory.destroyObject(*it); |
201 | } |
202 | } |
203 | catch (...) |
204 | { |
205 | poco_unexpected(); |
206 | } |
207 | } |
208 | |
209 | P borrowObject(long timeoutMilliseconds = 0) |
210 | /// Obtains an object from the pool, or creates a new object if |
211 | /// possible. |
212 | /// |
213 | /// Returns null if no object is available after timeout. |
214 | /// |
215 | /// If activating the object fails, the object is destroyed and |
216 | /// the exception is passed on to the caller. |
217 | { |
218 | Poco::FastMutex::ScopedLock lock(_mutex); |
219 | |
220 | if (!_pool.empty()) |
221 | { |
222 | P pObject = _pool.back(); |
223 | _pool.pop_back(); |
224 | return activateObject(pObject); |
225 | } |
226 | |
227 | if (_size >= _peakCapacity) |
228 | { |
229 | if (timeoutMilliseconds == 0) |
230 | { |
231 | return 0; |
232 | } |
233 | while (_size >= _peakCapacity) |
234 | { |
235 | if ( !_availableCondition.tryWait(_mutex, timeoutMilliseconds)) |
236 | { |
237 | // timeout |
238 | return 0; |
239 | } |
240 | } |
241 | } |
242 | |
243 | // _size < _peakCapacity |
244 | P pObject = _factory.createObject(); |
245 | activateObject(pObject); |
246 | _size++; |
247 | return pObject; |
248 | } |
249 | |
250 | void returnObject(P pObject) |
251 | /// Returns an object to the pool. |
252 | { |
253 | Poco::FastMutex::ScopedLock lock(_mutex); |
254 | |
255 | if (_factory.validateObject(pObject)) |
256 | { |
257 | _factory.deactivateObject(pObject); |
258 | if (_pool.size() < _capacity) |
259 | { |
260 | try |
261 | { |
262 | _pool.push_back(pObject); |
263 | return; |
264 | } |
265 | catch (...) |
266 | { |
267 | } |
268 | } |
269 | } |
270 | _factory.destroyObject(pObject); |
271 | _size--; |
272 | _availableCondition.signal(); |
273 | } |
274 | |
275 | std::size_t capacity() const |
276 | { |
277 | return _capacity; |
278 | } |
279 | |
280 | std::size_t peakCapacity() const |
281 | { |
282 | return _peakCapacity; |
283 | } |
284 | |
285 | std::size_t size() const |
286 | { |
287 | Poco::FastMutex::ScopedLock lock(_mutex); |
288 | |
289 | return _size; |
290 | } |
291 | |
292 | std::size_t available() const |
293 | { |
294 | Poco::FastMutex::ScopedLock lock(_mutex); |
295 | |
296 | return _pool.size() + _peakCapacity - _size; |
297 | } |
298 | |
299 | protected: |
300 | P activateObject(P pObject) |
301 | { |
302 | try |
303 | { |
304 | _factory.activateObject(pObject); |
305 | } |
306 | catch (...) |
307 | { |
308 | _factory.destroyObject(pObject); |
309 | throw; |
310 | } |
311 | return pObject; |
312 | } |
313 | |
314 | private: |
315 | ObjectPool(); |
316 | ObjectPool(const ObjectPool&); |
317 | ObjectPool& operator = (const ObjectPool&); |
318 | |
319 | F _factory; |
320 | std::size_t _capacity; |
321 | std::size_t _peakCapacity; |
322 | std::size_t _size; |
323 | std::vector<P> _pool; |
324 | mutable Poco::FastMutex _mutex; |
325 | Poco::Condition _availableCondition; |
326 | }; |
327 | |
328 | |
329 | } // namespace Poco |
330 | |
331 | |
332 | #endif // Foundation_ObjectPool_INCLUDED |
333 | |