Here is my sample code that opens a pipe in read mode. It uses boost::asio
to read from the pipe. When data (let's say X bytes) is written to the pipe, it calls on_read
with ec=EOF
, bytes=X
and X bytes of data in the buffer.
EOF is sent because the writer, after finishing writing to the pipe, closes it. I want to keep reading. That is why I call pipe.async_wait()
in on_read
. However, even if nothing is ready to be read from the pipe, on_read
calls my_pipe::async_read()
which again calls on_read()
with bytes = 0
and ec=EOF
. This goes on in an infinite loop.
Why does it keeping reading EOF
repeatedly?
#include <boost/asio/io_service.hpp>
#include <boost/asio/placeholders.hpp>
#include <boost/asio/posix/stream_descriptor.hpp>
#include <unistd.h>
#include <chrono>
#include <cstdint>
#include <fstream>
#include <iostream>
#include <string>
#include <boost/asio/read.hpp>
#include <boost/asio/write.hpp>
#include <boost/bind.hpp>
class my_pipe final
{
public:
explicit my_pipe(boost::asio::io_service& io_service);
~my_pipe();
private:
boost::asio::posix::stream_descriptor pipe;
std::vector<char> _buf{};
void async_read(const boost::system::error_code& ec);
void on_read(const boost::system::error_code& ec, std::size_t bytes_transferred);
};
my_pipe::my_pipe(boost::asio::io_service& io_service) : pipe(io_service)
{
int fd = open("/tmp/pipe1", O_RDONLY | O_NONBLOCK);
if (fd == -1)
return;
_buf.resize(8192);
pipe.assign(fd);
pipe.async_wait(boost::asio::posix::stream_descriptor::wait_read,
boost::bind(&my_pipe::async_read, this, boost::asio::placeholders::error));
}
my_pipe::~my_pipe()
{
pipe.close();
}
void my_pipe::async_read(const boost::system::error_code& ec)
{
std::cout << "async_read\n";
if (ec)
return;
boost::asio::async_read(pipe, boost::asio::buffer(_buf),
boost::bind(&my_pipe::on_read, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
}
void my_pipe::on_read(const boost::system::error_code& ec, std::size_t bytes)
{
std::cout << "on_read. bytes=" << bytes << "\n";
if (!ec || ec == boost::asio::error::eof) {
if (ec == boost::asio::error::eof)
std::cout << "eof\n";
std::cout << "call async_read\n";
pipe.async_wait(boost::asio::posix::stream_descriptor::wait_read,
boost::bind(&my_pipe::async_read, this, boost::asio::placeholders::error));
} else {
std::cout << "on_read error: " << ec.message() << "\n";
}
}
int main()
{
boost::asio::io_service ios;
my_pipe obj(ios);
ios.run();
}
Thanks for all your help.
A pipe is "widowed" when all handles on one end are closed.
In this case, after you get an EOF, you should close the pipe handle and then reopen the pipe. You can then issue an async_read()
on the new descriptor to wait for more data.
If you have multiple writers, also consider that writes are only guaranteed to be atomic up to PIPE_BUF
bytes.