Re: [Boost-users] How to design proper release of a boost::asio socket or wrapper thereof
On 04/05/2017 10:32 PM, Christopher Pisz via Boost-users wrote:
The problem I have with this example listing is that it cheats and it cheats big, by making the tcp_connection a shared_ptr, such that it doesn't worry about the lifetime management of each connection.
That is not cheating.
Consider we'd have a callstack that looked something like this: tcp_connection::~tcp_connection tcp_server::OnDisconnect tcp_connection::OnDisconnect tcp_connection::Disconnect
If Disconnect is an asynchronous operation, then it should not call OnDisconnect directly. You can post it on the io_service instead.
I imagine everyone doing server programming comes across this scenario in some fashion. What is a strategy for handling it?
Preferably shared_ptr, or io_service::post() if for some reason you cannot use shared_ptr, e.g. if the object has to be destroyed by a a specific thread.
Using shared_ptr is absolutely cheating. I do not think it is real world at all to maintain a connection's lifetime through the use of outstanding receive calls. A simple scenario as to why it is cheating is: What if the server wants to send "Hello" to all clients every 5 minutes? You need a collection. If you have a collection, then you maintain a reference count when using shared_ptrs, and just killed the tutorials lifetime management scheme. In the end, you need to know when it is safe to remove the connection from a collection and destroy it. If I post disconnect, then I am guaranteed no other outstanding IO operation is in progress for that connection. That's a step in the right direction. Only if I have one and only one thread that called io_service.run, but then I still have to know, "when is it safe to destroy my connection object?" Let's say I posted a callback to tcp_server::Disconnect(tcp_connection * connection) from tcp_connection::OnReceive. Is it safe for me then to delete tcp_connection in the body and therefore the socket as well? Will no "cancel" notifications be called back afterward when tcp_server::Disconnect then calls close() on the socket? Or should I call close() back in OnReceive and post a callback to tcp_server::OnDisconnect, which simply deletes the connection object? I think the latter might make sense. I will make an attempt at this in code. Meanwhile a little more clarification on the order of events there might help. Thanks! -- View this message in context: http://boost.2283326.n4.nabble.com/How-to-design-proper-release-of-a-boost-a... Sent from the Boost - Users mailing list archive at Nabble.com.
On 6/04/2017 10:14, Christopher Pisz via Boost-users wrote:
What if the server wants to send "Hello" to all clients every 5 minutes? You need a collection. If you have a collection, then you maintain a reference count when using shared_ptrs, and just killed the tutorials lifetime management scheme. In the end, you need to know when it is safe to remove the connection from a collection and destroy it.
Make a collection of weak_ptrs. When you go through and try to send to them, some of them will have expired, and you can remove them from the collection then. The rest are still alive (although some might error out when you try to send to them, due to the way TCP works; if that happens you can just close() them and they'll be expired the next time you check). If you want to explicitly disconnect a given connection, you simply need to call shutdown() and close() on it (via a method on your connection class), which will abort the pending receive and release the internal references, so once you discard the local shared_ptr you just used to close it then the connection object will be destroyed automatically. (Note that it's not safe to assume you can destroy the object immediately after calling close() on the socket -- if the receive was still pending, it still needs to call the completion handler with the error, which might happen later on a different thread. This is why using shared_ptr is the preferred solution as it handles different lifetime ordering correctly without explicit locking.)
Genious. Thank you sir. On Wed, Apr 5, 2017 at 10:02 PM, Gavin Lambert via Boost-users < boost-users@lists.boost.org> wrote:
On 6/04/2017 10:14, Christopher Pisz via Boost-users wrote:
What if the server wants to send "Hello" to all clients every 5 minutes? You need a collection. If you have a collection, then you maintain a reference count when using shared_ptrs, and just killed the tutorials lifetime management scheme. In the end, you need to know when it is safe to remove the connection from a collection and destroy it.
Make a collection of weak_ptrs. When you go through and try to send to them, some of them will have expired, and you can remove them from the collection then. The rest are still alive (although some might error out when you try to send to them, due to the way TCP works; if that happens you can just close() them and they'll be expired the next time you check).
If you want to explicitly disconnect a given connection, you simply need to call shutdown() and close() on it (via a method on your connection class), which will abort the pending receive and release the internal references, so once you discard the local shared_ptr you just used to close it then the connection object will be destroyed automatically.
(Note that it's not safe to assume you can destroy the object immediately after calling close() on the socket -- if the receive was still pending, it still needs to call the completion handler with the error, which might happen later on a different thread. This is why using shared_ptr is the preferred solution as it handles different lifetime ordering correctly without explicit locking.)
_______________________________________________ Boost-users mailing list Boost-users@lists.boost.org http://lists.boost.org/mailman/listinfo.cgi/boost-users
participants (3)
-
Bjorn Reese
-
Christopher Pisz
-
Gavin Lambert