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