I'm working on some comms code for which I need a regular timer for monitoring. I'm using boost ASIO for the comms so I decided to use a deadline timer for the timing and put it on the same IO service.
Everything works great the first time the code is run but it goes wrong once the comms (in my case a serial port) is stopped and restarted. The timer begins to malfunction and the comms break down. I believe the two are related so I'm just focusing on the timer for this question.
Consider the code below. This should start a timer and let it run for 10 seconds, stop the timer, and then start it again for another 10 seconds. What actually happens though is that when the timer is restarted it fires continuously, ie without any delay between firing.
#include <iostream>
#include <thread>
#include <boost/bind.hpp>
#include <boost/asio.hpp>
boost::posix_time::ptime timer_start_;
void CallbackTimerFunc(boost::asio::deadline_timer* timer) {
auto time_since_start = timer->expires_at() - timer_start_;
std::cout << "It's been " << time_since_start.total_seconds() << " seconds." << std::endl;
// Sleep is here to prevent spamming when timer starts malfunctioning.
usleep(20000);
timer->expires_at(timer->expires_at() + boost::posix_time::milliseconds(1000));
timer->async_wait(boost::bind(&CallbackTimerFunc, timer));
}
int main(int /*argc*/, char** /*args*/) {
// Start
boost::asio::io_service io_service_;
boost::asio::deadline_timer deadline_timer_(io_service_);
deadline_timer_.expires_from_now(boost::posix_time::milliseconds(1000));
timer_start_ = deadline_timer_.expires_at();
deadline_timer_.async_wait(boost::bind(&CallbackTimerFunc, &deadline_timer_));
std::thread io_thread_(boost::bind(&boost::asio::io_service::run, &io_service_));
// Stop
sleep(10);
io_service_.stop();
while (!io_service_.stopped()) usleep(10000);
deadline_timer_.cancel();
io_thread_.join();
std::cout << "******************************" << std::endl;
// Restart
io_service_.restart();
deadline_timer_.expires_from_now(boost::posix_time::milliseconds(1000));
timer_start_ = deadline_timer_.expires_at();
deadline_timer_.async_wait(boost::bind(&CallbackTimerFunc, &deadline_timer_));
io_thread_ = std::thread(boost::bind(&boost::asio::io_service::run, &io_service_));
// Stop
sleep(10);
io_service_.stop();
while (!io_service_.stopped()) usleep(10000);
deadline_timer_.cancel();
io_thread_.join();
return 0;
}
Expected output is for the timer to count to 10 (well in reality from 0 to 8) twice. The actual output is that it counts to 10 once, and then just goes haywire claiming that hundreds of seconds are passing.
I can make this code work by creating a brand new IO service and timer but that seems like it should be unnecessary given that they are supposed to be reusable.
If anyone can tell me what's going on here or at least reproduce my results I'd appreciate it.
Thanks to #tkausl for putting me on the right track. Here is the corrected code. Note the extra check at the top of CallbackTimerFunc.
#include <iostream>
#include <thread>
#include <boost/bind.hpp>
#include <boost/asio.hpp>
boost::posix_time::ptime timer_start_;
void CallbackTimerFunc(const boost::system::error_code& error, boost::asio::deadline_timer* timer) {
if (error.value() == boost::asio::error::operation_aborted) {
std::cout << "Abort was sent on the first firing. Because of course it would be. Ignoring it will fix the problem. Because of course it will." << std::endl;
return;
}
auto time_since_start = timer->expires_at() - timer_start_;
std::cout << "It's been " << time_since_start.total_seconds() << " seconds." << std::endl;
// Sleep is here to prevent spamming when timer starts malfunctioning.
usleep(20000);
timer->expires_at(timer->expires_at() + boost::posix_time::milliseconds(1000));
timer->async_wait(boost::bind(&CallbackTimerFunc, boost::asio::placeholders::error, timer));
}
int main(int /*argc*/, char** /*args*/) {
// Start
boost::asio::io_service io_service_;
boost::asio::deadline_timer deadline_timer_(io_service_);
deadline_timer_.expires_from_now(boost::posix_time::milliseconds(1000));
timer_start_ = deadline_timer_.expires_at();
deadline_timer_.async_wait(boost::bind(CallbackTimerFunc, boost::asio::placeholders::error, &deadline_timer_));
std::thread io_thread_(boost::bind(&boost::asio::io_service::run, &io_service_));
// Stop
sleep(10);
io_service_.stop();
while (!io_service_.stopped()) usleep(10000);
deadline_timer_.cancel();
io_thread_.join();
std::cout << "******************************" << std::endl;
// Restart
io_service_.restart();
deadline_timer_.expires_from_now(boost::posix_time::milliseconds(1000));
timer_start_ = deadline_timer_.expires_at();
deadline_timer_.async_wait(boost::bind(CallbackTimerFunc, boost::asio::placeholders::error, &deadline_timer_));
io_thread_ = std::thread(boost::bind(&boost::asio::io_service::run, &io_service_));
// Stop
sleep(10);
io_service_.stop();
while (!io_service_.stopped()) usleep(10000);
deadline_timer_.cancel();
io_thread_.join();
return 0;
}
Related
So I was experimenting with the deadline_timer class and wrote the code below to see if I could have on deadline_timer with multiple async_wait operations that would execute at different times.
Below I create a deadline timer in the main function all the way at the bottom and initially set it to expire after 3 seconds. Then I call an async_wait operation and pass the first print function as the handler. I then use the expires_from_now operation to set the time of expiration for what I intended to only affect the second async_wait call which has print2 as a handler. The output from running this is below the code.
This is test1.cpp
#include <iostream>
#include <string>
#include <boost/asio.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/bind.hpp>
#include <time.h>
#include <sys/time.h>
double get_wall_time(){
struct timeval time;
if (gettimeofday(&time,NULL)){
// Handle error
return 0;
}
return (double)time.tv_sec + (double)time.tv_usec * .000001;
}
void print(double start, const boost::system::error_code& /*e*/)
{
std::cout << "Hello, world!" << std::endl;
std::cout << get_wall_time() - start << std::endl;
}
void print2(double start, const boost::system::error_code& /*e*/)
{
std::cout << "SECOND Hello, world!" << std::endl;
std::cout << get_wall_time() - start << std::endl;
}
int main(int argc, char* argv[])
{
boost::asio::io_service io;
boost::asio::deadline_timer timer(io, boost::posix_time::seconds(3));
auto start = get_wall_time();
timer.async_wait(boost::bind(print, start, boost::asio::placeholders::error));
timer.expires_from_now(boost::posix_time::seconds(20));
timer.async_wait(boost::bind(print2, start, boost::asio::placeholders::error));
io.run();
return 0;
}
Here is the output
Hello, world!
0.000774145
SECOND Hello, world!
20.0085
This is the output after commenting out the second async_wait with the expiration modification.
Hello, world!
3.00079
As you can see the first handler executes instantly when I intended for it to execute after 3 seconds. The second handler correctly executes after 20 seconds. Is there any way I could get the behavior I intended with a deadline_timer without having to create a bunch of them?
A timer must have only one outstanding async_wait at a time. IIRC, issuing another implicitly cancels the first one (which will fire it's handler with an error code) as if you called cancel() followed by async_wait().
If you want to respond to 2 timer events, you have 2 choices. Either have 2 timers, or set the timeout and issue the second async_wait in the handler of the first.
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
I require a heartbeat signal every 10 seconds or so. To implement this I have produced a class with the following constructor:
HeartBeat::HeartBeat (int Seconds, MessageQueue * MsgQueue)
{
TimerSeconds = Seconds;
pQueue = MsgQueue;
isRunning = true;
assert(!m_pHBThread);
m_pHBThread = shared_ptr<thread>(new thread(boost::bind(&HeartBeat::TimerStart,this)));
}
Which calls the following method in a new thread:
void HeartBeat::TimerStart ()
{
while (1)
{
cout << "heartbeat..." << endl;
boost::this_thread::sleep(boost::posix_time::seconds (TimerSeconds));
addHeartBeat();
}
}
This produces a heartbeat with out any issues. However I would like to be able to reset the sleep timer back to zero. Is there a simple way of doing this, or should I use something other than
boost::this_thread::sleep
for my sleep?
OS: Redhat
IDE: Eclipse
Code language: C++
EDIT:
I have looked at using
m_pHBThread->interrupt();
And it seems to be what I'm after, so thank you!
This sounds exactly like what asynchronous timer does. Since you're using boost already, perhaps it makes sense to use boost's own async timers in the long run?
#include <iostream>
#include <boost/thread.hpp>
#include <boost/date_time.hpp>
#include <boost/asio.hpp>
boost::posix_time::ptime now()
{
return boost::posix_time::microsec_clock::local_time();
}
class HeartBeat {
boost::asio::io_service ios;
boost::asio::deadline_timer timer;
boost::posix_time::time_duration TimerSeconds;
boost::thread thread;
public:
HeartBeat(int Seconds) : ios(), timer(ios),
TimerSeconds(boost::posix_time::seconds(Seconds))
{
reset(); // has to start timer before starting the thread
thread = boost::thread(boost::bind(&boost::asio::io_service::run,
&ios));
}
~HeartBeat() {
ios.stop();
thread.join();
}
void reset()
{
timer.expires_from_now(TimerSeconds);
timer.async_wait(boost::bind(&HeartBeat::TimerExpired,
this, boost::asio::placeholders::error));
}
void TimerExpired(const boost::system::error_code& ec)
{
if (ec == boost::asio::error::operation_aborted) {
std::cout << "[" << now() << "] timer was reset" << std::endl;
} else {
std::cout << "[" << now() << "] heartbeat..." << std::endl;
reset();
}
}
};
int main()
{
std::cout << "[" << now() << "] starting up.\n";
HeartBeat hb(10);
sleep(15);
std::cout << "[" << now() << "] Resetting the timer\n";
hb.reset();
sleep(15);
}
test run:
~ $ ./test
[2011-Sep-07 12:08:17.348625] starting up.
[2011-Sep-07 12:08:27.348944] heartbeat...
[2011-Sep-07 12:08:32.349002] Resetting the timer
[2011-Sep-07 12:08:32.349108] timer was reset
[2011-Sep-07 12:08:42.349160] heartbeat...
Perhaps you can use interrupt() to do this.
Well, it is not very efficient to launch a new thread every time you have an heart beat...
I'd do it instead with a single thread and a sleep inside it.
If you need to change the heart beat frequency then you can kill the current thread and start a new one with a new sleep time. You can use the boost::thread and the interrupt signal for this.
EDIT: look here for info on boost threads: boost thread management
if you want to reset the time to zero and execute your code immediately then call it inside the catch for boost::thread_interrupted...
EDIT2: I didn't look properly to the code and I assumed that the common error of launching a new thread for each heart beat was there... sorry, my mistake... I guess I don't like the fact that the thread's name is: TimerStart()
Anyway I think that using the interrupt() and catching it should work if you need to execute the heart beat right away.
I tried using the boost deadline_timer in this simple test application, but had some trouble. The goal is for the timer to trigger every 45 milliseconds using the expires_at() member function of the deadline_timer. (I need an absolute time, so I'm not considering expires_from_now(). I am also not concerned about drift at the moment). When I run the program, wait() does not wait for 45 ms! Yet, no errors are reported. Am I using the library incorrectly somehow?
Sample program:
#include <boost/asio.hpp>
#include <boost/thread.hpp>
#include <boost/bind.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <iostream>
using namespace std;
int main()
{
boost::asio::io_service Service;
boost::shared_ptr<boost::thread> Thread;
boost::asio::io_service::work RunForever(Service);
Thread = boost::shared_ptr<boost::thread>(new boost::thread(boost::bind(&boost::asio::io_service::run, &Service)));
boost::shared_ptr<boost::asio::deadline_timer> Timer(new boost::asio::deadline_timer(Service));
while(1)
{
boost::posix_time::time_duration Duration;
Duration = boost::posix_time::microseconds(45000);
boost::posix_time::ptime Start = boost::posix_time::microsec_clock::local_time();
boost::posix_time::ptime Deadline = Start + Duration;
boost::system::error_code Error;
size_t Result = Timer->expires_at(Deadline, Error);
cout << Result << ' ' << Error << ' ';
Timer->wait(Error);
cout << Error << ' ';
boost::posix_time::ptime End = boost::posix_time::microsec_clock::local_time();
(cout << "Duration = " << (End - Start).total_milliseconds() << " milliseconds" << endl).flush();
}
return 0;
}
You are mixing local time with system time. The time that asio is comparing your local time to is most likely some number of hours after the time that you want your deadline set to so wait returns immediately (depending on where you live; this same code could wait for several hours as well). To avoid this point of confusion, absolute times should be derived from asio::time_traits.
#include <boost/asio.hpp>
#include <boost/asio/time_traits.hpp>
#include <boost/thread.hpp>
#include <boost/bind.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <iostream>
using namespace std;
typedef boost::asio::time_traits<boost::posix_time::ptime> time_traits_t;
int main() {
boost::asio::io_service Service;
boost::shared_ptr<boost::thread> Thread;
boost::asio::io_service::work RunForever(Service);
Thread = boost::shared_ptr<boost::thread>(new boost::thread(boost::bind(&boost::asio::io_service::run, &Service)));
boost::shared_ptr<boost::asio::deadline_timer> Timer(new boost::asio::deadline_timer(Service));
while(1)
{
boost::posix_time::time_duration Duration;
Duration = boost::posix_time::microseconds(45000);
boost::posix_time::ptime Start = time_traits_t::now();
boost::posix_time::ptime Deadline = Start + Duration;
boost::system::error_code Error;
size_t Result = Timer->expires_at(Deadline, Error);
cout << Result << ' ' << Error << ' ';
Timer->wait(Error);
cout << Error << ' ';
boost::posix_time::ptime End = boost::posix_time::microsec_clock::local_time();
(cout << "Duration = " << (End - Start).total_milliseconds() << " milliseconds" << endl).flush();
}
return 0;
}
That should work out for you in this case.
You are mixing asynchronous methods io_service::run with synchronous methods deadline_timer::wait. This will not work. Either use deadline_timer::async_wait with io_service::run, or skip the io_service::run and just use deadline_timer::wait. You also don't need a thread to invoke io_service:run if you go the asynchronous route, one thread will do just fine. Both concepts are explained in detail in the Basic Skills section of the Asio tutorial.
void print(const boost::system::error_code& /*e*/)
{
std::cout << "Hello, world!\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;
}
Note you will need to give some work for your io_service to service prior to invoking run(). In this example, async_wait is that work.
Potentially unrelated: 45ms is quite a small delta. In my experience the smallest time for any handler to make it through the Asio epoll reactor queue is around 30 ms, this can be considerably longer at higher loads. Though it all largely depends on your application.
I want a very simple periodic timer to call my code every 50ms. I could make a thread that sleeps for 50ms all the time (but that's a pain)... I could start looking into Linux API's for making timers (but it's not portable)...
I'd like to use boost.. I'm just not sure it's possible. Does boost provide this functionality?
A very simple, but fully functional example:
#include <iostream>
#include <boost/asio.hpp>
boost::asio::io_service io_service;
boost::posix_time::seconds interval(1); // 1 second
boost::asio::deadline_timer timer(io_service, interval);
void tick(const boost::system::error_code& /*e*/) {
std::cout << "tick" << std::endl;
// Reschedule the timer for 1 second in the future:
timer.expires_at(timer.expires_at() + interval);
// Posts the timer event
timer.async_wait(tick);
}
int main(void) {
// Schedule the timer for the first time:
timer.async_wait(tick);
// Enter IO loop. The timer will fire for the first time 1 second from now:
io_service.run();
return 0;
}
Notice that it is very important to call expires_at() to set a new expiration time, otherwise the timer will fire immediately because it's current due time already expired.
The second example on Boosts Asio tutorials explains it.
You can find it here.
After that, check the 3rd example to see how you can call it again with a periodic time intervall
To further expand on this simple example. It will block the execution as was said in the comments, so if you want more io_services running, you should run them in a thread like so...
boost::asio::io_service io_service;
boost::asio::io_service service2;
timer.async_wait(tick);
boost::thread_group threads;
threads.create_thread(boost::bind(&boost::asio::io_service::run, &io_service));
service2.run();
threads.join_all();
As I had some issues with prior answers, here is my example:
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <iostream>
void print(const boost::system::error_code&, boost::asio::deadline_timer* t,int* count)
{
if (*count < 5)
{
std::cout << *count << std::endl;
++(*count);
t->expires_from_now(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 << std::endl;
return 0;
}
it did what it supposed to do: counting to five. May it help someone.
A Boost Asio add-on class that encapsulates this functionality (call a specfied function every N milliseconds): https://github.com/mikehaben69/boost/tree/main/asio
The repo includes a demo source file and makefile.