asio service handler for stdin keypress - c++

I have adapted step 3 of the Boost asio tutorial to run forever, and display "tick" and "tock" once per second instead of the counter:
#include <iostream>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
void print(const boost::system::error_code& /*e*/,
boost::asio::deadline_timer* t, int* count)
{
if( !((*count) % 2) )
std::cout << "tick\n";
else
std::cout << "tock\n";
++(*count);
t->expires_at(t->expires_at() + boost::posix_time::seconds(1));
t->async_wait(boost::bind(print,
boost::asio::placeholders::error, t, count));
}
int main()
{
boost::asio::io_service io;
int count = 0;
boost::asio::deadline_timer t(io, boost::posix_time::seconds(1));
t.async_wait(boost::bind(print,
boost::asio::placeholders::error, &t, &count));
io.run();
std::cout << "Final count is " << count << "\n";
return 0;
}
Now I want to asynchronously be able to handle a keypress on stdin. Is there an io_service handler I can use to respond to keypresses, without blocking sleeps or waits?
For example, I'd like to be able to implement a handler function similar to:
void handle_keypress(const boost::error_code&,
char c)
{
std::cout << "Tap '" << c << "'\n";
}
And I would expect my invocation of this handler to be something along the lines of:
char c = 0;
boost::asio::stdin_receiver sr(io);
st.async_wait(boost::bind(handle_keypress, boost::asio::placeholders::error, &c));
io.run();
Is this something I can do with asio, either by using a builtin service handler, or writing my own?
EDIT, ELABORATION:
I have seen this question, but the linked-to code in the accpeted answer simply does this in main:
while (std::cin.getline(
The application I'm writing isn't this simple tick-tock-tap gizmo I've outlined above, but will be a multicast server. Several worker threads will be sending packets to multicast groups, responding to messages from the main thread, and sending messages back to the main thread. The application, in turn, will be "driven" by input from the stdin -- for example, when the user presses the "P" key, multicast broadcast will be paused, and when the hit "Q" the whole thing will shut down. In the main thread, all I'll do in response to these inputs is send messages to the worker threads.
The while loop above won't work in my scenario because while it's waiting for stdin input from the user, the main thread will not be able to process messages coming in from the worker threads. Some of those messages from the worker thread will generate output to stdout.

I don't believe the posix chat client uses a while loop or invokes std::getline, which is the sample code you linked to in my previous answer. Perhaps you are referring to another example? In any case, you don't need to use io_service::dispatch or even a separate thread. The built-in facilities of a stream descriptor work just fine here. See my previous answer to a similar question: Use a posix::stream_descriptor and assign STDIN_FILENO to it. Use async_read and handle the requests in the read handlers.
I've modified your sample code with one way to accomplish this
#include <iostream>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
void print(const boost::system::error_code& /*e*/,
boost::asio::deadline_timer* t, int* count)
{
if( !((*count) % 2) )
std::cout << "tick\n";
else
std::cout << "tock\n";
++(*count);
t->expires_at(t->expires_at() + boost::posix_time::seconds(1));
t->async_wait(boost::bind(print,
boost::asio::placeholders::error, t, count));
}
class Input : public boost::enable_shared_from_this<Input>
{
public:
typedef boost::shared_ptr<Input> Ptr;
public:
static void create(
boost::asio::io_service& io_service
)
{
Ptr input(
new Input( io_service )
);
input->read();
}
private:
explicit Input(
boost::asio::io_service& io_service
) :
_input( io_service )
{
_input.assign( STDIN_FILENO );
}
void read()
{
boost::asio::async_read(
_input,
boost::asio::buffer( &_command, sizeof(_command) ),
boost::bind(
&Input::read_handler,
shared_from_this(),
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred
)
);
}
void read_handler(
const boost::system::error_code& error,
const size_t bytes_transferred
)
{
if ( error ) {
std::cerr << "read error: " << boost::system::system_error(error).what() << std::endl;
return;
}
if ( _command != '\n' ) {
std::cout << "command: " << _command << std::endl;
}
this->read();
}
private:
boost::asio::posix::stream_descriptor _input;
char _command;
};
int main()
{
boost::asio::io_service io;
int count = 0;
boost::asio::deadline_timer t(io, boost::posix_time::seconds(1));
t.async_wait(boost::bind(print,
boost::asio::placeholders::error, &t, &count));
Input::create( io);
io.run();
std::cout << "Final count is " << count << "\n";
return 0;
}
compile, link, and run
samm:stackoverflow samm$ g++ -I /opt/local/include stdin.cc -L /opt/local/lib -lboost_system -Wl,-rpath,/opt/local/lib
samm:stackoverflow samm$ echo "hello world" | ./a.out
command: h
command: e
command: l
command: l
command: o
command:
command: w
command: o
command: r
command: l
command: d
read error: End of file
tick
tock
tick
tock
tick
tock
tick
tock
tick
tock
^C
samm:stackoverflow samm$

There is always the option of handling stdin in a separate thread and posting any keypresses to your main event loop via io_service::dispatch
This function is used to ask the io_service to execute the given handler.
The io_service guarantees that the handler will only be called in a thread in which the run(), run_one(), poll() or poll_one() member functions is currently being invoked.

Under Linux the key to getting STDIN keypress events lies in the terminal configuration. You can unset the ICANON flag via tcsetattr() and then you will get every single key pressed event. Here is an example program, which demonstrates that. Note that the buffer is larger than one, so we can get more characters at once as well, if they are availabe (for example when you copy&paste).
#include <boost/asio.hpp>
#include <iostream>
int main()
{
boost::asio::io_service ioservice;
boost::asio::posix::stream_descriptor in(ioservice, STDIN_FILENO);
char buf[10];
struct termios term_old;
tcgetattr(STDIN_FILENO, &term_old);
struct termios newt = term_old;
newt.c_lflag &= ~(ICANON); // don't wait until EOL
newt.c_lflag &= ~(ECHO); // don't echo
tcsetattr(STDIN_FILENO, TCSANOW, &newt);
std::function<void(boost::system::error_code, size_t)> read_handler
= [&](boost::system::error_code ec, size_t len) {
if (ec) {
std::cerr << "exit: " << ec.message() << std::endl;
} else {
buf[len] = '\0';
std::cout << "in: " << buf << std::endl;
in.async_read_some(boost::asio::buffer(buf), read_handler);
}
};
in.async_read_some(boost::asio::buffer(buf), read_handler);
ioservice.run();
tcsetattr(STDIN_FILENO, TCSANOW, &term_old);
}

Related

boost process running() and exit_code() thread safety

I am using boost::process::child and boost::process::async_pipe to start an application and read asynchronously (through the means of boost::asio) everything that app outputs on screen whenever this happens.
I want to check also if the application is alive by using child::running() method; if not running I'd like to read the exit code using child::exit_code.
This is very useful ESPECIALLY as it is a way to be notified about an application crashing or exiting unexpectedly (I could not find a better way); when the app exits the callback is called with boost::system::error_code set.
Do you know if I can use these two methods inside the callback called by async_pipe::async_read_some ?
In general the much more simple question would be if child::running() and child::exit_code() are thread safe (in both Windows and Linux).
namespace bp = boost::process;
char my_buffer[1024];
boost::asio::io_service io;
bp::async_pipe in_pipe(io);
void handle_pipe_read(const boost::system::error_code &ec, std::size_t bytes_transferred);
void schedule_read() {
in_pipe.async_read_some(
boost::asio::buffer(my_buffer),
boost::bind(&handle_pipe_read,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
void handle_pipe_read(
const boost::system::error_code &ec,
std::size_t bytes_transferred
)
{
// Q: Is this call possible? 'handle_pipe_read' can run in any thread
if(c->running())
std::cout << "I am alive" << std::endl;
else
std::cout << "EXIT CODE:" << c->exit_code() << std::endl;
if(ec) return; //app probably exit
// Do something with buffer and re-schedule
schedule_read();
}
int main() {
bp::child c("my_program_url", bp::std_out > in_pipe);
any_c = &c;
schedule_read();
io.run();
}
Since you only run the io_service::run() on the main thread, all completion handlers also run there. There's no threading.
Remember to pass the io_service to the child, and use the on_exit handler:
Live On Coliru
#include <boost/process.hpp>
//#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <system_error>
#include <utility>
#include <iostream>
namespace bp = boost::process;
char my_buffer[1024];
boost::asio::io_service io;
bp::async_pipe in_pipe(io);
void handle_pipe_read(const boost::system::error_code &ec, std::size_t bytes_transferred);
void schedule_read() {
in_pipe.async_read_some(
boost::asio::buffer(my_buffer),
boost::bind(&handle_pipe_read, _1, _2));
}
void handle_pipe_read(const boost::system::error_code &ec, std::size_t bytes_transferred) {
if (ec)
return; // app probably exit
// Do something with buffer and re-schedule
std::cout.write(my_buffer, bytes_transferred);
if (in_pipe.is_open())
schedule_read();
}
int main() {
bp::child c("/bin/ls", bp::std_out > in_pipe,
bp::on_exit([](int code, std::error_code ec) {
std::cout << "Child exited (" << code << "): " << ec.message() << std::endl;
in_pipe.close();
}), io);
schedule_read();
io.run();
std::cout << "Service done (" << c.exit_code() << ")" << std::endl;
}
Prints:
a.out
main.cpp
Child exited (0): Success
Service done (0)
The only solution that worked for me is the following
schedule a completion read
always check calling child::running()
on error_code set don't reschedule
When the pipe gets broken (because of a crash) the completion handler for a read has the boost::error_code argument set to true. Despite this I've seen cases where child::running() is false also when boost::error_code is NOT set.

Reading ftrace pipes with Boost::asio posix stream_descriptor

I'm trying to build an application that reads from the ftrace pipes at the debug fs.
It seems that when trying to read asynchronously from trace_pipe or
trace_pipe_raw using boost::asio API, the events waiting in pipe are being
processed and printed to screen by the async_read handle, but new events that arrive after the program started don't trigger the async_read handle.
Running the sample code below, i'm getting a print of all events waiting in queue but i don't get any print for new events that arrive later.
The same sample works perfectly if i'm trying to read from manually created pipe using mkfifo but doesn't work for the ftrace pipes.
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <string>
#include <iostream>
namespace asio = boost::asio;
#ifdef BOOST_ASIO_HAS_POSIX_STREAM_DESCRIPTOR
typedef asio::posix::stream_descriptor stream_descriptor;
#endif
class PipeReader
{
typedef std::shared_ptr<PipeReader> PipeReaderPtr;
typedef std::weak_ptr<PipeReader> PipeReaderWeakPtr;
public:
static PipeReaderWeakPtr Create(asio::io_service& io_service, const std::string& path);
void HandleRead(PipeReaderPtr me, const boost::system::error_code &error);
private:
PipeReader(asio::io_service& io_service, const std::string& path);
stream_descriptor m_pipe;
char buf[4096];
};
PipeReader::PipeReaderWeakPtr PipeReader::Create(asio::io_service& io_service, const std::string& path)
{
PipeReaderPtr ptr(new PipeReader(io_service, path));
ptr->m_pipe.async_read_some(boost::asio::buffer(ptr->buf),
boost::bind(&PipeReader::HandleRead,
ptr.get(),
ptr,
asio::placeholders::error));
return ptr;
}
PipeReader::PipeReader(asio::io_service& io_service, const std::string& path)
: m_pipe(io_service)
{
int dev = open(path.c_str(), O_RDWR);
if (dev == -1) {
std::cout << "failed to open path - " << path << std::endl;
}
else
{
m_pipe.assign(dev);
}
}
void PipeReader::HandleRead(PipeReaderPtr me, const boost::system::error_code &error)
{
if (!error) {
std::string str(me->buf);
std::cout << "got message: " << str << std::endl;
m_pipe.async_read_some(boost::asio::buffer(me->buf),
boost::bind(&PipeReader::HandleRead,
this,
me,
asio::placeholders::error));
}
else
{
std::cout << "got error - " << error.message() << std::endl;
}
}
int main()
{
boost::asio::io_service io_service;
boost::asio::io_service::work dummy(io_service);
PipeReader::Create(io_service, "/sys/kernel/debug/tracing/trace_pipe");
io_service.run();
return 0;
}
I found the problem. it was a bug in the implementation of ftrace that caused the epoll to hang.
The bug was fixed at kernel 3.16.
correspondence thread,
commit in git hub

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

asynchronous read write with device file

I am writing some binary data to a device fie like /dev/itun.
void ahaConnector::asyncWriteData(vector<uint8_t> packedMessage) {
cout<<"\n async write data packed message";
deviceStreamDescriptor.assign(device);
boost::asio::write (
deviceStreamDescriptor,
boost::asio::buffer(packedMessage)
);
readbuffer.resize(1024);
deviceStreamDescriptor.async_read_some(boost::asio::buffer(readbuffer),
boost::bind(&ahaConnector::readHeader, this,
boost::asio::placeholders::error(),
boost::asio::placeholders::bytes_transferred()
));
io_service.run();
}
void ahaConnector::readHeader(const boost::system::error_code &ec, std::size_t bytes_transferred) {
if(!ec) {
std::cout<<"\n Bytes transfereed :"<<bytes_transferred<<" "<<readbuffer.size();
deviceStreamDescriptor.async_read_some(boost::asio::buffer(readbuffer),
boost::bind(&ahaConnector::readHeader, this,
boost::asio::placeholders::error(),
boost::asio::placeholders::bytes_transferred()
));
Callbacks callbacks;
callbacks.OnReceivingPackedMessage();
io_service.run();
}
else {
cout<<"\n System Error Code "<<ec;
}
}
The callback function readhandler is getting executed successfully, however I am not able to transfer the control from my Callback function to another class.
Is something wrong from the design perspective. I need to handle the message received from the callback function for further logic. Should I use another thread here ?
Looking at this code you might just want to replace the read(device,...) by boost Asio's support for Posix streams:
#include <boost/asio.hpp>
#include <boost/asio/posix/stream_descriptor.hpp>
#include <boost/function.hpp>
#include <iostream>
static int device = 0;
using namespace boost;
int main() {
boost::asio::io_service io_svc;
boost::asio::posix::stream_descriptor iodevice(io_svc, device);
char buffer[1024];
function<void(system::error_code const&, size_t)> callback;
callback = [&](boost::system::error_code const& ec, size_t bytes_transferred) {
if (ec)
{
std::cout << "Error '" << ec.message() << "' during asynchronous operation\n";
}
else
{
std::cout << "Read exactly " << bytes_transferred << " bytes\n";
std::cout << "Data: '";
std::cout.write(buffer, bytes_transferred);
std::cout << "'\n";
iodevice.async_read_some(asio::buffer(buffer), callback);
}
};
iodevice.async_read_some(asio::buffer(buffer), callback);
io_svc.run();
}
See it Live On Coliru.
Sadly on Coliru it can't work because input is redirected from a non-stream. But if you run it interactively it will work and print the first 10 characters entered.
The answer depends on exactly what are the properties of the device. Check the documentation for the device driver you're trying to use. If the device supports non-blocking I/O, open the device with O_NONBLOCK, and use poll() to wait for device to be available for reading or writing.
If the device does not support non-blocking I/O, the only viable option would be to use a separate thread to read and/or write to the device, and use the background thread to construct facade that pretends and behaves like a non/blocking data source and sink.

How to asynchronously read input from command line using boost asio in Windows?

I found this question which asks how to read input asynchronously, but will only work with POSIX stream descriptors, which won't work on Windows. So, I found this tutorial which shows that instead of using a POSIX stream descriptor I can use a boost::asio::windows::stream_handle.
Following both examples I came up with the code below. When I run it, I cannot type anything into the command prompt, as the program immediately terminates. I'd like it to capture any input from the user, possibly into a std::string, while allowing other logic within my program to execute (i.e. perform asynchronous I/O from a Windows console).
Essentially, I'm trying to avoid blocking my program when it attempts to read from stdin. I do not know if this is possible in Windows, as I also found this post which details problems another user encountered when trying to do the same thing.
#define _WIN32_WINNT 0x0501
#define INPUT_BUFFER_LENGTH 512
#include <cstdio>
#include <iostream>
#define BOOST_THREAD_USE_LIB // For MinGW 4.5 - (https://svn.boost.org/trac/boost/ticket/4878)
#include <boost/bind.hpp>
#include <boost/asio.hpp>
class Example {
public:
Example( boost::asio::io_service& io_service)
: input_buffer( INPUT_BUFFER_LENGTH), input_handle( io_service)
{
// Read a line of input.
boost::asio::async_read_until( input_handle, input_buffer, "\r\n",
boost::bind( &Example::handle_read, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
void handle_read( const boost::system::error_code& error, std::size_t length);
void handle_write( const boost::system::error_code& error);
private:
boost::asio::streambuf input_buffer;
boost::asio::windows::stream_handle input_handle;
};
void Example::handle_read( const boost::system::error_code& error, std::size_t length)
{
if (!error)
{
// Remove newline from input.
input_buffer.consume(1);
input_buffer.commit( length - 1);
std::istream is(&input_buffer);
std::string s;
is >> s;
std::cout << s << std::endl;
boost::asio::async_read_until(input_handle, input_buffer, "\r\n",
boost::bind( &Example::handle_read, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
else if( error == boost::asio::error::not_found)
{
std::cout << "Did not receive ending character!" << std::endl;
}
}
void Example::handle_write( const boost::system::error_code& error)
{
if (!error)
{
// Read a line of input.
boost::asio::async_read_until(input_handle, input_buffer, "\r\n",
boost::bind( &Example::handle_read, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
}
int main( int argc, char ** argv)
{
try {
boost::asio::io_service io_service;
Example obj( io_service);
io_service.run();
} catch( std::exception & e)
{
std::cout << e.what() << std::endl;
}
std::cout << "Program has ended" << std::endl;
getchar();
return 0;
}
I just spent an hour or two investigating this topic so decided to post to prevent others to waste their time.
Windows doesn't support IOCP for standard input/output handles. When you take the handle by GetStdHandle(STD_INPUT_HANDLE), the handle doesn't have FILE_FLAG_OVERLAPPED set so it doesn't support overlapped (async) IO. But even if you
CreateFile(L"CONIN$",
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING,
NULL);
WinAPI just ignore dwFlagsAndAttributes and again returns the handle that doesn't support overlapped IO. The only way to get async IO of console input/output is to use the handle with WaitForSingleObject with 0 timeout so you can check if there's anything to read non-blocking. Not exactly async IO but can avoid multithreading if it's a goal.
More details about console API: https://msdn.microsoft.com/en-us/library/ms686971(v=VS.85).aspx
What's the difference between handles returned by GetStdHandle and CreateFile is described here: https://msdn.microsoft.com/en-us/library/windows/desktop/ms682075(v=vs.85).aspx. In short the difference is only for a child processes when CreateFile can give access to its console input buffer even if it was redirected in the parent process.
You need to invoke io_service::run() to start the event processing loop for asynchronous operations.
class Example {
public:
Example( boost::asio::io_service& io_service )
: io_service(io_service), input_buffer( INPUT_BUFFER_LENGTH), input_handle( io_service)
{
}
void start_reading();
void handle_read( const boost::system::error_code& error, std::size_t length);
void handle_write( const boost::system::error_code& error);
private:
boost::asio::io_service& io_service;
boost::asio::streambuf input_buffer;
boost::asio::windows::stream_handle input_handle;
};
int main( int argc, char * argv)
{
boost::asio::io_service io_service;
Example obj( io_service );
obj.start_reading();
io_service.run();
return 0;
}
You need to initialize your stream_handle to the console input handle. You can't use the same stream_handle for input and for output because those are two different handles.
For input:
Example()
: /* ... */ input_handle( io_service, GetStdHandle(STD_INPUT_HANDLE) )
For output you would use CONSOLE_OUTPUT_HANDLE. But that is probably overkill, you're unlikely to be pushing that much data into stdout on windows that you'd need to use an async write.