Opened 5 years ago

Last modified 4 years ago

#13515 new Bugs

async_pipe::async_read_some returns zero read size if -O<something> is used

Reported by: Tomasz Jonak <tjonak@…> Owned by: chris_kohlhoff
Milestone: To Be Determined Component: asio
Version: Boost 1.66.0 Severity: Problem
Keywords: Cc:

Description

Hi there,

We are developing coroutine based application which sits mostly on some sort of I/O. Decided to go with coroutine based approach with boost 1.66 and asio/beast/process libs. While doing some internal POC we ran into issue with retrieving size of data obtained through pipe.

#include <boost/asio.hpp>
#include <boost/asio/spawn.hpp>
#include <boost/process.hpp>
#include <chrono>
#include <iostream>

void test_process()
{
  static const char* path = "./dataspit.py";
  static const char* interpreter = "python3";

  std::cout << "Hello from streamer2\n";

  namespace asio = boost::asio;
  namespace process = boost::process;
  namespace chrono = std::chrono;

  asio::io_context ioc;

  asio::spawn(ioc,
    [&ioc](asio::yield_context yield)
    {
      process::async_pipe pipe{ioc};
      auto child = process::child{process::search_path(interpreter), path,
                                  process::std_out > pipe};

      std::array<char, 4096> buffer{0};

      std::cout <<  "Buffer state: " << (int)buffer[0] << (int)buffer[1] << std::endl;
      boost::system::error_code read_ec;
      do
      {
        auto timePoint = chrono::system_clock::now();
        std::cout << timePoint.time_since_epoch().count() << ": Sleeping\n";

        std::size_t size = pipe.async_read_some(asio::buffer(buffer), yield[read_ec]);

        if (read_ec) {
            std::cerr << read_ec.message() << std::endl;
            break;
        }

        timePoint = chrono::system_clock::now();
        std::cout << timePoint.time_since_epoch().count() << ": Read " << size << " bytes\n";
        std::cout <<  "Buffer state: " << (int)buffer[0] << (int)buffer[1] << std::endl;
      }
      while (read_ec != boost::asio::error::eof);

      child.wait();
    }
  );

  ioc.run();

  std::cout << "Farewell from streamer2\n";
}

int main(int argc, char** argv)
{
  test_process();
  return 0;
}

This code works fine when no compiler optimizations are used. As soon as its compiled with -Os or any numbered -O type.

pipe.async_read_some(asio::buffer(buffer), yield[read_ec]);

starts returning 0. Buffer contents are modified though. Callback based approach yields same result.

...
readv(6, [{"57757488835088650590974423544437"..., 4096}], 1) = 1030
write(1, "1523018074402113597: Read 0 byte"..., 341523018074402113597: Read 0 bytes) = 34
write(1, "Buffer state: 5355\n", 19Buffer state: 5355)    = 19
write(1, "1523018074402457408: Sleeping\n", 301523018074402457408: Sleeping) = 30
readv(6, 0x83c2168, 1)                  = -1 EAGAIN (Resource temporarily unavailable)
epoll_wait(4, [{EPOLLIN, {u32=138163408, u64=138163408}}], 128, -1) = 1
readv(6, [{"15827357453760420053575823774839"..., 4096}], 1) = 546
write(1, "1523018076807802498: Read 0 byte"..., 341523018076807802498: Read 0 bytes) = 34
write(1, "Buffer state: 4953\n", 19Buffer state: 4953)    = 19
write(1, "1523018076808233410: Sleeping\n", 301523018076808233410: Sleeping) = 30
readv(6, 0x83c2168, 1)                  = -1 EAGAIN (Resource temporarily unavailable)
epoll_wait(4, [{EPOLLIN, {u32=138163408, u64=138163408}}], 128, -1) = 1
readv(6, [{"66365484273136085944223051047033"..., 4096}], 1) = 814
write(1, "1523018079517434883: Read 0 byte"..., 341523018079517434883: Read 0 bytes) = 34
write(1, "Buffer state: 5454\n", 19Buffer state: 5454)    = 19
write(1, "1523018079517782099: Sleeping\n", 301523018079517782099: Sleeping) = 30
readv(6, 0x83c2168, 1)                  = -1 EAGAIN (Resource temporarily unavailable)
...

Epoll waits as expected, readv seems to return length as well. Looks like some kind of reordering issue.

Tested this behavior on ubuntu 14.04/16.04 in 32/64bit modes on using stock gcc, all ran in docker containers within ubuntu 16.04 host.

Issue applies to boost 1.66 for sure, tested also 1.65 and 1.67 beta on ubuntu 14.04 32 bit. Issue is there as well.

Complete sample code in attachments.

I'd be grateful for some notification if you find root cause and possible patch. I'd like to apply such to our boost sources.

Best Regards, Tomasz Jonak

Attachments (1)

asio_bug_sample.tar.bz2 (1.7 KB ) - added by Tomasz Jonak <tjonak@…> 5 years ago.

Download all attachments as: .zip

Change History (3)

by Tomasz Jonak <tjonak@…>, 5 years ago

Attachment: asio_bug_sample.tar.bz2 added

comment:1 by anonymous, 4 years ago

Hello,

while (read_ec != boost::asio::error::eof);

error::eof is an enum value of type boost::asio::error::errc; in this context read_ec will be converted to bool and compared against the integer value of error::eof.

Try to compare against read_ec.value() instead.

comment:2 by bronf, 4 years ago

error::eof is an enum value of type boost::asio::error::errc; in this context read_ec will be converted to bool and compared against the integer value of error::eof.

Apparently this not the case. I tested in gdb and boost::asio::error::eof is first converted to an error_code which is then compared to read_ec.

Note: See TracTickets for help on using tickets.