0

This is related to my question here. I was looking for a way to add a timeout on boost::asio::read. Thanks for the answer. I learnt how to emulate a blocking read with a timeout using boost::asio::async_read. That worked great.

I am trying to extend the same for blocking wite with a timeout. Basically trying to do a boost::asio::write with a timeout. The same logic which worked here on read does not seem to work for write if I just replace the boost::asio::async_read with boost::asio::async_write. In fact doing that makes every write take some 2-3 seconds time which is very long wrt the data I am trying to transfer.

Does write have a different mechanism when it comes to putting a timeout? Is there an example of doing a boost::asio::write with a timeout ?

So, basically I need know how to do a timeout based boost::asio::write ?

Posting the following code that I am trying out based on example here:

#include <type_traits>

template<class Stream, class ConstBufferSequence, class Handler>
auto async_write_with_timeout(Stream& stream, ConstBufferSequence&& sequence, std::size_t millis, Handler&& handler)
{
  using handler_type = std::decay_t<Handler>;
  using buffer_sequence_type = std::decay_t<ConstBufferSequence>;
  using stream_type = Stream;

  struct state_machine : std::enable_shared_from_this<state_machine>
  {
    state_machine(stream_type& stream, buffer_sequence_type sequence, handler_type handler)
            : stream_(stream)
            , sequence_(std::move(sequence))
            , handler_(std::move(handler))
    {}
    void start(std::size_t millis)
    {
        timer_.expires_from_now(boost::posix_time::milliseconds(millis));
        timer_.async_wait(strand_.wrap([self = this->shared_from_this()](auto&& ec) {
            self->handle_timeout(ec);
        }));
        boost::asio::async_write(stream_, sequence_,
                                strand_.wrap([self = this->shared_from_this()]  (auto&& ec, auto size){
            self->handle_write(ec, size);
        }));
    }

    void handle_timeout(boost::system::error_code const& ec)
    {
        if (not ec and not completed_)
        {
            boost::system::error_code sink;
            stream_.cancel(sink);
        }
    }

    void handle_write(boost::system::error_code const& ec, std::size_t size)
    {
        assert(not completed_);
        boost::system::error_code sink;
        timer_.cancel(sink);
        completed_ = true;
        handler_(ec, size);
    }

    stream_type& stream_;
    buffer_sequence_type sequence_;
    handler_type handler_;
    boost::asio::io_service::strand strand_ { stream_.get_io_service() };
    boost::asio::deadline_timer timer_ { stream_.get_io_service() };
    bool completed_ = false;
  };

  auto psm = std::make_shared<state_machine>(stream,
                                           std::forward<ConstBufferSequence>(sequence),
                                           std::forward<Handler>(handler));
  psm->start(millis);
}

std::size_t WriteData(boost::asio::ip::tcp::socket& socket,
                 std::vector<unsigned char> & buffer,
                 unsigned int size_to_write,
                 boost::system::error_code& ec) {

  buffer.resize(size_to_write);

  ec.clear();
  std::size_t bytes_written = 0;
  auto& executor = socket.get_io_service();
  async_write_with_timeout(socket, boost::asio::buffer(buffer),
                        2000, // 2 seconds for example
                        [&](auto&& err, auto size){
    ec = err;
    bytes_written = size;
  });

  // todo: use a more scalable executor than spawning threads
  auto future = std::async(std::launch::async, [&] {
    if (executor.stopped()) {
        executor.reset();
    }
    executor.run();
  });
  future.wait();

  return bytes_written;
}
Community
  • 1
  • 1
TheWaterProgrammer
  • 7,055
  • 12
  • 70
  • 159
  • can you show the code you have tried so far? I can't imagine why it won't work on a write. Except that I would expect most writes to complete almost immediately - unless you fill the write buffer and the network is slow. – Richard Hodges Apr 07 '17 at 21:15
  • @RichardHodges ok. then it should be a mistake I am doing with my buffers may be. I wanted to confirm that the same `timeout` technique as in `read`, can be used with `async_write` – TheWaterProgrammer Apr 08 '17 at 13:52
  • 1
    It should work. It might be worth adding some debug code to check that the time is being cancelled properly. An outstanding async wait on a timer will block io_service::run – Richard Hodges Apr 08 '17 at 17:07
  • I am trying to use the same template method with a switch to read & write both operations. In this case `io_service::run` is very strangely taking long time to return. almost a 2 seconds each time. does not happen if I use the templatised method only for read or write. any one of the 2. time is being cancelled properly. there is no error code returned in `read_deadline_timer.cancel(sink)` called in `handle_read`. trying to add a destructor to the struct call some cancel or what ever possible – TheWaterProgrammer Apr 09 '17 at 16:23

0 Answers0