1

I have a function called read_packet. This function remains blocked while there is no connection request or the timer is signaled.

The code is the following:

    std::size_t read_packet(const std::chrono::milliseconds& timeout,
                            boost::system::error_code& error)
    {
      // m_timer_ --> boost::asio::high_resolution_timer

      if(!m_is_first_time_) {
        m_is_first_time = true;

        // Set an expiry time relative to now.
        m_timer_.expires_from_now( timeout );
      } else {
        m_timer_.expires_at( m_timer_.expires_at() + timeout );
      }

      // Start an asynchronous wait.
      m_timer_.async_wait(
          [ this ](const boost::system::error_code& error){
            if(!error) m_is_timeout_signaled_ = true;
          }
      );

      auto result = m_io_service_.run_one();

      if( !m_is_timeout_signaled_ ) {
        m_timer_.cancel();
      }

      m_io_service_.reset();

      return result;
    }

The function works correctly while not receiving a connection request. All acceptances of requests are asynchronous.

After accepting a connection, the run_one() function does not remains blocked the time set by the timer. The function always returns 1 (one handle has been processed). This handle corresponds to the timer.

I do not understand why this situation occurs.

Why the function is not blocked the time required for the timer?

Cheers.

NOTE: This function is used in a loop.

UPDATE:

I have my own io_service::run() function. This function performs other actions and tasks. I want to listen and process the network level for a period of time:

  • If something comes on the network level, io_service::run_one() returns and read_packet() returns the control to my run() function.

  • Otherwise, the timer is fired and read_packet() returns the control to my run() function.

Everything that comes from the network level is stored in a data structure. Then my run() function operates on that data structure. It also runs other options.

    void run(duration timeout, boost::system::error_code& error)
    {
      time_point start = clock_type::now();
      time_point deadline = start + timeout;

        while( !stop() ) {
          read_packet(timeout, error);
          if(error) return;

          if(is_timeout_expired( start, deadline, timeout )) return;

          // processing network level

          // other actions
        }
    }

In my case, the sockets are always active until a client requests the closing of the connection.

During a time slot, you manage the network level and for another slot you do other things.

Juan Solo
  • 359
  • 1
  • 5
  • 17
  • Why are you running `run_one`, not `run`? It does look like you're only waiting once, why do yo expect the timer to run more than once? – sehe Dec 12 '15 at 19:40
  • @sehe : I have an asynchronous acceptor. For this reason, the `io_service::run()` function is always blocked. While I wait for a connection, I want to do other tasks. The timer allows me to do this. – Juan Solo Dec 12 '15 at 21:17
  • Please look at some of the samples. It looks like you're rowing against the stream. Can you use a thread for the IO or post tasks for the responsive tasks in te meantime? Also, it sounds like complete contradicting. If you use the timers to callback your wiel to do in the meantime, what is the problem with run not returning? The timer will still fire – sehe Dec 12 '15 at 21:20
  • In my code, I can not use threads. The problem is that the timer doesn't fire. – Juan Solo Dec 12 '15 at 21:40
  • I think you're overcomplicating. It doesn't make sense. Let me show you: https://www.livecoding.tv/sehe/ – sehe Dec 12 '15 at 22:37
  • Huh. Where's the readining happening :) I'll make stuff up, but the sample should really be SSCCE – sehe Dec 12 '15 at 22:41

2 Answers2

0

After reading the question more closely I got the idea that you are actually trying to use Asio to get synchronous IO, but with a timeout on each read operation.

That's not what Asio was intended for (hence, the name "Asynchronous IO Library").

But sure, you can do it if you insist. Like I said, I feel you're overcomplicating things.

In the completion handler of your timer, just cancel the socket operation if the timer had expired. (Note that if it didn't, you'll get operation_aborted, so check the error code).

Small selfcontained example (which is what you should always do when trying to get help, by the way):

Live On Coliru

#include <boost/asio.hpp>
#include <boost/asio/high_resolution_timer.hpp>
#include <iostream>

struct Program {

    Program() { sock_.connect({ boost::asio::ip::address_v4{}, 6771 }); }

    std::size_t read_packet(const std::chrono::milliseconds &timeout, boost::system::error_code &error) {
        m_io_service_.reset();

        boost::asio::high_resolution_timer timer { m_io_service_, timeout };

        timer.async_wait([&](boost::system::error_code) { 
                sock_.cancel();
            });

        size_t transferred = 0;
        boost::asio::async_read(sock_, boost::asio::buffer(buffer_), [&](boost::system::error_code ec, size_t tx) {
                    error       = ec;
                    transferred = tx;
                });

        m_io_service_.run();

        return transferred;
    }

  private:
    boost::asio::io_service m_io_service_;
    using tcp = boost::asio::ip::tcp;

    tcp::socket sock_{ m_io_service_ };
    std::array<char, 512> buffer_;
};

int main() {
    Program client;
    boost::system::error_code ec;

    while (!ec) {
        client.read_packet(std::chrono::milliseconds(100), ec);
    }

    std::cout << "Exited with '" << ec.message() << "'\n"; // operation canceled in case of timeout
}

If the socket operation succeeds you can see e.g.:

Exited with 'End of file'

Otherwise, if the operation didn't complete within 100 milliseconds, it will print:

Exited with 'Operation canceled'

See also await_operation in this previous answer, which generalizes this pattern a bit more:

Community
  • 1
  • 1
sehe
  • 374,641
  • 47
  • 450
  • 633
  • See the recorded live stream in case you are curious how I go about the reading/answering process: https://www.livecoding.tv/video/repeatable-receive-operation-under-a-timeout/ – sehe Dec 12 '15 at 23:14
  • I'm sorry but my English is very bad and I can not explain well. It is not exactly what I want. I edited the thread adding more information. – Juan Solo Dec 13 '15 at 11:45
0

Ok, The code is incorrect. When the timer is canceled, the timer handler is always executed. For this reason io_service::run_one() function is never blocked.

More information: basic_waitable_timer::cancel

Thanks for the help.

Juan Solo
  • 359
  • 1
  • 5
  • 17