ASIO socket termination crashed at boost::asio::async_read
Hi,
I use boost TCP socket for months, it works well until I call a
termination of smart pointer which is running at
boost::asio::async_read. Could anyone explain what could be the cause
for the following errors?
void Comms::HandleReadBody(const boost::system::error_code &error,
const uint32_t length) {
................
boost::asio::async_read(this->mHandler->Socket(),
boost::asio::buffer(this->mHeader, sizeof(this->mHeader)), [=](const
boost::system::error_code &status, const uint32_t length)
{this->HandleReadHeader(status, length);});
...............
}
(gdb) run
2019-07-15 11:47:32.396184: Sanity check socket connection and
terminate it when it is inactive
.............
pure virtual method called
terminate called without an active exception
Program received signal SIGABRT, Aborted.
(gdb) backtrace
#0 __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:51
#1 0x00007ffff7a24801 in __GI_abort () at abort.c:79
#2 0x00007ffff5565957 in ?? () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#3 0x00007ffff556bab6 in ?? () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#4 0x00007ffff556baf1 in std::terminate() ()
from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#5 0x00007ffff556c8bf in __cxa_pure_virtual ()
from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#6 HandleReadHeader(boost::system::error_code const&, unsigned int) ()
#7 0x00007ffff679a116 in
boost::asio::detail::read_op
::do_complete(boost::asio::detail::task_io_service*, boost::asio::detail::task_io_service_operation*, boost::system::error_code const&, unsigned long) () #10 0x000055555559f5c0 in boost::asio::detail::task_io_service::do_run_one(boost::asio::detail::scoped_lockboost::asio::detail::posix_mutex&, boost::asio::detail::task_io_service_thread_info&, boost::system::error_code const&) () #11 0x000055555559f747 in boost::asio::detail::task_io_service::run(boost::system::error_code&) ()
On 16/07/2019 00:35, JH wrote:
I use boost TCP socket for months, it works well until I call a termination of smart pointer which is running at boost::asio::async_read. Could anyone explain what could be the cause for the following errors?
Be careful of the order of operations when you have pending async actions. In particular, when you close a socket, the async read will be cancelled and a cancellation callback will be queued to the io_context -- but you must still guarantee that all of the objects involved in the callback for that operation (including the socket and the read buffer, plus anything else accessed by the completion handler) are still kept alive until the callback actually executes. A simple trick to help ensure this (as demonstrated in several of the Asio examples) is to pass a shared_ptr to the container of the socket and read buffer to the async_read operation (eg. as a bound lambda argument); this will keep that object and its children alive until the handler is actually called. Of course, then you can't use destruction of that object to trigger the close in the first place, which can have consequences for the rest of your app. There are some ways around this, such as separating the "real" container of the socket/buffer from the "logical" container from the perspective of the rest of the app.
On 7/16/19, Gavin Lambert via Boost
Be careful of the order of operations when you have pending async actions.
In particular, when you close a socket, the async read will be cancelled and a cancellation callback will be queued to the io_context -- but you must still guarantee that all of the objects involved in the callback for that operation (including the socket and the read buffer, plus anything else accessed by the completion handler) are still kept alive until the callback actually executes.
That seems the cause of the crash, if I terminated the socket object after it connected to the remote server but before it transmitted any messages, it was fine, but once it transmitted messages to the remote server, it crashed in termination process.
A simple trick to help ensure this (as demonstrated in several of the Asio examples) is to pass a shared_ptr to the container of the socket and read buffer to the async_read operation (eg. as a bound lambda argument); this will keep that object and its children alive until the handler is actually called.
Right, the socket is in shared_ptr, but the read buf is not, need be fixed.
Of course, then you can't use destruction of that object to trigger the close in the first place, which can have consequences for the rest of your app. There are some ways around this, such as separating the "real" container of the socket/buffer from the "logical" container from the perspective of the rest of the app.
Did you mean if to use class destructor to trigger the close, it would
cause errors like pure virtual method called terminate called without
an active exception? Did you allude every close need be explicitly
called in functions not the destructors, especially the derived the
class?
On 7/16/19, Mathias Gaunard
You need to guarantee "this" still lives when the callback runs.
You probably want to capture a shared ptr of "this" rather than "this" here. enable_shared_from_this is typically used for these scenarios.
So all the callbacks need be wrapped by shared_ptr? Does that mean the bind callback: boost::bind(&SocketHandler::Handshake, this, boost::asio::placeholders::error) Should be replaced by boost::bind(&shared_ptrSocketHandler::Handshake, this, boost::asio::placeholders::error) Will it be compiled? Will try it. Thank you all, so many tricks. - jh
On 17/07/2019 23:03, JH wrote:
Of course, then you can't use destruction of that object to trigger the close in the first place, which can have consequences for the rest of your app. There are some ways around this, such as separating the "real" container of the socket/buffer from the "logical" container from the perspective of the rest of the app.
Did you mean if to use class destructor to trigger the close, it would cause errors like pure virtual method called terminate called without an active exception? Did you allude every close need be explicitly called in functions not the destructors, especially the derived the class?
Calling close() in the destructor of the class that the handlers are defined on is rarely the correct choice. If you're not using shared_ptr, then you've called close() too late -- there is still an outstanding async operation but you're in the process of deleting the object that will be called when that (later) completes, meaning that you have a dangling pointer and will be accessing deleted memory later. If you are using shared_ptr, then the close() in the destructor is (mostly) pointless, because the destructor won't get called until all the pending operations are already ended, which *usually* means that something else has already called close() first. (Although you have to be a little careful of loopholes, such as when your async_read handler doesn't start a new read, either due to error or exception. Still, as long as you're holding the socket by value and you don't have any pending async operations on it, it's safe to just let the socket be destroyed without explicitly close()ing it.) So yes, you should be calling close() from some other method. This might be an explicit disconnection operation, or following a failed read operation, or in the destructor of some other class that is different from the shared_ptr used for the operations themselves. A useful trick is to implement the pimpl idiom using a shared_ptr -- your 'outer' public class can call close() in its destructor because the shared_ptr is to the 'inner' private class instead. Just avoid calling "out" from the inner class to the outer class in handlers, because the inner class will have a longer lifetime than the outer class.
So all the callbacks need be wrapped by shared_ptr? Does that mean the bind callback:
boost::bind(&SocketHandler::Handshake, this, boost::asio::placeholders::error)
Should be replaced by boost::bind(&shared_ptrSocketHandler::Handshake, this, boost::asio::placeholders::error)
No, not even slightly. Have a look at the example code included with Asio.
On Mon, 15 Jul 2019 at 13:35, JH via Boost
Hi,
I use boost TCP socket for months, it works well until I call a termination of smart pointer which is running at boost::asio::async_read. Could anyone explain what could be the cause for the following errors?
void Comms::HandleReadBody(const boost::system::error_code &error, const uint32_t length) { ................ boost::asio::async_read(this->mHandler->Socket(), boost::asio::buffer(this->mHeader, sizeof(this->mHeader)), [=](const boost::system::error_code &status, const uint32_t length) {this->HandleReadHeader(status, length);}); ............... }
You need to guarantee "this" still lives when the callback runs. You probably want to capture a shared ptr of "this" rather than "this" here. enable_shared_from_this is typically used for these scenarios.
On Mon, Jul 15, 2019 at 5:35 AM JH via Boost
Could anyone explain what could be the cause for the following errors?
If you watch my presentation at about 25 and a half minutes in, I explain how to manage the lifetime of connection objects ("this" in your code): https://youtu.be/7FQwAjELMek?t=1543 Regards
participants (4)
-
Gavin Lambert
-
JH
-
Mathias Gaunard
-
Vinnie Falco