2

I am receiving this error message

"The I/O operation has been aborted because of either a thread exit or an application request"

when using boost::asio::socket::async_read_some()

What does the error mean? What should I be looking for?

Here is the relevant code:

void tcp_connection::start()
{
  printf("Connected to simulator\n");

  socket_.async_read_some(boost::asio::buffer(myBuffer,256),
      boost::bind(&tcp_connection::read_sim_handler,this,
      boost::asio::placeholders::error,
      boost::asio::placeholders::bytes_transferred));
}

void tcp_connection::read_sim_handler(
                                  const boost::system::error_code& error, // Result of operation.
                                  std::size_t len )         // Number of bytes read.
{
try {
if (error == boost::asio::error::eof) {
    // Connection closed cleanly by peer.
    printf("Sim connection closed\n");
    return;
} else if (error) {
    throw boost::system::system_error(error); // Some other error.  if( ! error ) 
}

socket_.async_read_some(boost::asio::buffer(myBuffer,256),
    boost::bind(&tcp_connection::read_sim_handler,this,
    boost::asio::placeholders::error,
    boost::asio::placeholders::bytes_transferred));
}
 catch (std::exception& e)
{
std::cerr << e.what() << std::endl;
}
}

When I replace the call to async_read_some() with read_some() in the start() method, everything works fine ( except the server blocks waiting for a message! )

Following a comment i see that tcp_connection is going out of scope. I copied the code from http://www.boost.org/doc/libs/1_45_0/doc/html/boost_asio/tutorial/tutdaytime3.html which says this: "We will use shared_ptr and enable_shared_from_this because we want to keep the tcp_connection object alive as long as there is an operation that refers to it." I confess that I do not know what all that means. So I have broken it somehow?

Following further comments, the answer is

void tcp_connection::start()
{
  printf("Connected to simulator\n");

  socket_.async_read_some(boost::asio::buffer(myBuffer,256),
      boost::bind(&tcp_connection::read_sim_handler,
              shared_from_this(),
      boost::asio::placeholders::error,
      boost::asio::placeholders::bytes_transferred));
}

Passing shared_from_this() rather than this employs the clever ( too clever? ) keep alive infrastructure established by the server code, even though the connection manager is not in scope, by normal means. For technical details, see comments under accepted answer.

Blacktempel
  • 3,935
  • 3
  • 29
  • 53
ravenspoint
  • 19,093
  • 6
  • 57
  • 103
  • myBuffer is a member of tcp_connection. The tcp_connection class is created by tcp_server code, copied directly from http://www.boost.org/doc/libs/1_45_0/doc/html/boost_asio/tutorial/tutdaytime3/src.html – ravenspoint Jan 06 '11 at 20:46
  • @villintehaspam However, I just added an instrumented destructor to tcp_connection. It is being called! I think you have put me on the right track. – ravenspoint Jan 06 '11 at 20:50
  • 1
    I updated my answer now that I see your additional text on shared_ptr and enable_shared_from_this. These parts are essential in keeping your object alive in the example, see my answer. Write another comment on my answer if you need more clarification. – villintehaspam Jan 06 '11 at 21:28

1 Answers1

4

Your tcp_connection object or your buffer object is likely going out of scope prior to the async operation completing.

Since your program is based on one of the tutorial examples, why don't you check out another of the examples that reads some data as well: http://www.boost.org/doc/libs/1_45_0/doc/html/boost_asio/example/echo/async_tcp_echo_server.cpp

The reason your class goes out of scope is that you are no longer using shared_from_this(). What this does is create a shared_ptr to your class that is stored by the bind handler. This means that the shared_ptr will keep your class alive until your handler is called.

This is also why you need to inherit from enable_shared_from_this.

The last shared_ptr that goes out of scope will delete your class instance.

villintehaspam
  • 8,540
  • 6
  • 45
  • 76
  • I will accept this answer. When I moved the connection out of the server and into the global namespace, everything began to work. Ugly, but serviceable. – ravenspoint Jan 06 '11 at 21:16
  • If you look closely, you will see that my code is a combination of tutdaytime3 and async_tcp_echo_server – ravenspoint Jan 06 '11 at 21:17
  • You may also notice that the exception is caught immediately - it does not travel anywhere through the callstack. – ravenspoint Jan 06 '11 at 21:18
  • Ok, then the link wasn't really helpful... Sorry about that :) . I'm not sure what you mean by moving the connection out of the server and into the global namespace? Changing from an internal class to a separate one should not affect its lifetime - do you mean that you are _creating_ classes outside of the server object now? – villintehaspam Jan 06 '11 at 21:20
  • Ahh, yes, I see that now. The formatting is a bit weird so I missed that. I will remove that from the answer. – villintehaspam Jan 06 '11 at 21:22
  • The tcp_connection instance created by the server when a connection is made is, in the tutorial code, an automatic in the start_accept() method. So it goes out of scope, of course. I have moved it into the global namespace, so it will not go out of scope. – ravenspoint Jan 06 '11 at 21:28
  • 1
    @ravenspoint, if you take another look at the example you can see that the automatic variable is in reality not the connection object but a shared_ptr to a connection object. This is a big difference - it is only the shared_ptr that goes out of scope but since your connection class is reference counted, your connection object stays alive. This is because another shared_ptr is created by shared_from_this(). – villintehaspam Jan 06 '11 at 21:33
  • For as long as at least one shared_ptr pointing to your object exists, your object stays alive. This is a very powerful method of making sure that an object isn't deleted while there is still someone using it. – villintehaspam Jan 06 '11 at 21:34
  • @ravenspoint, ... so I think you should still have the server class create the connections like in the example. Otherwise you most likely won't be able to accept more than one connection if you've just moved the object out of the server class. The shared_ptr class + enable_shared_from_this is your friend! – villintehaspam Jan 06 '11 at 21:37
  • @villintehaspam Why are you assuming that I am not using shared_ptr class + enable_shared_from_this? I have copied the code directly from the tutorial. I mentioned that I was using them in my question ( though I did not include them in the code fragment posted since the server code is unchanged from the tutorial. – ravenspoint Jan 06 '11 at 21:50
  • 1
    @ravenspoint, because it says so in the code that you've posted? You will need to use shared_from_this() instead of this in all calls to _socket.async_read_some(...), otherwise your object goes out of scope. The lifetime is as follows in the example: 1. Created and stored in a shared_ptr in the server::start_accept method. 2. Bound using a shared_ptr to the handle_accept method. 3. The start_accept method ends and the first shared_ptr goes out of scope. 4. handle_accept gets called, this calls start on the connection that it gets from the shared_ptr ... continued. – villintehaspam Jan 06 '11 at 22:04
  • 5. start calls async_read_some, then ends. 6. The handle_accept call ends (ignoring that it starts another accept) and the shared_ptr goes out of scope. Now if you haven't created a new shared_ptr in step 5, this means that your connection object dies at this moment. – villintehaspam Jan 06 '11 at 22:05
  • " You will need to use shared_from_this() instead of this in all calls to _socket.async_read_some(...), otherwise your object goes out of scope." This looks like it might be the vital point! – ravenspoint Jan 06 '11 at 22:18
  • That did it! Simpy replace "this" with "shared_from_this()" and everything starts working without any other changes. – ravenspoint Jan 06 '11 at 22:27
  • +1 understanding object lifetime when using asynchronous handlers is a very important concept. Using shared_ptr simplifies this greatly. – Sam Miller Jan 08 '11 at 00:32