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  // The resizing will be done as we get requests
156  boost::mutex::scoped_lock lock(mutex_);
157  max_ = ns;
158  freeSlots_ = max_ - used_.size();
159  // Increment the semaphore size if needed
160  // Take into account the used
161  if (freeSlots_ > 0)
162  available_.notify_all();
163  }
164 
165  private:
166  int max_;
167 
169 
170  std::queue<E> free_;
171  std::map<E, unsigned> used_;
172  unsigned freeSlots_;
173 
174  boost::mutex mutex_;
175  boost::condition_variable available_;
176  };
177 
178  /// Convenience class that releases a resource on destruction
179  template <class E>
180  class PoolGrabber {
181  public:
182  PoolGrabber(PoolContainer<E>& pool, bool block = true): pool_(pool)
183  {
184  element_ = pool_.acquire(block);
185  }
186 
188  pool_.release(element_);
189  }
190 
191  operator E ()
192  {
193  return element_;
194  }
195 
196  private:
199  };
200 };
201 
202 #endif // DMLITE_CPP_UTILS_POOLCONTAINER_H