read from keyboard using boost async_read and posix::stream_descriptor - c++

I am trying to capture single keyboard inputs in a non blocking way inside a while loop using boost asio async_read. The handler is expected to display the read characters.
My code:
#include <boost/asio/io_service.hpp>
#include <boost/asio/posix/stream_descriptor.hpp>
#include <boost/asio/read.hpp>
#include <boost/system/error_code.hpp>
#include <iostream>
#include <unistd.h>
#include <termios.h>
using namespace boost::asio;
void read_handler(const boost::system::error_code&, std::size_t)
{
char c;
std::cin>>c;
std::cout << "keyinput=" << c << std::endl;
}
int main()
{
io_service ioservice;
posix::stream_descriptor stream(ioservice, STDIN_FILENO);
char buf[1];
while(1)
{
async_read(stream, buffer(buf,sizeof(buf)), read_handler);
ioservice.run();
}
return 0;
}
My output is not as expected(keyinput=char format):
a
key input
b
c
d
e
Where am I going wrong?
Also the program is very cpu intensive. How to rectify it?

There's an important restriction on async IO with stdin: Strange exception throw - assign: Operation not permitted
Secondly, if you use async_read do not use std::cin at the same time (you will just do two reads). (Do look at async_wait instead).
That aside, you should be able to fix the high CPU load by using async IO properly:
#include <boost/asio.hpp>
#include <iostream>
using namespace boost::asio;
int main()
{
io_service ioservice;
posix::stream_descriptor stream(ioservice, STDIN_FILENO);
char buf[1] = {};
std::function<void(boost::system::error_code, size_t)> read_handler;
read_handler = [&](boost::system::error_code ec, size_t len) {
if (ec) {
std::cerr << "exit with " << ec.message() << std::endl;
} else {
if (len == 1) {
std::cout << "keyinput=" << buf[0] << std::endl;
}
async_read(stream, buffer(buf), read_handler);
}
};
async_read(stream, buffer(buf), read_handler);
ioservice.run();
}
As you can see the while loop has been replaced with a chain of async operations.

Related

boost::asio::async_read keeps returning eof on named pipe

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.

async_read on async_pipe child process giving no data

I have the following code which is simplified from my real code where I am trying to do an async_read on an async_pipe connected to a child process. In the child process I am calling "ls ." as just a test and I want my async read to get the result of that. It returns the following
$ ./a.out
system:0
0
Why does this happen I cannot figure out? Ideally I want to replace "ls ." with a long running process where I can read line after line with async_read.
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <iostream>
#include <fstream>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <boost/process.hpp>
namespace bp = boost::process;
class test {
private:
boost::asio::io_service ios;
boost::asio::io_service::work work;
bp::async_pipe ap;
std::vector<char> buf;
public:
test()
: ios(), work(ios), ap(ios) {
}
void read(
const boost::system::error_code& ec,
std::size_t size) {
std::cout << ec << std::endl;
std::cout << size << std::endl;
}
void run() {
bp::child c(bp::search_path("ls"), ".", bp::std_out > ap);
boost::asio::async_read(ap, boost::asio::buffer(buf),
boost::bind(&test::read,
this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
ios.run();
}
};
int main() {
test c;
c.run();
}
You read into a vector of size 0.
You read 0 bytes. That's what you asked for.
I'd suggest using a streambuf and just reading till EOF. Also, drop work unless you really did want run() to never return:
Live On Coliru
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/process.hpp>
#include <iostream>
namespace bp = boost::process;
class test {
private:
boost::asio::io_service ios;
bp::async_pipe ap;
boost::asio::streambuf buf;
public:
test() : ios(), ap(ios) {}
void read(const boost::system::error_code &ec, std::size_t size) {
std::cout << ec.message() << "\n";
std::cout << size << "\n";
std::cout << &buf << std::flush;
}
void run() {
bp::child c(bp::search_path("ls"), ".", bp::std_out > ap, ios);
async_read(ap, buf, boost::bind(&test::read, this, _1, _2));
ios.run();
}
};
int main() {
test c;
c.run();
}
Prints, e.g.
End of file
15
a.out
main.cpp

Using asynchronous boost asio code for synchronous operation

I have a server and a client code written in boost ASIO and it works pretty fine.
Since synchronous and asynchornous boost asio API's are different, is it possible in any way that the code I have written for asynchronous communication behaves and works in a synchronous fashion instead of asynchronous. ?
You can run any asynchronous code on a dedicated io_service, and simply run the service blocking:
Live On Coliru
#include <boost/asio.hpp>
#include <boost/asio/high_resolution_timer.hpp>
#include <iostream>
using namespace std::chrono_literals;
using namespace boost::asio;
using boost::system::system_error;
io_service svc;
high_resolution_timer deadline(svc, 3s);
void task_foo() {
deadline.async_wait([](system_error) { std::cout << "task done\n"; });
}
int main() {
task_foo();
std::cout << "Before doing work\n";
svc.run(); // blocks!
std::cout << "After doing work\n";
}
Prints
Before doing work
task done
After doing work
Alternatively:
You can always use futures that you can then await blocking:
Live On Coliru
#include <boost/asio.hpp>
#include <boost/asio/high_resolution_timer.hpp>
#include <boost/make_shared.hpp>
#include <future>
#include <iostream>
#include <thread>
using namespace std::chrono_literals;
using namespace boost::asio;
using boost::system::system_error;
io_service svc;
high_resolution_timer deadline(svc, 3s);
std::future<int> task_foo() {
auto p = boost::make_shared<std::promise<int> >();
auto fut = p->get_future();
deadline.async_wait([p](system_error) {
std::cout << "task done\n";
p->set_value(42);
});
return fut;
}
int main() {
auto foo = task_foo();
std::cout << "Before doing work\n";
std::thread([] { svc.run(); }).detach(); // doesn't block!
std::cout << "After starting work\n"; // happens before task completion
auto result = foo.get(); // blocks again!
std::cout << "Task result: " << result << "\n";
}
Prints
Before doing work
After starting work
task done
Task result: 42
This way you can still have the io_service running concurrently and don't require it to complete even though a particular task completes synchronously (foo.get())

Boost.Asio - polling a named pipe

I am trying to listen for input on a named pipe. I'm using Boost.Asio's stream_descriptor and async_read under Linux. The problem is, the call to io_service::run() only blocks like I want it to until the first read. After that, it just keeps calling the handler immediately with the "End of file" error, even though I try to attach more async_reads to it. The code I have is equivalent to the following:
boost::asio::io_service io_service;
int fifo_d = open("/tmp/fifo", O_RDONLY);
boost::asio::posix::stream_descriptor fifo(io_service, fifo_d);
while (true)
{
// buffer and handler probably aren't important for the purposes of this question
boost::asio::async_read(fifo, buffer, handler);
io_service.run();
}
Only the first async_read works as I expect it to. Subsequent async_reads just return immediately. The only way I found to make it work like I want is to close and reopen the named pipe, but it seems like a hack:
boost::asio::io_service io_service;
while (true)
{
int fifo_d = open("/tmp/fifo", O_RDONLY);
boost::asio::posix::stream_descriptor fifo(io_service, fifo_d);
boost::asio::async_read(fifo, buffer, handler);
io_service.run();
close(fifo_d);
}
Can anyone tell me what am I doing wrong?
UPDATE: Here's a simple "read" version, which allowed for some code simplification, the problem remains the same:
int fifo_d = open("/tmp/fifo", O_RDONLY);
boost::asio::posix::stream_descriptor fifo(io_service, fifo_d);
while (true) {
try {
boost::asio::read(fifo, boost::asio::buffer(buffer));
}
catch (boost::system::system_error& err) {
// It loops here with "read: End of file" error
std::cout << err.what() << std::endl;
}
}
This is not how works. run() is not intended to be called in a loop. If you insist, you need to call reset() in between (as per the documentation).
Also, if you /want/ blocking behaviour, why are you using the async_* interface?
Demos
Consider using a simple iostream to read the fd:
Live On Coliru
#include <iostream>
#include <fstream>
int main() {
std::ifstream fifo("/tmp/fifo");
std::string word;
size_t lineno = 0;
while (fifo >> word) {
std::cout << "word: " << ++lineno << "\t" << word << "\n";
}
}
Or if you must attach to some fd you get from somewhere else, use file_descriptor from Boost IOstreams:
Live On Coliru
#include <boost/iostreams/device/file_descriptor.hpp>
#include <boost/iostreams/stream.hpp>
#include <iostream>
#include <fcntl.h>
int main() {
namespace io = boost::iostreams;
using src = io::file_descriptor_source;
io::stream<src> fifo(src(open("./fifo", O_RDONLY), io::file_descriptor_flags::close_handle));
std::string word;
size_t number = 0;
while (fifo >> word) {
std::cout << "word: " << ++number << "\t" << word << "\n";
}
}
Both examples print the expected:
word: 1 hello
word: 2 world
As also sehe reported, that's not the way boost::asio works.
The ioservice::run() method runs in blocking mode while it has some work. When the ioservice goes out of work you have to call the reset() method before putting other work, so that's why in your first code the async_read is done only once.
A common pattern in this case would look something like:
void handler(...) {
if (!error) {
// do your work
boost::asio::async_read(fifo, buffer, handler); // <-- at the end of the handler a subsequent async_read is put to the ioservice, so it never goes out-of-work
}
}
boost::asio::io_service io_service;
int fifo_d = open("/tmp/fifo", O_RDONLY);
boost::asio::posix::stream_descriptor fifo(io_service, fifo_d);
boost::asio::async_read(fifo, buffer, handler); // <-- you call async_read only once here.
io_service.run(); //<-- this blocks till an error occurs

Why boost asio signal handler is immediately canceled?

I have the following code snippet:
#include <signal.h>
#include <boost/asio.hpp>
#include <iostream>
void startAsyncWaitForSignal(boost::asio::io_service& ioService)
{
boost::asio::signal_set signals{ioService};
signals.add(SIGTERM);
signals.async_wait(
[&ioService](boost::system::error_code errorCode, int signalNumber)
{
std::cerr << errorCode.message() << std::endl;
if (!errorCode) {
std::cerr << "received signal " << signalNumber << std::endl;
startAsyncWaitForSignal(ioService);
}
}
);
}
int main() {
boost::asio::io_service ioService;
startAsyncWaitForSignal(ioService);
ioService.run();
}
I'd expect this program to wait, until the first SIGTERM is arrived, then wait to the next, then again to the next, ...
However the program is immediately terminates with the following output:
Operation canceled
What is the reason of this immediate operation cancel? I tried to make an io_service::work object, but that just changed the fact that ioService.run() was not finished, but the signal_set was still canceled immediately.
I am using boost 1.54. There is a bug fix related to asio/signals in 1.55, but that looks like a different issue.
When leaving startAsyncWaitForSignal() the local signal_set variable gets destroyed and the async_wait() call gets cancelled. The signal_set needs to live just a little longer. Move it out of startAsyncWaitForSignal() and pass it as a parameter, for example:
#include <signal.h>
#include <boost/asio.hpp>
#include <iostream>
void startAsyncWaitForSignal(boost::asio::io_service& ioService, boost::asio::signal_set& signals)
{
signals.async_wait(
[&ioService, &signals](boost::system::error_code errorCode, int signalNumber)
{
std::cerr << errorCode.message() << std::endl;
if (!errorCode) {
std::cerr << "received signal " << signalNumber << std::endl;
startAsyncWaitForSignal(ioService, signals);
}
}
);
}
int main() {
boost::asio::io_service ioService;
boost::asio::signal_set signals{ioService};
signals.add(SIGTERM);
startAsyncWaitForSignal(ioService, signals);
ioService.run();
}