include/dmlite/cpp/utils/poolcontainer.h

Go to the documentation of this file.
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       // The resizing will be done as we get requests
00156       boost::mutex::scoped_lock lock(mutex_);
00157       max_ = ns;
00158       freeSlots_ = max_ - used_.size();
00159       // Increment the semaphore size if needed
00160       // Take into account the used
00161       if (freeSlots_ > 0)
00162         available_.notify_all();
00163     }
00164 
00165    private:
00166     int max_;
00167 
00168     PoolElementFactory<E> *factory_;
00169 
00170     std::queue<E>         free_;
00171     std::map<E, unsigned> used_;
00172     unsigned freeSlots_;
00173 
00174     boost::mutex              mutex_;
00175     boost::condition_variable available_;
00176   };
00177 
00178   /// Convenience class that releases a resource on destruction
00179   template <class E>
00180   class PoolGrabber {
00181    public:
00182     PoolGrabber(PoolContainer<E>& pool, bool block = true): pool_(pool)
00183     {
00184       element_ = pool_.acquire(block);
00185     }
00186 
00187     ~PoolGrabber() {
00188       pool_.release(element_);
00189     }
00190 
00191     operator E ()
00192     {
00193       return element_;
00194     }
00195 
00196    private:
00197     PoolContainer<E>& pool_;
00198     E element_;
00199   };
00200 };
00201 
00202 #endif // DMLITE_CPP_UTILS_POOLCONTAINER_H

Generated on 11 Jul 2013 for dmlite by  doxygen 1.4.7