00001
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
00012
00013
00014
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() {}
00028
00030 virtual ~AbstractObjectGenerator() {}
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
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
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
00161 ObjectSlab * tmp = _firstSlab;
00162 tmp->RemoveFromSlabList();
00163 tmp->AppendToSlabList();
00164 }
00165 }
00166 else
00167 {
00168
00169 ObjectSlab * slab = newnothrow ObjectSlab(this);
00170 if (slab)
00171 {
00172 ret = slab->ObtainObjectNode();
00173 if (slab->HasAvailableNodes()) slab->PrependToSlabList();
00174 else slab->AppendToSlabList();
00175 _curPoolSize += NUM_OBJECTS_PER_SLAB;
00176 }
00177
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);
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;
00230 }
00231 else WARN_OUT_OF_MEMORY;
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
00298 ObjectSlab * toDelete = NULL;
00299
00300
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
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) {}
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;
00372 };
00373
00374 friend class ObjectSlab;
00375
00376
00377
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
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
00403
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;
00453 uint32 _maxPoolSize;
00454 ObjectSlab * _firstSlab;
00455 ObjectSlab * _lastSlab;
00456 };
00457
00458 END_NAMESPACE(muscle);
00459
00460 #endif