Daniel
Daniel

Reputation: 53

io_service object working mechanism

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

Upvotes: 1

Views: 234

Answers (1)

sehe
sehe

Reputation: 393999

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.

Upvotes: 1

Related Questions