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