boost::process async IO example doesn't work? - c++

The following program:
#include <boost/asio.hpp>
#include <boost/process.hpp>
#include <iostream>
namespace bp = boost::process;
int main() {
boost::asio::io_service ios;
std::vector<char> buf(4096);
bp::async_pipe ap(ios);
bp::child c("/bin/ls", bp::std_out > ap);
boost::asio::async_read(ap, boost::asio::buffer(buf),
[](const boost::system::error_code &ec, std::size_t size){});
ios.run();
int result = c.exit_code();
std::cout << result << std::endl;
}
outputs 383. I would expect it to output 0.
This is very nearly a copy-and-paste of the example from:
https://www.boost.org/doc/libs/1_71_0/doc/html/boost_process/tutorial.html#boost_process.tutorial.async_io

There may be a few issues here. Firstly, I think the comment in the documentation...
Passing an instance of boost::asio::io_service to the launching
function automatically cause it to wait asynchronously for the exit,
so no call of wait is needed
...refers to the example after the code you've shown. Specifically...
boost::asio::io_service ios;
std::vector<char> buf(4096);
bp::child c(bp::search_path("g++"), "main.cpp", bp::std_out > boost::asio::buffer(buf), ios);
ios.run();
int result = c.exit_code();
Where the io_service is passed by reference to the child ctor.
The comment is also slightly misleading. While it's true that the subsequent call to ios.run() does wait asynchronously for the exit it also appears (boost 1.71.0) that the exit code is not fixed up as one might hope. The exit code is stored within the child class as...
std::shared_ptr<std::atomic<int>> _exit_status;
From a quick scan of the source code it seems _exit_status->store(...) is only invoked from the following members...
boost::process::child::running
boost::process::child::wait
boost::process::child::wait_until
So, even though the process has exited (assuming all went well) when ios.run() returns one or more of running, wait or wait_until must be called to make the exit code available.
As commented elsewhere by #sehe this looks like it's possibly a regression. If I can find a bug report I'll update this. In the meantime the workaround is to simply call c.wait() before c.exit_code().

Related

Boost-Asio io_context post after run

I am new to Boost::asio and I am currently looking at io_context.
In the docs https://www.boost.org/doc/libs/1_75_0/doc/html/boost_asio/reference/io_context.html shown is the following example:
{
...
}
...
boost::asio::io_context io_context;
// Submit a function to the io_context.
boost::asio::post(io_context, my_task);
// Submit a lambda object to the io_context.
boost::asio::post(io_context,
[]()
{
...
});
// Run the io_context until it runs out of work.
io_context.run();
However, I would like to be able to post even after io_context.run() has been called.
Essentially, something like this:
#include <boost/asio.hpp>
int value = -1;
void my_task()
{
value = 42;
}
int main() {
boost::asio::io_context io_context;
// Submit a function to the io_context.
//boost::asio::post(io_context, my_task);
// Run the io_context until it runs out of work.
io_context.run();
boost::asio::executor_work_guard<boost::asio::io_context::executor_type> work(io_context.get_executor());
// Submit a lambda object to the io_context.
boost::asio::post(io_context,
[]()
{
my_task();
});
assert(value == 42);
}
After compiling the above with g++ -o a example.cpp -lboost_system -lpthread I am getting an assertion failure. What is the "right" way to accomplish this?
Either run the io_context on a separate thread, or, indeed use the execution context that already has that built in.
Using a manual thread
Live On Coliru
#include <boost/asio.hpp>
#include <iostream>
int value = -1;
void my_task() { value = 42; }
int main() {
boost::asio::io_context io_context;
auto work = make_work_guard(io_context);
std::thread thread([&] { io_context.run(); });
// submit to the io_context
post(io_context, my_task);
work.reset();
thread.join();
std::cout << value << "\n";
}
Prints
g++ -std=c++20 -O2 -Wall -pedantic -pthread main.cpp && ./a.out
42
Using asio::thread_pool:
Live On Coliru
#include <boost/asio.hpp>
#include <iostream>
int value = -1;
void my_task() { value = 42; }
int main() {
boost::asio::thread_pool io(1);
post(io, my_task);
io.join();
std::cout << value << "\n";
}
As you can see it's basically the same, but
less code
less work (literally)
more correct (see e.g. Should the exception thrown by boost::asio::io_service::run() be caught?)
trivial to make multi-threaded thread_pool io(16); or just using the default thread_pool io;
io_context::run() blocks as long as there is work to do. In your first example, you could just wait until both the posted tasks are finished. After that, io_context::run() returns and you can submit new work and call run() again. If you don't want this sequential behavior but rather submit tasks while the io_context works on tasks, you need a second thread, that executes io_conext::run(). You can create one for example like this:
#include <thread>
//...
std::thread my_thread( [&](){ io_context.run(); } );
// post more work here
// wait for it to finish
my_thread.join();
Make sure that the io_context object lives longer than the thread, if you use the lamda capture by reference like in this example.

Boost::Asio - async_wait in async_accept handler crashes application

I am currently trying to create a server application using Boost::Asio that does two simple things:
Accept a client's incoming connection
Once the client has been accepted, start a boost::asio::deadline_timer which repeats itself
The following code shows my current attempt:
#define BOOST_ASIO_ENABLE_HANDLER_TRACKING
#include <WinSock2.h>
#include <Mswsock.h>
#include <boost/asio/io_service.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/bind.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
using namespace boost::asio;
using namespace boost::asio::ip;
void timerHandler(const boost::system::error_code& errorCode, deadline_timer* timer) {
timer->expires_at(timer->expires_at() + boost::posix_time::seconds(1));
timer->async_wait(boost::bind(timerHandler, _1, timer));
}
void acceptHandler(const boost::system::error_code &errorCode, io_service *ioService) {
deadline_timer timer(*ioService, boost::posix_time::seconds(1));
timer.async_wait(boost::bind(timerHandler, _1, &timer));
}
int main(int argc, char** argv) {
io_service ioService;
tcp::socket socket(ioService);
tcp::acceptor acceptor{ ioService, tcp::endpoint{ tcp::v4(), 12345 } };
acceptor.listen();
acceptor.async_accept(socket, boost::bind(acceptHandler, _1, &ioService));
ioService.run();
return EXIT_SUCCESS;
}
Problem:
The timer somehow does not work as expected in the acceptHandler. Somehow it gets cancelled twice, triggers an error on top of that and eventually crashes the entire application.
Handler Tracking Output:
#asio|1460922050.075890|0*1|socket#000000000015FAD0.async_accept
#asio|1460922051.153952|>1|ec=system:0
#asio|1460922051.153952|1*2|deadline_timer#000000000015F608.async_wait
#asio|1460922051.153952|1|deadline_timer#000000000015F608.cancel
#asio|1460922051.153952|<1|
#asio|1460922051.153952|>2|ec=system:995
#asio|1460922051.153952|2|deadline_timer#000000000015F608.cancel
Questions:
What causes the acceptHandler to cancel the deadline_timer in line 4 of the Handler Tracking output?
What casues the error 995 in line 6 of the Handler Tracking output? Error message is: The I/O operation has been aborted because of either a thread exit or an application request
What causes the timerHandler to cancel the deadline_timer in line 7 of the Handler Tracking output?
timer is allocated on the stack in the acceptHandler and is therefore not valid by the time timerHandler is called. You need to allocate the timer dynamically.
Also, you should check for error codes in both handlers. This is especially important when you want to end the program and cancel the timer.

Boost::Asio - Passing socket to second class

I am currently trying to get the following application to work:
Await incoming client connection.
Start async. timer in another class.
While the timer runs repeatedly, do other stuff such as async_read and async_write.
Current source code:
#define BOOST_ASIO_ENABLE_HANDLER_TRACKING
#include <WinSock2.h>
#include <Mswsock.h>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include "TimerClass.hpp"
using namespace boost::asio;
using namespace boost::asio::ip;
TimerClass *timerClass;
void acceptHandler(const boost::system::error_code &errorCode, tcp::socket *socket) {
timerClass = new TimerClass(socket);
timerClass->startTimer();
while(true) {
// Do other suff such as async_write, ...
}
}
int main(int argc, char** argv) {
io_service ioService;
tcp::socket socket(ioService);
tcp::acceptor acceptor{ ioService, tcp::endpoint{ tcp::v4(), 12345 } };
acceptor.listen();
acceptor.async_accept(socket, boost::bind(acceptHandler, _1, &socket));
ioService.run();
return EXIT_SUCCESS;
}
TimerClass.hpp:
#include <boost/asio.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
using namespace boost::asio;
using namespace boost::posix_time;
class TimerClass {
public:
TimerClass(ip::tcp::socket *socket);
void startTimer();
void timerHandler(const boost::system::error_code& errorCode);
deadline_timer timer;
};
TimerClass.cpp:
#include <boost/bind.hpp>
#include "TimerClass.hpp"
TimerClass::TimerClass(ip::tcp::socket *socket) : timer(socket->get_io_service(), boost::posix_time::seconds(1)) {}
void TimerClass::startTimer() {
timer.async_wait(boost::bind(&TimerClass::timerHandler, this, boost::asio::placeholders::error));
}
void TimerClass::timerHandler(const boost::system::error_code& errorCode) {
timer.expires_at(timer.expires_at() + boost::posix_time::seconds(1));
timer.async_wait(boost::bind(&TimerClass::timerHandler, this, boost::asio::placeholders::error));
}
Handler Tracking Output:
#asio|1461070492.111630|0*1|socket#000000000021FBD0.async_accept
#asio|1461070498.527997|>1|ec=system:0
Questions:
Why won't it even call async_wait in startTimer? Debugging shows that startTimer gets called but I can't find anything in the Handler Tracking output. Why is that?
Am I correctly passing the socket to the TimerClass?
Without the infinite while(true) loop in the acceptHandler the acceptHandler returns but the application crashes before the io_service properly returns. How is that?
I compiled your code and it works for me (using boost version 1.54).
With your code I get the following output:
#asio|1461081908.437388|0*1|socket#003BFE2C.async_accept
#asio|1461081983.220840|>1|ec=system:0
#asio|1461081983.221817|1*2|deadline_timer#001C1318.async_wait
To make it run properly I had to remove the while(true) on your acceptHandler, obtaining the following output (added a std::cout inside the handler):
#asio|1461083707.104424|0*1|socket#0030FB6C.async_accept
#asio|1461083709.061824|>1|ec=system:0
#asio|1461083709.062803|1*2|deadline_timer#00641318.async_wait
#asio|1461083709.062803|<28158494073611763|
#asio|1461083710.064992|>2|ec=system:0
#asio|1461083710.064992|2|deadline_timer#00641318.cancel
#asio|1461083710.064992|2*3|deadline_timer#00641318.async_wait
TimerHandler executed...
#asio|1461083710.065971|<28169626628843099|
#asio|1461083711.065223|>3|ec=system:0
#asio|1461083711.065223|3|deadline_timer#00641318.cancel
#asio|1461083711.065223|3*4|deadline_timer#00641318.async_wait
TimerHandler executed...
I actually did this test using only the header TimerClass.hpp (defining the methods directly within it -I was lazy-) and it worked like a charm, the problem seems to be when using the .cpp file, that's why I asked if you were using include guards (not the issue though, already tested).
You should consider changing your design approach though, i.e. do not use blocking loops in you handlers, just call another asynchronous operation if needed (like async_read or async_write).
Take a look at this question and corresponding accepted answer for a nice server implementation idea. Or try to adapt some of the boost examples to your needs.
As per the segmentation fault you get when separating declaration from definition in the corresponding header and implementation files, you might want to check this other question.

What does this C++ code segment do?

can someone tell me what this code does?
const boost::system::error_code&
i suspect that this code is used to connect to the function via a pointer,
but is it everything what it does?
there is full code:
#include <iostream>
#include <boost/asio.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
void print(const boost::system::error_code&)
{
std::cout<<"hello word\n";
}
int main()
{
boost::asio::io_service io;
boost::asio::deadline_timer t(io, boost::posix_time::seconds(5));
t.async_wait(&print);
io.run();
return 0;
}
I don't know boost::asio, but I suspect that boost::asio::deadline_timer::async_wait() needs a function taking a single argument of the type const boost::system::error_code&. In order to call async_wait(), you will have to pass a pointer to such a function.
void print(const boost::system::error_code&) is such a function.
If you do not want to use a function argument, you can leave it unnamed. That prevents warnings compilers typically emit when you are not using one of the function arguments provided.

Async Resolve with Boost.Asio

I'm trying to asynchronously resolve a ftp host using Boost.Asio.
Here's what I've tried so far:
#include <iostream>
#include <string>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
using boost::asio::ip::tcp;
class FtpSession {
public:
void Connect(std::string& host) {
boost::asio::io_service io_service;
tcp::resolver resolver(io_service);
tcp::resolver::query query(host, "ftp");
resolver.async_resolve(query,
boost::bind(&FtpSession::OnResolve, this,
boost::asio::placeholders::error,
boost::asio::placeholders::iterator));
}
private:
void OnResolve(const boost::system::error_code& err, tcp::resolver::iterator endpoint_iterator) {
if (!err)
{
std::cout << "resolved!";
}
else
{
std::cout << "error.";
}
}
};
int main() {
FtpSession session;
std::string host("ftp.remotesensing.org");
session.Connect(host);
return 0;
}
But for some reason, when I execute it, it just doesn't print anything:
alon#alon-GA-73PVM-S2H:~/Desktop$ g++ -o test -lboost_system test.cc
alon#alon-GA-73PVM-S2H:~/Desktop$ ./test
alon#alon-GA-73PVM-S2H:~/Desktop$
No errors or warnings at the compilation though.
How can I fix this?
You need to call io_service.run() to actually do the work methinks. Think of the async_resolve as a request in a request queue - you need something (the io_service) to process the requests in the queue, and to do that, you actually need to run() it! In this case, it will see one request, execute it, call the handler and then exit.
Your io_service and ip::tcp::resolver object are going out of scope. Move both of them into members of the FtpSession class, then invoke io_service::run inside of main after session.Connect(host) to start the event loop.
I answered a similar question a few days ago that may help you as well.