Boost :: asio acceptor avoid memory leak

Using boost :: asio I use async_accept to receive connections. This works well, but there is one problem, and I need to suggest how to deal with it. Using regular 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)); } 

It works fine, but there is a problem: the Request object is created using the usual new one , so that it can "leak" memory. Not quite a leak, it only occurs when the program stops, but I want to make valgrind happy.

Of course, there is an option: I can replace it with shared_ptr and pass it to each event handler. This will work until the program stops, when asio io_service stops, all objects will be destroyed, and the request will be free. But in this way, I should always have an active asio event for Request , otherwise it will be destroyed! I think its a direct path to failure, so I also don't like this option.

UPD Third option: Listener contains a shared_ptr list for active connections. It looks great, and I prefer to use it unless a better way is found. The disadvantage is this: since this scheme allows you to do garbage collection on connection downtimes, it is unsafe: deleting a pointer to a connection with Listener will immediately destroy it, which can lead to segfault when any of the connection handlers is active in another thread. Using mutex can not fix this cus in this case we should block almost everything.

Is there a way to make the acceptor work with connection management in a beautiful and safe way? I will be glad to hear any suggestions.

+4
source share
3 answers

A typical recipe for preventing memory leaks when using this library uses shared_ptr , the io_service documentation specifically mentions this

Note

The destruction sequence described above allows programs to simplify their resource management using shared_ptr<> . Where the object lifetime is tied to the lifetime of the connection (or some other sequence of asynchronous operations), a shared_pt r for the object to be associated with handlers for all associated asynchronous operations with this. This works as follows:

When one connection ends, all associated asynchronous operations are complete. The corresponding handler objects are destroyed, and all shared_ptr object references are destroyed. To close the whole program, the io_service stop() function is called to end any call to run() as soon as possible. The io_service destructor defined above destroys all handlers, invoking all shared_ptr links to all connection objects to be destroyed.

For your script, modify your Listener::handle_accept( ) method to accept the boost::shared_ptr<Request> parameter. Your second concern

removing the connection pointer from the Listener will immediately destroy it, which can lead to segfault when any connection handler in another topic is active. Using mutex can not fix this cus, in this case we should block almost everything.

softened by inheriting from the boost::enable_shared_from_this in your classes:

 class Listener : public boost::enable_shared_from_this<Listener> { ... }; 

then when sending handlers, use shared_from_this() instead of this when linking to Listener member functions.

+1
source

If anyone is interested, I found another way. Listener contains a shared_ptr list for active connections. Connections ending / ending are done via io_service::post , which call Listener::FinishConnection wrapped in asio::strand . Usually I always port request methods using strand - this is more secure in terms of DDOS and / or thread safety. So calling FinishConnection from post using strand protects against segfault in another thread

0
source

I'm not sure if this is directly related to your problem, but I also had similar memory leaks using Boost Asio libraries, in particular the same acceptor object that you talked about. It turned out that I did not close the service correctly; some connections will remain open, and their corresponding objects will not be freed from memory. A call to the following eliminated the leaks reported by Valgrind:

 acceptor.close(); 

Hope this can be helpful for someone!

0
source

Source: https://habr.com/ru/post/1437262/


All Articles