Reputation: 7357
Using boost::asio i use async_accept to accept connections. This works good, but there is one issue and i need a suggestion how to deal with it. Using typical async_accept:
Listener::Listener(int port)
: acceptor(io, ip::tcp::endpoint(ip::tcp::v4(), port))
, socket(io) {
start_accept();
}
void Listener::start_accept() {
Request *r = new Request(io);
acceptor.async_accept(r->socket(),
boost::bind(&Listener::handle_accept, this, r, placeholders::error));
}
Works fine but there is a issue: Request object is created with plain new so it can memory "leak". Not really a leak, it leaks only at program stop, but i want to make valgrind happy.
Sure there is an option: i can replace it with shared_ptr, and pass it to every event handler. This will work until program stop, when asio io_service is stopping, all objects will be destroyed and Request will be free'd. But this way i always must have an active asio event for Request, or it will be destroyed! I think its direct way to crash so i dont like this variant, too.
UPD Third variant: Listener
holds list of shared_ptr to active connections. Looks great and i prefer to use this unless some better way will be found. The drawback is: since this schema allows to do "garbage collection" on idle connects, its not safe: removing connection pointer from Listener will immediately destroy it, what can lead to segfault when some of connection's handler is active in other thread. Using mutex cant fix this cus in this case we must lock nearly anything.
Is there a way to make acceptor work with connection management some beautiful and safe way? I will be glad to hear any suggestions.
Upvotes: 4
Views: 2360
Reputation: 6483
Not sure whether this is directly related to your issue, but I was also having similar memory leaks by using the Boost Asio libraries, in particular the same acceptor
object you mentioned. Turned out that I was not shutting down the service correctly; some connections would stay opened and their corresponding objects would not be freed from memory. Calling the following got rid of the leaks reported by Valgrind:
acceptor.close();
Hope this can be useful for someone!
Upvotes: 0
Reputation: 7357
If anyone interested, i found another way. Listener
holds list of shared_ptr to active connections. Connections ending/terminating is made via io_service::post
which call Listener::FinishConnection
wrapped with asio::strand
. Usually i always wrap Request's methods with strand - its safer in terms of DDOS and/or thread safety. So, calling FinishConnection
from post
using strand
protects from segfault in other thread
Upvotes: 0
Reputation: 24164
The typical recipe for avoiding memory leaks when using this library is using a shared_ptr
, the io_service
documentation specifically mentions this
Remarks
The destruction sequence described above permits programs to simplify their resource management by using
shared_ptr<>
. Where an object's lifetime is tied to the lifetime of a connection (or some other sequence of asynchronous operations), ashared_pt
r to the object would be bound into the handlers for all asynchronous operations associated with it. This works as follows:When a single connection ends, all associated asynchronous operations complete. The corresponding handler objects are destroyed, and all shared_ptr references to the objects are destroyed. To shut down the whole program, the
io_service
functionstop()
is called to terminate anyrun()
calls as soon as possible. The io_service destructor defined above destroys all handlers, causing allshared_ptr
references to all connection objects to be destroyed.
For your scenario, change your Listener::handle_accept(
) method to take a boost::shared_ptr<Request>
parameter. Your second concern
removing connection pointer from Listener will immediately destroy it, what can lead to segfault when some of connection's handler is active in other thread. Using mutex cant fix this cus in this case we must lock nearly anything.
is mitigated by inheriting from the boost::enable_shared_from_this
template in your classes:
class Listener : public boost::enable_shared_from_this<Listener>
{
...
};
then when you dispatch handlers, use shared_from_this()
instead of this
when binding to member functions of Listener
.
Upvotes: 2