0

I have a boost socket doing an async_read_some:

socket_.async_read_some(boost::asio::buffer(data_, max_length),
    boost::bind(&Session::handle_read, this,
    boost::asio::placeholders::error,
    boost::asio::placeholders::bytes_transferred));

When my Session class is deleted socket_.close() is called. I thought this would cancel the async_read_some and call Session::handle_read with an error.

However this is not the case and looking at basic_socket.hpp

/// Close the socket.
/**
* This function is used to close the socket. Any asynchronous send, receive
* or connect operations will be cancelled immediately, and will complete
* with the boost::asio::error::operation_aborted error.
*
* @throws boost::system::system_error Thrown on failure. Note that, even if
* the function indicates an error, the underlying descriptor is closed.
*
* @note For portable behaviour with respect to graceful closure of a
* connected socket, call shutdown() before closing the socket.
*/
void close()

there is no mention of reads being cancelled. So my question is, how do I cancel the read so I can close the socket cleanly?

Kirill Chernikov
  • 1,387
  • 8
  • 21
Ne0
  • 2,688
  • 3
  • 35
  • 49
  • Could you try to [call shutdown](http://stackoverflow.com/a/3068106/220636) on that socket? – nabulke Sep 10 '13 at 11:14
  • I just tried `socket_.shutdown(boost::asio::basic_socket::shutdown_type::shutdown_both);` before calling close (as your link suggests), but still `Session::handle_read` isn't called. – Ne0 Sep 10 '13 at 11:28
  • "Any asynchronous send, receive or connect operations will be cancelled immediately, and will complete with the boost::asio::error::operation_aborted error." – Igor R. Sep 10 '13 at 11:31
  • @IgorR. That comment is reason for my question. I want to know how to cancel the read. – Ne0 Sep 10 '13 at 11:32

2 Answers2

2

First, it should cause the handle to be called when the next run,run_one,poll, or poll_one is called, assuming the io_service is still in a valid state. Canceling/closing does not suddenly change the behavior of how handlers are invoked in ASIO.

Because of this, if you're triggering this on deletion of your Session, you will have UB, since the handler will be invoked on the destroyed object, since you passed it the raw pointer, which has subsequently been deleted.

My usual technique for this is to use shared_ptr combined with a variant of the pimpl pattern. I usually have the implementation in a shared pointer, and pass that to the various ASIO calls. When the exterior interface is destroyed, I call a shutdown method on the pimpl which causes ASIO to cancel its operations, and then the interface resets its copy of the shared pointer.

Once ASIO calls of the stored handlers, it will no longer have a copy of the shared pointer, and the destruction will complete.

Dave S
  • 20,507
  • 3
  • 48
  • 68
  • This may well have contributed to my issue, so will give @DaveS credit. While editing my code to eliminate the risk of deleting classes before closing sockets, I noticed I was calling `stop` on my `io_service` before closing the sockets. doh! – Ne0 Sep 10 '13 at 13:30
  • @Ne0: Another option, is to stop the io_service, and destroy all of the objects. This will work as long as you don't require your handlers to execute during shutdown. This is more of an abrupt shutdown vs. a graceful shutdown. – Dave S Sep 10 '13 at 16:30
0

In my case, calling acceptor->close() only sends operation_aborted error to queued accept_async and send_async tasks. My read_some_async does not receive error. I found that calling socket->shutdown(socket_base::shutdown_type::shutdown_both); and socket->close(); will trigger the error completion callback.

Yuanyi Wu
  • 199
  • 2
  • 9