Reputation: 4327
This program reads from stdin (via iostream) and writes to stdout (via boost/asio):
#include <boost/asio.hpp>
#include <cassert>
#include <iostream>
#include <string>
boost::asio::io_service io_service;
boost::asio::posix::stream_descriptor out(io_service, ::dup(STDOUT_FILENO));
std::string line = "";
static void handler(
boost::system::error_code const &,
std::size_t
) {
assert(std::getline(std::cin, line));
line += "\n";
async_write(out, boost::asio::buffer(line), handler);
}
int main()
{
async_write(out, boost::asio::buffer(line), handler);
io_service.run();
}
build: g++ -std=gnu++1y -O0 -g3 -o out in.cxx -lboost_system -lboost_thread
run: cat | ./out
output:
foo [TERMINAL INPUT]
foo
bar [TERMINAL INPUT]
cat: -: Resource temporarily unavailable
bar
out: in.cxx:14: void handler(const boost::system::error_code&, std::size_t): Assertion `std::getline(std::cin, line)' failed.
Aborted (core dumped)
cat
gets an EAGAIN
from a write()
to its standard output, treats it as an error, and closes the pipe. In turn getline()
fails and the program aborts.
This looks like asio is setting the program's standard input (which is the standard output of cat
, see Strange exception throw - assign: Operation not permitted) to non-blocking. It has no obvious reason to do this, because it is operating on standard output only, though.
If I get this right, is this a bug in asio? Is there a workaround?
libboost1.58-dev/xenial-updates,now 1.58.0+dfsg-5ubuntu3.1 amd64 [installed]
g++/xenial,now 4:5.3.1-1ubuntu1 amd64 [installed]
Upvotes: 2
Views: 1304
Reputation: 393174
You invoke Undefined Behaviour here:
async_write(out, boost::asio::buffer(string + "\n"), handler);
Because the buffer is a local variable it will be destroyed when handler
exits, before the async write operation may get a chance to run.
EDIT Actually, even if the variable wasn't local, the + "\n"
makes it a temporary!
Here's a suggested fix for this simple program:
#include <boost/asio.hpp>
#include <cassert>
#include <iostream>
#include <string>
boost::asio::io_service io_service;
boost::asio::posix::stream_descriptor out(io_service, ::dup(STDOUT_FILENO));
std::string input_buffer;
static void handler(boost::system::error_code /*error*/, std::size_t /*bytes_transferred*/) {
if (std::getline(std::cin, input_buffer)) {
input_buffer += "\n";
async_write(out, boost::asio::buffer(input_buffer), handler);
}
}
int main() {
async_write(out, boost::asio::buffer(""), handler);
io_service.run();
}
I'm not sure this solves all your problems, but at the very least you need to fix it in order to be able to reason about your program at all
Upvotes: 2