[Boost-bugs] [Boost C++ Libraries] #13515: async_pipe::async_read_some returns zero read size if -O<something> is used

Subject: [Boost-bugs] [Boost C++ Libraries] #13515: async_pipe::async_read_some returns zero read size if -O<something> is used
From: Boost C++ Libraries (noreply_at_[hidden])
Date: 2018-04-06 14:20:16


#13515: async_pipe::async_read_some returns zero read size if -O<something> is used
-------------------------------------+----------------------------
 Reporter: Tomasz Jonak <tjonak@…> | Owner: chris_kohlhoff
     Type: Bugs | Status: new
Milestone: To Be Determined | Component: asio
  Version: Boost 1.66.0 | Severity: Problem
 Keywords: |
-------------------------------------+----------------------------
 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.

 {{{
 #!div style="font-size: 80%"
   {{{#!C++
   #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.

 {{{
 #!div style="font-size: 80%"
   {{{#!C++
   pipe.async_read_some(asio::buffer(buffer), yield[read_ec]);
   }}}
 }}}
 starts returning 0. Buffer contents are modified though. Callback based
 approach yields same result.

 {{{
 #!div style="font-size: 80%"
 {{{
 ...
 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

-- 
Ticket URL: <https://svn.boost.org/trac10/ticket/13515>
Boost C++ Libraries <http://www.boost.org/>
Boost provides free peer-reviewed portable C++ source libraries.

This archive was generated by hypermail 2.1.7 : 2018-04-06 14:27:52 UTC