Why boost asio signal handler is immediately canceled? - c++

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();
}

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.

boost asio async_read_some timeout

I use this code using async_read_some with timeout
readdata=0;
port_->async_read_some(boost::asio::buffer(vector),
boost::bind(readCallback));
//init async timer
boost::asio::deadline_timer timer(io);
timer.async_wait(boost::bind(timeoutHandler));
timer.expires_from_now(boost::posix_time::seconds(5));
io.reset();
do {
io.run_one();
}
while (readdata==0);
here are my callbacks
void readCallback()
{
std::cout << "READ CALLBACK: "<<x<<std::endl;
readdata=1;
return;
}
void timeoutHandler()
{
std::cout << "TIMEOUT CALLBACK: "<<x<<std::endl;
readdata=1;
}
my Problem is that timeoutHandler is is executed instantly and not after 5 seconds
Simple mistake. You should be doing expires_from_now before calling async_wait.
#include <iostream>
#include <asio.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
int main() {
asio::io_service io_s;
asio::deadline_timer timer(io_s);
timer.expires_from_now(boost::posix_time::seconds(5));
timer.async_wait([](auto err_c) { std::cout << "After 5 seconds" << std::endl; } );
io_s.reset();
io_s.run();
return 0;
}

Boost timer expires immediately

I run boost deadline_timer and do async_wait, but timer cancelled immediately. What i am doing wrong? I run ioService in my main file.
Thank you for any possible help
class A(boost::asio:io_service& ioService):
m_timer(ioService)
{
m_timer.expires_at(boost::posix_time::pos_infin);
m_timer.async_wait([this](const boost::system::error_code& ec)
{
std::cout << "Timer callback " << ec.message() << std::endl;
});
Check the lifetime of your A object.
E.g. if you do this:
#include <boost/asio.hpp>
#include <iostream>
struct A {
A(boost::asio::io_service& ioService) : m_timer(ioService)
{
m_timer.expires_at(boost::posix_time::pos_infin);
m_timer.async_wait(
[this](const boost::system::error_code& ec) { std::cout << "Timer callback " << ec.message() << std::endl; }
);
}
boost::asio::deadline_timer m_timer;
};
int main()
{
boost::asio::io_service svc;
{
A a(svc);
}
svc.run();
}
The timer will have been canceled even before run() is invoked.
The following will do what you expected
int main()
{
boost::asio::io_service svc;
{
A a(svc);
svc.run();
} // A destructed after `run()` completes
}

Resuming asio coroutine from another thread

I have a problem with resuming boost::asio coroutine from another thread. Here is sample code:
#include <iostream>
#include <thread>
#include <boost/asio.hpp>
#include <boost/asio/steady_timer.hpp>
#include <boost/asio/spawn.hpp>
using namespace std;
using namespace boost;
void foo(asio::steady_timer& timer, asio::yield_context yield)
{
cout << "Enter foo" << endl;
timer.expires_from_now(asio::steady_timer::clock_type::duration::max());
timer.async_wait(yield);
cout << "Leave foo" << endl;
}
void bar(asio::steady_timer& timer)
{
cout << "Enter bar" << endl;
sleep(1); // wait a little for asio::io_service::run to be executed
timer.cancel();
cout << "Leave bar" << endl;
}
int main()
{
asio::io_service ioService;
asio::steady_timer timer(ioService);
asio::spawn(ioService, bind(foo, std::ref(timer), placeholders::_1));
thread t(bar, std::ref(timer));
ioService.run();
t.join();
return 0;
}
The problem is that the asio::steady_timer object is not thread safe and the program crashes. But if I try to use mutex to synchronize access to it then I have a deadlock because the scope of foo is not leaved.
#include <iostream>
#include <thread>
#include <mutex>
#include <boost/asio.hpp>
#include <boost/asio/steady_timer.hpp>
#include <boost/asio/spawn.hpp>
using namespace std;
using namespace boost;
void foo(asio::steady_timer& timer, mutex& mtx, asio::yield_context yield)
{
cout << "Enter foo" << endl;
{
lock_guard<mutex> lock(mtx);
timer.expires_from_now(
asio::steady_timer::clock_type::duration::max());
timer.async_wait(yield);
}
cout << "Leave foo" << endl;
}
void bar(asio::steady_timer& timer, mutex& mtx)
{
cout << "Enter bar" << endl;
sleep(1); // wait a little for asio::io_service::run to be executed
{
lock_guard<mutex> lock(mtx);
timer.cancel();
}
cout << "Leave bar" << endl;
}
int main()
{
asio::io_service ioService;
asio::steady_timer timer(ioService);
mutex mtx;
asio::spawn(ioService, bind(foo, std::ref(timer), std::ref(mtx),
placeholders::_1));
thread t(bar, std::ref(timer), std::ref(mtx));
ioService.run();
t.join();
return 0;
}
There is no such a problem if I use standard completion handler instead of coroutines.
#include <iostream>
#include <thread>
#include <mutex>
#include <boost/asio.hpp>
#include <boost/asio/steady_timer.hpp>
using namespace std;
using namespace boost;
void baz(system::error_code ec)
{
cout << "Baz: " << ec.message() << endl;
}
void foo(asio::steady_timer& timer, mutex& mtx)
{
cout << "Enter foo" << endl;
{
lock_guard<mutex> lock(mtx);
timer.expires_from_now(
asio::steady_timer::clock_type::duration::max());
timer.async_wait(baz);
}
cout << "Leave foo" << endl;
}
void bar(asio::steady_timer& timer, mutex& mtx)
{
cout << "Enter bar" << endl;
sleep(1); // wait a little for asio::io_service::run to be executed
{
lock_guard<mutex> lock(mtx);
timer.cancel();
}
cout << "Leave bar" << endl;
}
int main()
{
asio::io_service ioService;
asio::steady_timer timer(ioService);
mutex mtx;
foo(std::ref(timer), std::ref(mtx));
thread t(bar, std::ref(timer), std::ref(mtx));
ioService.run();
t.join();
return 0;
}
Is it possible to have behavior similar to the last example when couroutines are used.
A coroutine runs within the context of a strand. In spawn(), if one is not explicitly provided, a new strand will be created for the coroutine. By explicitly providing strand to spawn(), one can post work into the strand that will be synchronized with the coroutine.
Also, as noted by sehe, undefined behavior may occur if the coroutine is running in one thread, acquires a mutex lock, then suspends, but resumes and runs in a different thread and releases the lock. To avoid this, ideally one should not hold locks while the coroutine suspends. However, if it is necessary, one must guarantee that the coroutine runs within the same thread when it is resumed, such as by only running the io_service from a single thread.
Here is the minimal complete example based on the original example where bar() posts work into a strand to cancel the timer, causing the foo() coroutine to resume:
#include <iostream>
#include <thread>
#include <boost/asio.hpp>
#include <boost/asio/spawn.hpp>
#include <boost/asio/steady_timer.hpp>
void foo(boost::asio::steady_timer& timer, boost::asio::yield_context yield)
{
std::cout << "Enter foo" << std::endl;
timer.expires_from_now(
boost::asio::steady_timer::clock_type::duration::max());
boost::system::error_code error;
timer.async_wait(yield[error]);
std::cout << "foo error: " << error.message() << std::endl;
std::cout << "Leave foo" << std::endl;
}
void bar(
boost::asio::io_service::strand& strand,
boost::asio::steady_timer& timer
)
{
std::cout << "Enter bar" << std::endl;
// Wait a little for asio::io_service::run to be executed
std::this_thread::sleep_for(std::chrono::seconds(1));
// Post timer cancellation into the strand.
strand.post([&timer]()
{
timer.cancel();
});
std::cout << "Leave bar" << std::endl;
}
int main()
{
boost::asio::io_service io_service;
boost::asio::steady_timer timer(io_service);
boost::asio::io_service::strand strand(io_service);
// Use an explicit strand, rather than having the io_service create.
boost::asio::spawn(strand, std::bind(&foo,
std::ref(timer), std::placeholders::_1));
// Pass the same strand to the thread, so that the thread may post
// handlers synchronized with the foo coroutine.
std::thread t(&bar, std::ref(strand), std::ref(timer));
io_service.run();
t.join();
}
Which provides the following output:
Enter foo
Enter bar
foo error: Operation canceled
Leave foo
Leave bar
As covered in this answer, when the boost::asio::yield_context detects that the asynchronous operation has failed, such as when the operation is canceled, it converts the boost::system::error_code into a system_error exception and throws. The above example uses yield_context::operator[] to allow the yield_context to populate the provided error_code on failure instead of throwing throwing.

boost::asio::deadline_timer doesn't wake up (stress scenario)

I'm using a deadline_timer as an asynchronous event and I'm running into a situation where, after some time, the thread waiting on the event never seems to be woken up (despite more calls to cancel()). I've been able to reproduce this using some sample code that I've pasted below; it's not exactly consistent but I have seen what I think is the same issue I'm experiencing.
boost::asio::io_service io_service;
boost::asio::deadline_timer timer(io_service);
timer.expires_at(boost::posix_time::pos_infin);
int num_events = 0;
auto waiter = [&timer, &num_events](boost::asio::yield_context context) {
while (true) {
std::cout << "waiting on event" << std::endl;
boost::system::error_code e;
timer.async_wait(context[e]);
std::cout << "got event (" << e << ")" << std::endl;
++num_events;
}
};
boost::asio::spawn(io_service, std::move(waiter));
boost::thread thread(boost::bind(&boost::asio::io_service::run, &io_service));
for (auto i = 0; i < 500000; ++i) {
timer.cancel();
std::cout << i << std::endl;
}
Am I doing something here that's unsupported and inadvertently hitting some race condition? The error code from the wait() never looks troublesome (even on the very last time it's woken up before it never seems to again). EDIT: I've also noticed the original bug on 3 different platforms (Windows, Mac and Linux) but the above test I've been using to reproduce has been on Windows.
The deadline_timer object is not threadsafe.
You're canceling it from another thread than the one that's posting the async_wait. This means the calls can race.
I'm not sure how this can completely inhibit the callback, in your sample. It seems to me that the program should /just/ quit because the tight loop to 500000 finishes quickly (doing many redundant cancels that never get processed, because the coroutine would e.g. not even have posted the new async_wait).
So maybe you mean, "why don't I get 500000 events".
UPDATE
After the comment, here's a trivial transformation that shows how you are gonna be fine calling members on the timer from within an actor. Note: this critically hinges on the idea that the io_service is run from a single thread only!
#include <boost/asio.hpp>
#include <boost/asio/spawn.hpp>
#include <boost/make_shared.hpp>
#include <boost/thread.hpp>
#include <iostream>
using boost::thread;
using boost::asio::io_service;
int main() {
boost::asio::io_service io_service;
boost::asio::deadline_timer timer(io_service);
timer.expires_at(boost::posix_time::pos_infin);
boost::atomic_bool shutdown(false);
int num_events = 0;
auto waiter = [&timer, &num_events, &shutdown](boost::asio::yield_context context) {
while (!shutdown) {
std::cout << "waiting on event" << std::endl;
boost::system::error_code e;
timer.async_wait(context[e]);
std::cout << "got event (" << e.message() << ")" << std::endl;
++num_events;
}
};
boost::asio::spawn(io_service, std::move(waiter));
boost::thread thread(boost::bind(&boost::asio::io_service::run, &io_service));
for (auto i = 0; i < 5000; ++i) {
io_service.post([&timer, i]{
std::cout << i << std::endl;
timer.cancel();
});
}
io_service.post([&]{
shutdown = true;
timer.cancel();
});
thread.join();
std::cout << "Check: " << num_events << " events counted\n";
}
Also, it looks like you just wanted to signal a background task. As given you can simplify the program like:
See it Live On Coliru
#include <boost/asio.hpp>
#include <boost/thread.hpp>
#include <boost/make_shared.hpp>
#include <iostream>
using boost::thread;
using boost::asio::io_service;
int main() {
io_service svc;
int num_events = 0;
auto work = boost::make_shared<io_service::work>(svc); // keep svc running
boost::thread thread(boost::bind(&io_service::run, &svc));
for (auto i = 0; i < 500000; ++i) {
svc.post([&num_events,i]{
std::cout << "got event (" << i << ")" << std::endl;
++num_events;
});
}
work.reset();
thread.join();
std::cout << "Check: " << num_events << " events counted\n";
}
This does print all 500000 events:
got event (0)
got event (1)
got event (3)
...
got event (499998)
got event (499999)
Check: 500000 events counted