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 2*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_(2*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(60);
80 
81  while (freeSlots_ < 1) {
82  if (boost::get_system_time() >= timeout) {
83  syslog(LOG_USER | LOG_WARNING, "Timeout...%d seconds", 60);
84  break;
85  }
86  available_.timed_wait(lock, timeout);
87  }
88 
89  // If there is any in the queue, give one from there
90  if (free_.size() > 0) {
91  e = free_.front();
92  free_.pop_front();
93  // May have expired! In this case the element as to be destroyed,
94  // and the conclusion is that we have not found a good one in the pool
95  if (!factory_->isValid(e)) {
96  factory_->destroy(e);
97  }
98  else
99  found = true;
100  }
101 
102  } // lock
103 
104  // We create a new element out of the lock. This may help for elements that need other elements
105  // of the same type to be constructed (sigh)
106  if (!found)
107  e = factory_->create();
108 
109  { // lock scope (again, sigh)
110  boost::mutex::scoped_lock lock(mutex_);
111  // Keep track of used
112  used_.insert(std::pair<E, unsigned>(e, 1));
113 
114  // Note that in case of timeout freeSlots_ can become negative
115  --freeSlots_;
116  }
117  return e;
118  }
119 
120  /// Increases the reference count of a resource.
121  E acquire(E e)
122  {
123  boost::mutex::scoped_lock lock(mutex_);
124 
125  // Make sure it is there
126  typename std::map<E, unsigned>::const_iterator i = used_.find(e);
127  if (i == used_.end()) {
128  throw DmException(DMLITE_SYSERR(EINVAL), std::string("The resource has not been locked previously!"));
129  }
130 
131  // Increase
132  used_[e]++;
133 
134  // End
135  return e;
136  }
137 
138  /// Releases a resource
139  /// @param e The resource to release.
140  /// @return The reference count after releasing.
141  unsigned release(E e)
142  {
143  boost::mutex::scoped_lock lock(mutex_);
144  // Decrease reference count
145  unsigned remaining = --used_[e];
146  // No one else using it (hopefully...)
147  if (used_[e] == 0) {
148  // Remove from used
149  used_.erase(e);
150  // If the free size is less than the maximum, push to free and notify
151  if ((long)free_.size() < max_) {
152  free_.push_back(e);
153  }
154  else {
155  // If we are fine, destroy
156  factory_->destroy(e);
157  }
158  }
159  available_.notify_one();
160  ++freeSlots_;
161 
162  return remaining;
163  }
164 
165  /// Count the number of instances
166  unsigned refCount(E e)
167  {
168  typename std::map<E, unsigned>::const_iterator i = used_.find(e);
169  if (i == used_.end())
170  return 0;
171  return used_[e];
172  }
173 
174  /// Change the pool size
175  /// @param ns The new size.
176  void resize(int ns)
177  {
178  // The resizing will be done as we get requests
179  boost::mutex::scoped_lock lock(mutex_);
180  max_ = ns;
181 
182 
183  freeSlots_ = 2*max_ - used_.size();
184  // Increment the semaphore size if needed
185  // Take into account the used
186  if (freeSlots_ > 0)
187  available_.notify_all();
188  }
189 
190  private:
191  // The max count of pooled instances
192  int max_;
193 
195 
196  std::deque<E> free_;
197  std::map<E, unsigned> used_;
199 
200  boost::mutex mutex_;
201  boost::condition_variable available_;
202  };
203 
204  /// Convenience class that releases a resource on destruction
205  template <class E>
206  class PoolGrabber {
207  public:
208  PoolGrabber(PoolContainer<E>& pool, bool block = true): pool_(pool)
209  {
210  element_ = pool_.acquire(block);
211  }
212 
214  pool_.release(element_);
215  }
216 
217  operator E ()
218  {
219  return element_;
220  }
221 
222  private:
225  };
226 };
227 
228 #endif // DMLITE_CPP_UTILS_POOLCONTAINER_H
Convenience class that releases a resource on destruction.
Definition: poolcontainer.h:206
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:176
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:200
unsigned refCount(E e)
Count the number of instances.
Definition: poolcontainer.h:166
E acquire(E e)
Increases the reference count of a resource.
Definition: poolcontainer.h:121
std::map< E, unsigned > used_
Definition: poolcontainer.h:197
Base exception class.
Definition: exceptions.h:17
PoolContainer< E > & pool_
Definition: poolcontainer.h:223
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:196
~PoolGrabber()
Definition: poolcontainer.h:213
virtual E create()=0
Creates an element.
E element_
Definition: poolcontainer.h:224
unsigned release(E e)
Definition: poolcontainer.h:141
int max_
Definition: poolcontainer.h:192
~PoolContainer()
Destructor.
Definition: poolcontainer.h:48
int freeSlots_
Definition: poolcontainer.h:198
boost::condition_variable available_
Definition: poolcontainer.h:201
Definition: poolcontainer.h:20
PoolElementFactory< E > * factory_
Definition: poolcontainer.h:194
PoolGrabber(PoolContainer< E > &pool, bool block=true)
Definition: poolcontainer.h:208