dmlite  0.6
poolcontainer.h
Go to the documentation of this file.
1 /// @file include/dmlite/cpp/utils/poolcontainer.h
2 /// @brief Pooling
3 /// @author Alejandro Álvarez Ayllón <aalvarez@cern.ch>
4 #ifndef DMLITE_CPP_UTILS_POOLCONTAINER_H
5 #define DMLITE_CPP_UTILS_POOLCONTAINER_H
6 
7 #include <boost/thread/mutex.hpp>
8 #include <boost/thread/condition.hpp>
9 #include <boost/date_time/posix_time/posix_time.hpp>
10 #include <map>
11 #include <syslog.h>
12 #include <queue>
13 #include "../exceptions.h"
14 
15 namespace dmlite {
16 
17  /// Classes implementing this interface creates the actual element
18  /// since the pool is agnosstic
19  template <class E>
21  public:
22  /// Destructor
23  virtual ~PoolElementFactory() {};
24 
25  /// Creates an element
26  virtual E create() = 0;
27 
28  /// Destroys an element
29  virtual void destroy(E) = 0;
30 
31  /// Check it is still valid
32  virtual bool isValid(E) = 0;
33  };
34 
35 
36  /// Implements a pool of whichever resource
37  template <class E>
38  class PoolContainer {
39  public:
40  /// Constructor
41  /// @param factory The factory to use when spawning a new resource.
42  /// @param n The number of resources to keep in the pool. Up to 10*n slots can be created without penalty (but only n will be pooled)
43  PoolContainer(PoolElementFactory<E>* factory, int n): max_(n), factory_(factory), freeSlots_(10*n)
44  {
45  }
46 
47  /// Destructor
49  {
50  boost::mutex::scoped_lock lock(mutex_);
51  // Free 'free'
52  while (free_.size() > 0) {
53  E e = free_.front();
54  free_.pop_front();
55  factory_->destroy(e);
56  }
57  // Freeing used is dangerous, as we might block if the client code
58  // forgot about something. Assume the memory leak :(
59  if (used_.size() > 0) {
60  syslog(LOG_USER | LOG_WARNING, "%ld used elements from a pool not released on destruction!", (long)used_.size());
61  }
62  }
63 
64  /// Acquires a free resource.
65  E acquire(bool block = true)
66  {
67  bool found = false;
68  E e = {};
69 
70  { // lock scope
71  boost::mutex::scoped_lock lock(mutex_);
72 
73  // Wait for one free
74  if (!block && (freeSlots_ <= 0)) {
75  throw DmException(DMLITE_SYSERR(EBUSY),
76  std::string("No resources available"));
77  }
78 
79  boost::system_time const timeout = boost::get_system_time() + boost::posix_time::seconds(1);
80 
81  while (freeSlots_ < 1) {
82  if (boost::get_system_time() >= timeout) {
83  syslog(LOG_USER | LOG_WARNING, "Poolcontainer timeout. Size: %d free (can be negative): %d Stall: %d seconds in '%s'", max_, freeSlots_, 1, __PRETTY_FUNCTION__);
84  break;
85  }
86  available_.timed_wait(lock, timeout);
87  }
88 
89  // If there is any in the queue, give one from there
90  // Destroy elements that are not valid until a good one is found
91  while (free_.size() > 0) {
92  e = free_.front();
93  free_.pop_front();
94  // May have expired! In this case the element has to be destroyed,
95  // and the conclusion is that we have not found a good one in the pool
96  if (!factory_->isValid(e)) {
97  factory_->destroy(e);
98  }
99  else {
100  found = true;
101  break;
102  }
103  }
104 
105  } // lock
106 
107  // We create a new element out of the lock. This may help for elements that need other elements
108  // of the same type to be constructed (sigh)
109  if (!found)
110  e = factory_->create();
111 
112  { // lock scope (again, sigh)
113  boost::mutex::scoped_lock lock(mutex_);
114  // Keep track of used
115  used_.insert(std::pair<E, unsigned>(e, 1));
116 
117  // Note that in case of timeout freeSlots_ can become negative
118  --freeSlots_;
119  }
120  return e;
121  }
122 
123  /// Increases the reference count of a resource.
124  E acquire(E e)
125  {
126  boost::mutex::scoped_lock lock(mutex_);
127 
128  // Make sure it is there
129  typename std::map<E, unsigned>::const_iterator i = used_.find(e);
130  if (i == used_.end()) {
131  throw DmException(DMLITE_SYSERR(EINVAL), std::string("The resource has not been locked previously!"));
132  }
133 
134  // Increase
135  used_[e]++;
136 
137  // End
138  return e;
139  }
140 
141  /// Releases a resource
142  /// @param e The resource to release.
143  /// @return The reference count after releasing.
144  unsigned release(E e)
145  {
146  boost::mutex::scoped_lock lock(mutex_);
147  // Decrease reference count
148  unsigned remaining = --used_[e];
149  // No one else using it (hopefully...)
150  if (used_[e] == 0) {
151  // Remove from used
152  used_.erase(e);
153  // If the free size is less than the maximum, push to free and notify
154  if ((long)free_.size() < max_) {
155  free_.push_back(e);
156  }
157  else {
158  // If we are fine, destroy
159  factory_->destroy(e);
160  }
161  }
162  available_.notify_one();
163  ++freeSlots_;
164 
165  return remaining;
166  }
167 
168  /// Count the number of instances
169  unsigned refCount(E e)
170  {
171  typename std::map<E, unsigned>::const_iterator i = used_.find(e);
172  if (i == used_.end())
173  return 0;
174  return used_[e];
175  }
176 
177  /// Change the pool size
178  /// @param ns The new size.
179  void resize(int ns)
180  {
181  // The resizing will be done as we get requests
182  boost::mutex::scoped_lock lock(mutex_);
183  max_ = ns;
184 
185 
186  freeSlots_ = 10*max_ - used_.size();
187  // Increment the semaphore size if needed
188  // Take into account the used
189  if (freeSlots_ > 0)
190  available_.notify_all();
191  }
192 
193  private:
194  // The max count of pooled instances
195  int max_;
196 
198 
199  std::deque<E> free_;
200  std::map<E, unsigned> used_;
202 
203  boost::mutex mutex_;
204  boost::condition_variable available_;
205  };
206 
207  /// Convenience class that releases a resource on destruction
208  template <class E>
209  class PoolGrabber {
210  public:
211  PoolGrabber(PoolContainer<E>& pool, bool block = true): pool_(pool)
212  {
213  element_ = pool_.acquire(block);
214  }
215 
217  pool_.release(element_);
218  }
219 
220  operator E ()
221  {
222  return element_;
223  }
224 
225  private:
228  };
229 };
230 
231 #endif // DMLITE_CPP_UTILS_POOLCONTAINER_H
Convenience class that releases a resource on destruction.
Definition: poolcontainer.h:209
virtual bool isValid(E)=0
Check it is still valid.
Implements a pool of whichever resource.
Definition: poolcontainer.h:38
void resize(int ns)
Definition: poolcontainer.h:179
PoolContainer(PoolElementFactory< E > *factory, int n)
Definition: poolcontainer.h:43
virtual void destroy(E)=0
Destroys an element.
#define DMLITE_SYSERR(e)
Definition: errno.h:32
boost::mutex mutex_
Definition: poolcontainer.h:203
unsigned refCount(E e)
Count the number of instances.
Definition: poolcontainer.h:169
E acquire(E e)
Increases the reference count of a resource.
Definition: poolcontainer.h:124
std::map< E, unsigned > used_
Definition: poolcontainer.h:200
Base exception class.
Definition: exceptions.h:17
PoolContainer< E > & pool_
Definition: poolcontainer.h:226
virtual ~PoolElementFactory()
Destructor.
Definition: poolcontainer.h:23
E acquire(bool block=true)
Acquires a free resource.
Definition: poolcontainer.h:65
std::deque< E > free_
Definition: poolcontainer.h:199
~PoolGrabber()
Definition: poolcontainer.h:216
virtual E create()=0
Creates an element.
E element_
Definition: poolcontainer.h:227
unsigned release(E e)
Definition: poolcontainer.h:144
int max_
Definition: poolcontainer.h:195
~PoolContainer()
Destructor.
Definition: poolcontainer.h:48
int freeSlots_
Definition: poolcontainer.h:201
boost::condition_variable available_
Definition: poolcontainer.h:204
Namespace for the dmlite C++ API.
Definition: authn.h:16
Definition: poolcontainer.h:20
PoolElementFactory< E > * factory_
Definition: poolcontainer.h:197
PoolGrabber(PoolContainer< E > &pool, bool block=true)
Definition: poolcontainer.h:211