dmlite
0.6
|
00001 /// @file include/dmlite/cpp/utils/poolcontainer.h 00002 /// @brief Pooling 00003 /// @author Alejandro Álvarez Ayllón <aalvarez@cern.ch> 00004 #ifndef DMLITE_CPP_UTILS_POOLCONTAINER_H 00005 #define DMLITE_CPP_UTILS_POOLCONTAINER_H 00006 00007 #include <boost/thread/mutex.hpp> 00008 #include <boost/thread/condition.hpp> 00009 #include <map> 00010 #include <syslog.h> 00011 #include <queue> 00012 #include "../exceptions.h" 00013 00014 namespace dmlite { 00015 00016 /// Classes implementing this interface creates the actual element 00017 /// since the pool is agnosstic 00018 template <class E> 00019 class PoolElementFactory { 00020 public: 00021 /// Destructor 00022 virtual ~PoolElementFactory() {}; 00023 00024 /// Creates an element 00025 virtual E create() = 0; 00026 00027 /// Destroys an element 00028 virtual void destroy(E) = 0; 00029 00030 /// Check it is still valid 00031 virtual bool isValid(E) = 0; 00032 }; 00033 00034 00035 /// Implements a pool of whichever resource 00036 template <class E> 00037 class PoolContainer { 00038 public: 00039 /// Constructor 00040 /// @param factory The factory to use when spawning a new resource. 00041 /// @param n The number of resources to keep. 00042 PoolContainer(PoolElementFactory<E>* factory, int n): max_(n), factory_(factory), freeSlots_(n) 00043 { 00044 } 00045 00046 /// Destructor 00047 ~PoolContainer() 00048 { 00049 // Free 'free' 00050 while (free_.size() > 0) { 00051 E e = free_.front(); 00052 free_.pop(); 00053 factory_->destroy(e); 00054 } 00055 // Freeing used is dangerous, as we might block if the client code 00056 // forgot about something. Assume the memory leak :( 00057 if (used_.size() > 0) { 00058 syslog(LOG_USER | LOG_WARNING, "%ld used elements from a pool not released on destruction!", (long)used_.size()); 00059 } 00060 } 00061 00062 /// Acquires a free resource. 00063 E acquire(bool block = true) 00064 { 00065 E e; 00066 // Wait for one free 00067 if (!block && freeSlots_ == 0) { 00068 throw DmException(DMLITE_SYSERR(EBUSY), 00069 std::string("No resources available")); 00070 } 00071 00072 boost::mutex::scoped_lock lock(mutex_); 00073 while (freeSlots_ < 1) 00074 available_.wait(lock); 00075 00076 // If there is any in the queue, give one from there 00077 if (free_.size() > 0) { 00078 e = free_.front(); 00079 free_.pop(); 00080 // May have expired! 00081 if (!factory_->isValid(e)) { 00082 factory_->destroy(e); 00083 e = factory_->create(); 00084 } 00085 } 00086 else { 00087 // None created, so create it now 00088 e = factory_->create(); 00089 } 00090 // Keep track of used 00091 used_.insert(std::pair<E, unsigned>(e, 1)); 00092 --freeSlots_; 00093 00094 return e; 00095 } 00096 00097 /// Increases the reference count of a resource. 00098 E acquire(E e) 00099 { 00100 boost::mutex::scoped_lock lock(mutex_); 00101 00102 // Make sure it is there 00103 typename std::map<E, unsigned>::const_iterator i = used_.find(e); 00104 if (i == used_.end()) 00105 throw DmException(DMLITE_SYSERR(EINVAL), 00106 std::string("The resource has not been locked previously!")); 00107 00108 // Increase 00109 used_[e]++; 00110 00111 // End 00112 return e; 00113 } 00114 00115 /// Releases a resource 00116 /// @param e The resource to release. 00117 /// @return The reference count after releasing. 00118 unsigned release(E e) 00119 { 00120 boost::mutex::scoped_lock lock(mutex_); 00121 // Decrease reference count 00122 unsigned remaining = --used_[e]; 00123 // No one else using it (hopefully...) 00124 if (used_[e] == 0) { 00125 // Remove from used 00126 used_.erase(e); 00127 // If the free size is less than the maximum, push to free and notify 00128 if ((long)free_.size() < max_) { 00129 free_.push(e); 00130 available_.notify_one(); 00131 } 00132 else { 00133 // If we are fine, destroy 00134 factory_->destroy(e); 00135 } 00136 } 00137 ++freeSlots_; 00138 00139 return remaining; 00140 } 00141 00142 /// Count the number of instances 00143 unsigned refCount(E e) 00144 { 00145 typename std::map<E, unsigned>::const_iterator i = used_.find(e); 00146 if (i == used_.end()) 00147 return 0; 00148 return used_[e]; 00149 } 00150 00151 /// Change the pool size 00152 /// @param ns The new size. 00153 void resize(int ns) 00154 { 00155 int total, sv; 00156 // The resizing will be done as we get requests 00157 boost::mutex::scoped_lock lock(mutex_); 00158 max_ = ns; 00159 freeSlots_ = max_ - used_.size(); 00160 // Increment the semaphore size if needed 00161 // Take into account the used 00162 if (freeSlots_ > 0) 00163 available_.notify_all(); 00164 } 00165 00166 private: 00167 int max_; 00168 00169 PoolElementFactory<E> *factory_; 00170 00171 std::queue<E> free_; 00172 std::map<E, unsigned> used_; 00173 unsigned freeSlots_; 00174 00175 boost::mutex mutex_; 00176 boost::condition_variable available_; 00177 }; 00178 00179 /// Convenience class that releases a resource on destruction 00180 template <class E> 00181 class PoolGrabber { 00182 public: 00183 PoolGrabber(PoolContainer<E>& pool, bool block = true): pool_(pool) 00184 { 00185 element_ = pool_.acquire(block); 00186 } 00187 00188 ~PoolGrabber() { 00189 pool_.release(element_); 00190 } 00191 00192 operator E () 00193 { 00194 return element_; 00195 } 00196 00197 private: 00198 PoolContainer<E>& pool_; 00199 E element_; 00200 }; 00201 }; 00202 00203 #endif // DMLITE_CPP_UTILS_POOLCONTAINER_H