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 <map>
10 #include <syslog.h>
11 #include <queue>
12 #include "../exceptions.h"
13 
14 namespace dmlite {
15 
16  /// Classes implementing this interface creates the actual element
17  /// since the pool is agnosstic
18  template <class E>
20  public:
21  /// Destructor
22  virtual ~PoolElementFactory() {};
23 
24  /// Creates an element
25  virtual E create() = 0;
26 
27  /// Destroys an element
28  virtual void destroy(E) = 0;
29 
30  /// Check it is still valid
31  virtual bool isValid(E) = 0;
32  };
33 
34 
35  /// Implements a pool of whichever resource
36  template <class E>
37  class PoolContainer {
38  public:
39  /// Constructor
40  /// @param factory The factory to use when spawning a new resource.
41  /// @param n The number of resources to keep.
42  PoolContainer(PoolElementFactory<E>* factory, int n): max_(n), factory_(factory), freeSlots_(n)
43  {
44  }
45 
46  /// Destructor
48  {
49  // Free 'free'
50  while (free_.size() > 0) {
51  E e = free_.front();
52  free_.pop();
53  factory_->destroy(e);
54  }
55  // Freeing used is dangerous, as we might block if the client code
56  // forgot about something. Assume the memory leak :(
57  if (used_.size() > 0) {
58  syslog(LOG_USER | LOG_WARNING, "%ld used elements from a pool not released on destruction!", (long)used_.size());
59  }
60  }
61 
62  /// Acquires a free resource.
63  E acquire(bool block = true)
64  {
65  E e;
66  // Wait for one free
67  if (!block && freeSlots_ == 0) {
68  throw DmException(DMLITE_SYSERR(EBUSY),
69  std::string("No resources available"));
70  }
71 
72  boost::mutex::scoped_lock lock(mutex_);
73  while (freeSlots_ < 1)
74  available_.wait(lock);
75 
76  // If there is any in the queue, give one from there
77  if (free_.size() > 0) {
78  e = free_.front();
79  free_.pop();
80  // May have expired!
81  if (!factory_->isValid(e)) {
82  factory_->destroy(e);
83  e = factory_->create();
84  }
85  }
86  else {
87  // None created, so create it now
88  e = factory_->create();
89  }
90  // Keep track of used
91  used_.insert(std::pair<E, unsigned>(e, 1));
92  --freeSlots_;
93 
94  return e;
95  }
96 
97  /// Increases the reference count of a resource.
98  E acquire(E e)
99  {
100  boost::mutex::scoped_lock lock(mutex_);
101 
102  // Make sure it is there
103  typename std::map<E, unsigned>::const_iterator i = used_.find(e);
104  if (i == used_.end())
105  throw DmException(DMLITE_SYSERR(EINVAL),
106  std::string("The resource has not been locked previously!"));
107 
108  // Increase
109  used_[e]++;
110 
111  // End
112  return e;
113  }
114 
115  /// Releases a resource
116  /// @param e The resource to release.
117  /// @return The reference count after releasing.
118  unsigned release(E e)
119  {
120  boost::mutex::scoped_lock lock(mutex_);
121  // Decrease reference count
122  unsigned remaining = --used_[e];
123  // No one else using it (hopefully...)
124  if (used_[e] == 0) {
125  // Remove from used
126  used_.erase(e);
127  // If the free size is less than the maximum, push to free and notify
128  if ((long)free_.size() < max_) {
129  free_.push(e);
130  available_.notify_one();
131  }
132  else {
133  // If we are fine, destroy
134  factory_->destroy(e);
135  }
136  }
137  ++freeSlots_;
138 
139  return remaining;
140  }
141 
142  /// Count the number of instances
143  unsigned refCount(E e)
144  {
145  typename std::map<E, unsigned>::const_iterator i = used_.find(e);
146  if (i == used_.end())
147  return 0;
148  return used_[e];
149  }
150 
151  /// Change the pool size
152  /// @param ns The new size.
153  void resize(int ns)
154  {
155  int total, sv;
156  // The resizing will be done as we get requests
157  boost::mutex::scoped_lock lock(mutex_);
158  max_ = ns;
159  freeSlots_ = max_ - used_.size();
160  // Increment the semaphore size if needed
161  // Take into account the used
162  if (freeSlots_ > 0)
163  available_.notify_all();
164  }
165 
166  private:
167  int max_;
168 
170 
171  std::queue<E> free_;
172  std::map<E, unsigned> used_;
173  unsigned freeSlots_;
174 
175  boost::mutex mutex_;
176  boost::condition_variable available_;
177  };
178 
179  /// Convenience class that releases a resource on destruction
180  template <class E>
181  class PoolGrabber {
182  public:
183  PoolGrabber(PoolContainer<E>& pool, bool block = true): pool_(pool)
184  {
185  element_ = pool_.acquire(block);
186  }
187 
189  pool_.release(element_);
190  }
191 
192  operator E ()
193  {
194  return element_;
195  }
196 
197  private:
200  };
201 };
202 
203 #endif // DMLITE_CPP_UTILS_POOLCONTAINER_H