1

I am confused about io_services object working mechanism. My understanding is that there is a queue associates with io_service object, if any async invocation will add one item in the queue, when io_service.run_once is called ,one async invocation will run and dequeue from the queue. if the queue is empty the io_service.run_one will do nothing until new invocation is added. I organized some code form the boost example but It seems that my understanding is wrong.

#include <boost/asio/connect.hpp>
#include <boost/asio/io_service.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/system/system_error.hpp>
#include <boost/asio/write.hpp>
#include <cstdlib>
#include <iostream>
#include <string>
#include <boost/bind.hpp>
using boost::asio::deadline_timer;
using boost::asio::ip::tcp;

class client
{
public:
  client()
    : socket_(io_service_)
  {
  }

  void connect_handler(const boost::system::error_code& error,boost::system::error_code *er)
  {
      std::cerr<<"connect handler"<<std::endl;
      *er = error;
      std::cerr<<error<<std::endl;
  }

  void connect(const std::string& host, const std::string& service)
  {
    tcp::resolver::query query(host, service);
    tcp::resolver::iterator iter = tcp::resolver(io_service_).resolve(query);
    std::cerr<<"connect start"<<std::endl;

    boost::system::error_code ec = boost::asio::error::would_block;
    boost::asio::async_connect(socket_, iter, bind(&client::connect_handler,this,_1,&ec));

    do
    {io_service_.run_one();
    }while (ec == boost::asio::error::would_block);
    //io_service_.reset();  // The write async will be stuck without this reset call.
    std::cerr<<"connect done"<<std::endl;
    if (ec || !socket_.is_open())
      throw boost::system::system_error(
          ec ? ec : boost::asio::error::operation_aborted);
  }

  void write_handler(const boost::system::error_code& error, std::size_t size,boost::system::error_code* er )
  {
      std::cerr<<"write handler "<<std::endl;
      *er=error;
      std::cerr<<error<<std::endl;
  }

  void write_line(const std::string& line)
  {
    std::cerr<<"write start"<<std::endl;
    std::string data = line + "\n";
    boost::system::error_code ec = boost::asio::error::would_block;    
    boost::asio::async_write(socket_, boost::asio::buffer(data), bind(&client::write_handler,this,_1,_2,&ec));
    do
    {
        io_service_.run_one();     
    }while (ec == boost::asio::error::would_block);
    std::cerr<<"write done";
    if (ec)
      throw boost::system::system_error(ec);
  }

private:
  boost::asio::io_service io_service_;
  tcp::socket socket_;
};

int main()
{
  try
  {
    client c,d;
    c.connect("172.217.6.36", "80");// google IP.
    c.write_line("example");
  }
  catch (std::exception& e)
  {
    std::cerr << "Exception: " << e.what() << "\n";
  }

  return 0;
}

My understand is that:

   Start
     | 
async_connect ----> add one item in io_service queue 
     |
     |
io_serivce.run_one() ------> dequeue the async_connect call back from io_serivce queue 
     |
     |
connect_handler --------> connect_handler called change the ec value
     |
     |
async_write ----------> add one item in io_service queue.
     |
     |
io_service.run_one()------------>dequeue the async_write call back from io_serivce queue  
     |
     |
write_handler()----------------->write handler called and change the ec value
     |
   Done

but the reality is

Start
     | 
async_connect ----> add one item in io_service queue 
     |
     |
io_serivce.run_one() ------> dequeue the async_connect call back from io_serivce queue 
     |
     |
connect_handler --------> connect_handler called change the ec value
     |
     |
async_write ----------> add one item in io_service queue.
     |
     |
io_service.run_one()------------>stuck here in the while loop forever, the async_write handler is never be called the ec is never be changed. 

Sehe told me that the io_service.reset needed to be called in another post, what I don't understand why io_service.reset needs to be called? the original example doesn't use this call and it works fine. With the reset call works:

   Start
     | 
async_connect ----> add one item in io_service queue 
     |
     |
io_serivce.run_one() ------> dequeue the async_connect call back from io_serivce queue 
     |
     |
connect_handler --------> connect_handler called change the ec value
     |
     |
io_service.reset() --------> reset the io service.
     |
     |
async_write ----------> add one item in io_service queue.
     |
     |
io_service.run_one()------------>dequeue the async_write call back from io_serivce queue  
     |
     |
write_handler()----------------->write handler called and change the ec value
     |
   Done
Daniel
  • 53
  • 5

1 Answers1

1

The original sample uses the deadline-timer which is in a continuous chain of async_waits. This means that the io_service will never run out of work.

That's it. The whole difference. If you let the service run out of work, run_* will return and you will need to call reset() before you can use the io_service again.

See also Why must io_service::reset() be called?


For context the earlier answer boost socket example stuck in while loop where I give several better approaches to making this work either using synchronous calls or using asynchronous calls.

sehe
  • 374,641
  • 47
  • 450
  • 633
  • Hi Sehe, Thanks for your answer, you help me a lot :), I made this post want to get a better understanding of the io_services, your answers in my previous post showed me the correct way to do that. There is one thing I still don't understand why the io_serviece will out of work? my understandinng is that async_connect and async_write both of them can make io_services queue not empty, but why the async_connect works in this case but async_write doesn't and make io_service out of work? – Daniel Feb 07 '18 at 16:18
  • That's just how it's designed, IOW it's by definition. [The run() function blocks until all work has finished and there are no more handlers to be dispatched, or until the io_service has been stopped.](http://www.boost.org/doc/libs/1_65_0/doc/html/boost_asio/reference/io_service/run/overload1.html) – sehe Feb 07 '18 at 16:26
  • Async_connect also made the service run out of work (obviously). Which is why you find out next operation. – sehe Feb 07 '18 at 16:27
  • Sehe, Thanks for your reply, but I am still confused, let me simplify my question. why async_connect call back can be called , but the async_write call back is never called without reset()? from the code point of view, they used exact same routine: set ec ---> call async function----> while loop to check ec whether the call back is called. – Daniel Feb 08 '18 at 17:23
  • Because the work never gets scheduled without the reset. Another time: it's **not** `async_write` causing the service to stop. It's `async_connect` causing it to stop (even though you can't tell except if you check the return value of `run()` or `run_one()`) – sehe Feb 08 '18 at 17:25
  • Hi Sehe, Thank you so much, I appreciate your help. now it's crystal clear !:) – Daniel Feb 12 '18 at 19:49