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

Generated on 3 Mar 2013 for dmlite by  doxygen 1.4.7