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