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
30namespace Poco {
31
32
33template <class C, class P = C*>
34class 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{
44public:
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
92template <class C>
93class PoolableObjectFactory <C, Poco::AutoPtr<C> >
94{
95public:
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
120template <class C>
121class PoolableObjectFactory <C, Poco::SharedPtr<C> >
122{
123public:
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
148template <class C, class P = C*, class F = PoolableObjectFactory<C, P> >
149class 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{
168public:
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
299protected:
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
314private:
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