2010/2/18 Ryan McConnehey <mccorywork@gmail.com>
I like your concept for deallocating the object that pool handed out. I am using a private static class within pool to provide the function to the shared_ptr destructor. This allowed the object to be delete if the pool was destroyed but causes a bottleneck for other pools of the same type.
As I understand your case is multiple buffers used in async operations in boost::asio.
First: to gain performance, you need some reusable objects pool mechanics to avoid constant allocation/deallocation of memory used by buffers.
Second: buffers must survive pool destruction while being used.
Am I correct?
I have two questions. One, shouldn't the PoolObjectDeleter should be a template class?
To template or not is totally up to you :) Do you need some generic reusable code(could take some time to develop) or you need fast feature implementation and move further?
Two, why is the PoolObjectDeleter passed a shared_ptr<void>? It seems the pool_mem is to help with reference counting to determine if the pool is destroyed. Yet, I thought the enable_shared_from_this took care of the reference counting with the pool.lock().
In my sample pool_mem is malloc'ed objects data bodies memory, it would cause undefined behavior if accessed after "free". To avoid that shared_ptr is passed to PoolObjectDeleter. To determine if the pool is destroyed weak_ptr<Pool> is used.
If I'm correct about your case I'd like to post some more code:
typedef shared_ptr<asio::buffer> buffer_ptr;
class PoolBufferDeleter
{
private:
weak_ptr<Pool> pool;
public:
PoolBufferDeleter(const shared_ptr<Pool> &p) : pool(p) {}
void operator()(asio::buffer *buf) {
shared_ptr<Pool> shared_pool = this->pool.lock();
if(shared_pool)
shared_pool->deallocate(buf);
else {
free(asio::buffer_cast<void*>(*buf));
delete buf;
}
}
};
class Pool : enable_shared_from_this<Pool> {
private:
list<buffer_ptr> buffers; // free to use buffers
public:
buffer_ptr allocate(size_t sz) { // minimal size to use
for(list<buffer_ptr>::iterator i = this->buffers.begin(); i!= this->buffers.end(); ++i)
if(asio::buffer_size(*i) >= sz) {
buffer_ptr ret_value = *i;
this->buffers.erase(i);
return ret_value;
}
// malloc is NOT checked!!
return buffer_ptr(new asio::buffer(malloc(sz), sz), // T *t
PoolBufferDeleter(shared_from_this())); // D d
}
void deallocate(asio::buffer *b) {
this->buffers.push_front(buffer_ptr(b));
}
};
Deleter holds a weak_ptr to pool, so it can determine pool state. It's alive - pass buffer to it, destroy otherwise.