AnCH Framework  0.1
Another C++ Hack Framework
resourcePool.hpp
1 /*
2  ANCH Framework: ANother C++ Hack is a C++ framework based on C++11 standard
3  Copyright (C) 2012 Vincent Lachenal
4 
5  This file is part of ANCH Framework.
6 
7  ANCH Framework is free software: you can redistribute it and/or modify
8  it under the terms of the GNU Lesser General Public License as published by
9  the Free Software Foundation, either version 3 of the License, or
10  (at your option) any later version.
11 
12  ANCH Framework is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  GNU Lesser General Public License for more details.
16 
17  You should have received a copy of the GNU Lesser General Public License
18  along with ANCH Framework. If not, see <http://www.gnu.org/licenses/>.
19 */
20 #ifndef _ANCH_RES_POOL_H_
21 #define _ANCH_RES_POOL_H_
22 
23 #include <memory>
24 #include <mutex>
25 #include <chrono>
26 #include <condition_variable>
27 #include <deque>
28 #include <atomic>
29 
30 
31 namespace anch {
32 
42  class TimeoutException: std::exception {
43 
44  // Attributes +
45  private:
47  std::chrono::milliseconds _timeout;
48 
50  std::string _msg;
51  // Attributes -
52 
53  // Constructors +
54  public:
61  TimeoutException(const std::chrono::milliseconds& timeout, const std::string msg) noexcept: std::exception(),
62  _timeout(timeout),
63  _msg(msg) {
64  }
65  // Constructors -
66 
67  // Destructor +
68  public:
72  virtual ~TimeoutException() {
73  // nothing to do
74  }
75  // Destructor -
76 
77  // Accessors +
78  public:
84  virtual const char* what() const noexcept {
85  return _msg.data();
86  }
87 
93  inline const std::chrono::milliseconds& getTimeout() const noexcept {
94  return _timeout;
95  }
96  // Accessors -
97 
98  };
99 
116  template<typename T, typename C, std::shared_ptr<T>(*make_ptr)(const C&) = std::make_shared<T> >
117  class ResourcePool {
118 
128  class PoolableResource {
129 
130  // Attributes +
131  private:
134 
136  std::shared_ptr<T> _ptr;
137  // Attributes -
138 
139  // Constructors +
140  public:
144  PoolableResource(ResourcePool<T, C, make_ptr>& pool, std::shared_ptr<T> ptr): _pool(pool), _ptr(ptr) {
145  // Nothing to do
146  }
147  // Constructors -
148 
149  // Destructor +
150  public:
154  virtual ~PoolableResource() {
155  if(_ptr.get()->isValid()) {
156  _pool.returnResource(_ptr);
157  } else {
158  _pool.invalidateResource(_ptr);
159  }
160  }
161  // Destructor -
162 
163  // Accessors +
164  public:
170  inline T& get() {
171  return *(_ptr.get());
172  }
173  // Accessors -
174  };
175 
176  // Attributes +
177  private:
179  std::mutex _mutex;
180 
182  std::condition_variable _wait;
183 
185  std::mutex _waitex;
186 
188  std::deque<std::shared_ptr<T> > _availables;
189 
191  std::size_t _maxSize;
192 
194  std::atomic_size_t _used;
195 
197  C _config;
198 
200  std::chrono::milliseconds _timeout;
201  // Attributes -
202 
203  // Constructors +
204  public:
213  ResourcePool(const C& config,
214  std::size_t maxSize,
215  std::size_t initialiSize = 0,
216  std::chrono::milliseconds timeout = std::chrono::milliseconds(100)):
217  _mutex(),
218  _wait(),
219  _waitex(),
220  _availables(),
221  _maxSize(maxSize),
222  _used(0),
223  _config(config),
224  _timeout(timeout) {
225  for(std::size_t i = 0 ; i < initialiSize && i < maxSize ; ++i) {
226  try {
227  std::shared_ptr<T> ptr = make_ptr(_config);
228  _availables.push_back(ptr);
229  } catch(...) {
230  // Nothing to do => resources will be instanciated later ... or not
231  }
232  }
233  }
234 
238  ResourcePool(const ResourcePool&) = delete;
239  // Constructors -
240 
241  // Destructor +
242  public:
247  virtual ~ResourcePool() {
248  while(!_availables.empty()) {
249  _availables.front().reset();
250  _availables.pop_front();
251  }
252  }
253  // Destructor -
254 
255  // Methods +
256  public:
266  PoolableResource borrowResource() {
267  _mutex.lock();
268  std::shared_ptr<T> ptr;
269  if(_availables.empty()) {
270  if(_used.load() < _maxSize) {
271  try {
272  ptr = make_ptr(_config);
273  } catch(...) {
274  _mutex.unlock();
275  throw;
276  }
277  _used.fetch_add(1);
278  _mutex.unlock();
279 
280  } else {
281  _mutex.unlock();
282  std::unique_lock<std::mutex> lock(_waitex);
283  if(_wait.wait_for(lock, _timeout, [this](){return !this->_availables.empty();})) {
284  _mutex.lock();
285  ptr = _availables.front();
286  _availables.pop_front();
287  _used.fetch_add(1);
288  _mutex.unlock();
289  } else {
290  throw TimeoutException(_timeout, "Resource could not be retrieved.");
291  }
292  }
293 
294  } else {
295  ptr = _availables.front();
296  _availables.pop_front();
297  _used.fetch_add(1);
298  _mutex.unlock();
299  }
300  return PoolableResource(*this, ptr);
301  }
302 
303  private:
309  void returnResource(std::shared_ptr<T> res) {
310  std::lock_guard<std::mutex> lock(_mutex);
311  _availables.push_back(res);
312  _used.fetch_sub(1);
313  _wait.notify_one();
314  }
315 
322  void invalidateResource(std::shared_ptr<T> res) {
323  res.reset();
324  _used.fetch_sub(1);
325  try {
326  _availables.push_back(make_ptr(_config));
327  _wait.notify_one();
328  } catch(...) {
329  // Nothing to do => until resource will be instanciate other waiters will fail on timeout
330  }
331  }
332  // Methods -
333 
334  // Accessors +
335  public:
341  inline void setTimeout(const std::chrono::milliseconds timeout) {
342  _timeout = timeout;
343  }
344  // Accessors -
345 
346  };
347 
348 }
349 
350 #endif // _ANCH_RES_POOL_H_
virtual ~ResourcePool()
Definition: resourcePool.hpp:247
virtual const char * what() const noexcept
Definition: resourcePool.hpp:84
Timeout exception.
Definition: resourcePool.hpp:42
AnCH framework base namespace.
Definition: base64.hpp:28
const std::chrono::milliseconds & getTimeout() const noexcept
Definition: resourcePool.hpp:93
TimeoutException(const std::chrono::milliseconds &timeout, const std::string msg) noexcept
Definition: resourcePool.hpp:61
virtual ~TimeoutException()
Definition: resourcePool.hpp:72
ResourcePool(const C &config, std::size_t maxSize, std::size_t initialiSize=0, std::chrono::milliseconds timeout=std::chrono::milliseconds(100))
Definition: resourcePool.hpp:213
Generic resource pool utility class.
Definition: resourcePool.hpp:117
PoolableResource borrowResource()
Definition: resourcePool.hpp:266
void setTimeout(const std::chrono::milliseconds timeout)
Definition: resourcePool.hpp:341