I implement an ObjectPool using unique_ptr that allocates an array of T objects and uses 2 lists (free and busy) to handle the acquire() and release() in O(1). Here is the implementation:
`template <class T> class SimpleObjectPool {
public:
using pointer = std::unique_ptr<T,std::function<void(T*)> >;
using PoolType = SimpleObjectPool<T>;
SimpleObjectPool(uint64_t size) : _poolSize(size) { allocate(); }
SimpleObjectPool(const PoolType& orig) = delete;
virtual ~SimpleObjectPool() {
for(uint64_t i = 0; i < _poolIndex; i++)
delete _poolVector[i];
}
int poolSize() const {
return _poolSize * _poolIndex;
}
auto acquire() {
std::lock_guard<std::recursive_mutex> guard(_mutex);
if(_freeList.empty()) allocate();
uint64_t freeIndex = getFreeObjectIndex();
auto lastObjectItr = _busyList.end();
lastObjectItr--;
_poolVector[freeIndex/_poolSize]->_objects[freeIndex%_poolSize].clear();
return std::move(pointer(&_poolVector[freeIndex/_poolSize]->_objects[freeIndex%_poolSize],
[this,lastObjectItr](T* element)->void{freeObject(element,lastObjectItr);}));
}
void release(pointer objectPtr) {
std::lock_guard<std::recursive_mutex> guard(_mutex);
objectPtr.reset();
}
int freeObjectSize() { return _freeList.size(); }
int busyObjectSize() { return _busyList.size(); }
private:
struct objectArray {
objectArray(uint64_t size) {
_objects = new T[size];
}
~objectArray() {
delete [] _objects;
}
T * _objects = nullptr;
};
void allocate() {
std::lock_guard<std::recursive_mutex> guard(_mutex);
std::cout << "allocate" << std::endl;
_poolVector.push_back(new objectArray(_poolSize));
for (uint64_t i = 0; i < _poolSize; i++) {
uint64_t* index = new uint64_t(_poolIndex*_poolSize + i);
_freeList.push_back(index);
}
_poolIndex++;
}
void freeObject(T* element, std::list<uint64_t*>::iterator iter) {
std::lock_guard<std::recursive_mutex> guard(_mutex);
_freeList.push_back(*iter);
_busyList.erase(iter);
}
uint64_t getFreeObjectIndex(){
uint64_t* index = _freeList.front();
_freeList.pop_front();
_busyList.push_back(index);
return *index;
}
uint64_t _poolSize = 0;
std::list<uint64_t*> _freeList;
std::list<uint64_t*> _busyList;
std::vector<objectArray*> _poolVector;
uint64_t _poolIndex = 0;
std::recursive_mutex _mutex;
};
`
But the problem is that the line return std::move(pointer(&_poolVector[freeIndex/_poolSize]->_objects[freeIndex%_poolSize], [this,lastObjectItr](T* element)->void{freeObject(element,lastObjectItr);}));
actually calls to new() so the whole point of using pre-allocated object is missed. Is there a way to overcome this behavior ?
`class TestFile {
public:
TestFile(){
clear();
//std::cout<<"Construct default TestFile"<<*this<<"\n";
}
~TestFile() {
//std::cout<<"Delete TestFile"<<*this<<"\n";
}
void clear(){
_fileId = -1;
_fileName.clear();
}
friend std::ostream& operator<<(std::ostream& ostream, const TestFile& test) {
ostream<<"["<<test._fileId<<",'"<<test._fileName<<"']";
return ostream;
}
void setFileId(int id) { _fileId = id; }
void setFileName(std::string name) { _fileName = name; }
private:
int _fileId;
std::string _fileName;
};
using TestPool = SimpleObjectPool<TestFile>;
bool simpleTest(int size) {
TestPool pool(size);
if(pool.freeObjectSize() != size) return false;
if(pool.busyObjectSize() != 0) return false;
auto pt1 = pool.acquire();
pt1->setFileId(1234);
pt1->setFileName("TestFileName");
if(pool.freeObjectSize() != size-1) return false;
if(pool.busyObjectSize() != 1) return false;
pool.release(std::move(pt1));
if(pt1 != nullptr) return false;
if(pool.freeObjectSize() != size) return false;
if(pool.busyObjectSize() != 0) return false;
return true;
}`
Aucun commentaire:
Enregistrer un commentaire