ObjectPool.h

00001 /* This file is Copyright 2000-2008 Meyer Sound Laboratories Inc.  See the included LICENSE.txt file for details. */
00002 
00003 #ifndef MuscleObjectPool_h
00004 #define MuscleObjectPool_h
00005 
00006 #include "util/Queue.h"
00007 #include "system/Mutex.h"
00008 
00009 BEGIN_NAMESPACE(muscle);
00010 
00011 // Uncomment this #define to disable object pools (i.e. turn them into
00012 // fancy new/delete operators).  This is helpful if you are trying
00013 // to track down memory leaks.
00014 //#define DISABLE_OBJECT_POOLING 1
00015 
00016 #ifndef MUSCLE_POOL_SLAB_SIZE
00017 # define MUSCLE_POOL_SLAB_SIZE (4*1024)  // let's have each slab fit nicely into a 4KB page
00018 #endif
00019 
00023 class AbstractObjectGenerator
00024 {
00025 public:
00027    AbstractObjectGenerator() {/* empty */}
00028 
00030    virtual ~AbstractObjectGenerator() {/* empty */}
00031 
00037    virtual void * ObtainObjectGeneric() = 0;
00038 };
00039 
00043 class AbstractObjectRecycler
00044 {
00045 public:
00047    AbstractObjectRecycler();
00048 
00050    virtual ~AbstractObjectRecycler();
00051 
00057    virtual void RecycleObject(void * obj) = 0;
00058 
00062    virtual uint32 FlushCachedObjects() = 0;
00063 
00070    static void GlobalFlushAllCachedObjects();
00071 
00072 private:
00073    AbstractObjectRecycler * _prev;
00074    AbstractObjectRecycler * _next;
00075 };
00076 
00080 class AbstractObjectManager : public AbstractObjectGenerator, public AbstractObjectRecycler
00081 {
00082    // empty
00083 };
00084 
00092 template <class Object> class ObjectPool : public AbstractObjectManager
00093 {
00094 public:
00096    typedef void (*ObjectCallback)(Object * obj, void * userData);
00097 
00106    ObjectPool(uint32 maxPoolSize=100, 
00107               ObjectCallback recycleCallback = NULL, void * recycleData = NULL,
00108               ObjectCallback initCallback = NULL, void * initCallbackData = NULL) : 
00109       _initObjectFunc(initCallback), _initObjectUserData(initCallbackData),
00110       _recycleObjectFunc(recycleCallback), _recycleObjectUserData(recycleData),
00111       _curPoolSize(0), _maxPoolSize(maxPoolSize), _firstSlab(NULL), _lastSlab(NULL)
00112    {
00113       // empty
00114    }
00115 
00119    virtual ~ObjectPool()
00120    {
00121       while(_firstSlab)
00122       {
00123          if (_firstSlab->IsInUse()) 
00124          {
00125             LogTime(MUSCLE_LOG_CRITICALERROR, "~ObjectPool %p:  slab %p is still in use when we destroy it!\n", this, _firstSlab);
00126             MCRASH("ObjectPool destroyed while its objects were still in use (Is a CompleteSetupSystem object declared at the top of main()?");
00127          }
00128          ObjectSlab * nextSlab = _firstSlab->GetNext();
00129          delete _firstSlab;
00130          _firstSlab = nextSlab;
00131       }
00132    }
00133 
00140    Object * ObtainObject()
00141    {
00142 #ifdef DISABLE_OBJECT_POOLING
00143       Object * ret = newnothrow Object;
00144       if (ret) 
00145       {
00146          ret->SetManager(this);
00147          if (_initObjectFunc) _initObjectFunc(ret, _initObjectUserData);
00148       }
00149       else WARN_OUT_OF_MEMORY;
00150       return ret;
00151 #else
00152       Object * ret = NULL;
00153       if (_mutex.Lock() == B_NO_ERROR)
00154       {
00155          if ((_firstSlab)&&(_firstSlab->HasAvailableNodes()))
00156          {
00157             ret = _firstSlab->ObtainObjectNode();
00158             if ((_firstSlab->HasAvailableNodes() == false)&&(_firstSlab != _lastSlab))
00159             {
00160                // Move _firstSlab out of the way (to the end of the slab list) for next time
00161                ObjectSlab * tmp = _firstSlab;  // use temp var since _firstSlab will change
00162                tmp->RemoveFromSlabList();
00163                tmp->AppendToSlabList();
00164             }
00165          }
00166          else
00167          {
00168             // Hmm, we must have run out out of non-full slabs.  Create a new slab and use it.
00169             ObjectSlab * slab = newnothrow ObjectSlab(this);
00170             if (slab)
00171             {
00172                ret = slab->ObtainObjectNode();  // guaranteed not to fail, since slab is new
00173                if (slab->HasAvailableNodes()) slab->PrependToSlabList();
00174                                          else slab->AppendToSlabList();  // could happen, if NUM_OBJECTS_PER_SLAB==1
00175                _curPoolSize += NUM_OBJECTS_PER_SLAB;
00176             }
00177             // we'll do the WARN_OUT_OF_MEMORY below, outside the mutex lock
00178          }
00179          if (ret) --_curPoolSize;
00180          _mutex.Unlock();
00181       }
00182       if (ret)
00183       {
00184          ret->SetManager(this);
00185          if (_initObjectFunc) _initObjectFunc(ret, _initObjectUserData);
00186       }
00187       else WARN_OUT_OF_MEMORY;
00188       return ret;
00189 #endif
00190    }
00191 
00197    void ReleaseObject(Object * obj)
00198    {
00199       if (obj)
00200       {
00201          MASSERT(obj->GetManager()==this, "ObjectPool::ReleaseObject was passed an object that it never allocated!");
00202          if (_recycleObjectFunc) _recycleObjectFunc(obj, _recycleObjectUserData);
00203          obj->SetManager(NULL);
00204 
00205 #ifdef DISABLE_OBJECT_POOLING
00206          delete obj;
00207 #else
00208          if (_mutex.Lock() == B_NO_ERROR)
00209          {
00210             ObjectSlab * slabToDelete = NULL;
00211             ObjectNode * objNode = static_cast<ObjectNode *>(obj);
00212             ObjectSlab * objSlab = objNode->GetSlab();
00213 
00214             objSlab->ReleaseNode(objNode);  // guaranteed to work, since we know (obj) is in use in (objSlab)
00215 
00216             if ((++_curPoolSize > (_maxPoolSize+NUM_OBJECTS_PER_SLAB))&&(objSlab->IsInUse() == false))
00217             {
00218                _curPoolSize -= NUM_OBJECTS_PER_SLAB;
00219                objSlab->RemoveFromSlabList();
00220                slabToDelete = objSlab;
00221             }
00222             else if (objSlab != _firstSlab) 
00223             {
00224                objSlab->RemoveFromSlabList();
00225                objSlab->PrependToSlabList();
00226             }
00227             _mutex.Unlock();
00228 
00229             delete slabToDelete;  // do this outside the critical section, for better concurrency
00230          }
00231          else WARN_OUT_OF_MEMORY;  // critical error -- not really out of memory but still
00232 #endif
00233       }
00234    }
00235 
00237    virtual void * ObtainObjectGeneric() {return ObtainObject();}
00238 
00240    virtual void RecycleObject(void * obj) {ReleaseObject((Object *)obj);}
00241     
00252    status_t SetInitObjectCallback(ObjectCallback cb, void * userData)
00253    {
00254       if (_mutex.Lock() == B_NO_ERROR)
00255       {
00256          _initObjectFunc = cb;
00257          _initObjectUserData = userData;
00258          (void) _mutex.Unlock();
00259          return B_NO_ERROR;
00260       }
00261       else return B_ERROR;
00262    }
00263 
00273    void SetRecycleObjectCallback(ObjectCallback cb, void * userData)
00274    {
00275       if (_mutex.Lock() == B_NO_ERROR)
00276       {
00277          _recycleObjectFunc = cb;
00278          _recycleObjectUserData = userData;
00279          (void) _mutex.Unlock();
00280          return B_NO_ERROR;
00281       }
00282       else return B_ERROR;
00283    }
00284 
00286    virtual uint32 FlushCachedObjects() {uint32 ret = 0; (void) Drain(&ret); return ret;}
00287 
00293    status_t Drain(uint32 * optSetNumDrained = NULL)
00294    {
00295       if (_mutex.Lock() == B_NO_ERROR)
00296       {
00297          // This will be our linked list of slabs to delete, later
00298          ObjectSlab * toDelete = NULL;
00299 
00300          // Pull out all the slabs that are not currently in use
00301          ObjectSlab * slab = _firstSlab;
00302          while(slab)
00303          {
00304             ObjectSlab * nextSlab = slab->GetNext();
00305             if (slab->IsInUse() == false)
00306             {
00307                slab->RemoveFromSlabList();
00308                slab->SetNext(toDelete);
00309                toDelete = slab;
00310             }
00311             slab = nextSlab;
00312          }
00313          (void) _mutex.Unlock();
00314 
00315          // Do the actual slab deletions outside of the critical section, for better concurrency
00316          uint32 numObjectsDeleted = 0;
00317          while(toDelete)
00318          {
00319             ObjectSlab * nextSlab = toDelete->GetNext();
00320 
00321             numObjectsDeleted += NUM_OBJECTS_PER_SLAB;
00322             _curPoolSize -= NUM_OBJECTS_PER_SLAB;
00323 
00324             delete toDelete;
00325             toDelete = nextSlab;
00326          }
00327 
00328          if (optSetNumDrained) *optSetNumDrained = numObjectsDeleted;
00329          return B_NO_ERROR;
00330       }
00331       else return B_ERROR;
00332    }
00333 
00338    uint32 GetMaxPoolSize() const {return _maxPoolSize;}
00339 
00345    void SetMaxPoolSize(uint32 mps) {_maxPoolSize = mps;}
00346 
00347 private:
00348    Mutex _mutex;
00349 
00350    ObjectCallback _initObjectFunc;
00351    void * _initObjectUserData;
00352    
00353    ObjectCallback _recycleObjectFunc;
00354    void * _recycleObjectUserData;
00355 
00356    class ObjectSlab;
00357 
00358    class ObjectNode : public Object
00359    {
00360    public:
00361       ObjectNode() : _slab(NULL), _next(NULL) {/* empty */}
00362 
00363       void SetSlab(ObjectSlab * slab) {_slab = slab;}
00364       ObjectSlab * GetSlab() const {return _slab;}
00365 
00366       void SetNext(ObjectNode * next) {_next = next;}
00367       ObjectNode * GetNext() const {return _next;}
00368 
00369    private:
00370       ObjectSlab * _slab;
00371       ObjectNode * _next;  // only used when we are in the free list
00372    };
00373 
00374    friend class ObjectSlab;  // for VC++ compatibility, this must be here
00375 
00376    // All the (int) casts are here so that it the user specifies a slab size of zero, we will get a negative
00377    // number and not a very large positive number that crashes the compiler!
00378    #define __POOL_OPS__ (((int)MUSCLE_POOL_SLAB_SIZE-(5*(int)sizeof(void *)))/(int)sizeof(ObjectNode))
00379    enum {NUM_OBJECTS_PER_SLAB = ((__POOL_OPS__>0)?__POOL_OPS__:1)};
00380    
00381    class ObjectSlab
00382    {
00383    public:
00384       // Note that _prev and _next are deliberately not set here... we don't use them until we are added to the list
00385       ObjectSlab(ObjectPool * pool) : _pool(pool), _firstFreeNode(NULL), _numNodesInUse(0)
00386       {
00387          for (int32 i=0; i<NUM_OBJECTS_PER_SLAB; i++)
00388          {
00389             ObjectNode * n = &_nodes[i];
00390             n->SetSlab(this);
00391             n->SetNext(_firstFreeNode);
00392             _firstFreeNode = n;
00393          }
00394       }
00395 
00397       bool HasAvailableNodes() const {return (_firstFreeNode != NULL);}
00398 
00400       bool IsInUse() const {return (_numNodesInUse > 0);}
00401 
00402       // Note:  this method assumes a node is available!  Don't call it without
00403       //        calling HasAvailableNodes() first to check, or you will crash!
00404       ObjectNode * ObtainObjectNode()
00405       {
00406          ObjectNode * ret = _firstFreeNode;
00407          _firstFreeNode = ret->GetNext();
00408          ++_numNodesInUse;
00409          return ret;
00410       }
00411 
00413       void ReleaseNode(ObjectNode * node)
00414       {
00415          node->SetNext(_firstFreeNode);
00416          _firstFreeNode = node; 
00417          --_numNodesInUse;
00418       }
00419 
00420       void RemoveFromSlabList()
00421       {
00422          (_prev ? _prev->_next : _pool->_firstSlab) = _next;
00423          (_next ? _next->_prev : _pool->_lastSlab)  = _prev;
00424       }
00425 
00426       void AppendToSlabList()
00427       {
00428          _prev = _pool->_lastSlab;
00429          _next = NULL;
00430          (_prev ? _prev->_next : _pool->_firstSlab) = _pool->_lastSlab = this;
00431       }
00432 
00433       void PrependToSlabList()
00434       {
00435          _prev = NULL;
00436          _next = _pool->_firstSlab;
00437          (_next ? _next->_prev : _pool->_lastSlab) = _pool->_firstSlab = this;
00438       }
00439 
00440       void SetNext(ObjectSlab * next) {_next = next;}
00441       ObjectSlab * GetNext() const {return _next;}
00442 
00443    private:
00444       ObjectPool * _pool;
00445       ObjectSlab * _prev;
00446       ObjectSlab * _next;
00447       ObjectNode * _firstFreeNode;
00448       uint32 _numNodesInUse;
00449       ObjectNode _nodes[NUM_OBJECTS_PER_SLAB];
00450    };
00451 
00452    uint32 _curPoolSize;  // tracks the current number of "available" objects
00453    uint32 _maxPoolSize;  // the maximum desired number of "available" objects
00454    ObjectSlab * _firstSlab;
00455    ObjectSlab * _lastSlab;
00456 };
00457 
00458 END_NAMESPACE(muscle);
00459 
00460 #endif

Generated on Thu Jun 5 17:47:53 2008 for MUSCLE by  doxygen 1.5.1