Reputation: 11
I'm trying to create qt application that uses a timer from boost, I have a problem with the following code Compiled on osx, qt 5.2, boost 1.55
This is my code:
CCTimer::CCTimer(int interval)
: m_pingTimer(m_ioServiceTimer, boost::posix_time::seconds(interval)), m_interval(interval)
{
m_isRunning = false;
}
CCTimer::~CCTimer()
{
Stop();
}
void CCTimer::Start()
{
if(!m_isRunning)
{
m_isRunning = true;
Reset(m_interval);
m_timerThread = boost::shared_ptr<boost::thread>(new boost::thread(boost::bind(&boost::asio::io_service::run, &m_ioServiceTimer)));
}
}
void CCTimer::Reset(int durationTime)
{
m_beforeTime = boost::posix_time::microsec_clock::universal_time();
m_pingTimer.expires_from_now(boost::posix_time::seconds(durationTime));
m_pingTimer.async_wait(boost::bind(&CCTimer::Wait, this, _1));
}
void CCTimer::Wait(const boost::system::error_code& errorCode)
{
boost::posix_time::time_duration duration = boost::posix_time::microsec_clock::universal_time() - m_beforeTime;
std::cout << boost::posix_time::microsec_clock::universal_time() << std::endl;
if(duration.seconds() > m_interval) {
std::cout << "Error time " << std::endl;
}
Reset(m_interval);
}
void CCTimer::Stop()
{
if(m_isRunning)
{
m_isRunning = false;
m_pingTimer.cancel();
m_ioServiceTimer.stop();
m_timerThread->join();
m_ioServiceTimer.reset();
}
}
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
CCTimer timer(1);
timer.Start();
return app.exec();
}
After creation of the timer in qt application, console shows:
2014-Mar-26 22:04:30.549722
2014-Mar-26 22:04:31.550977
2014-Mar-26 22:04:32.552229
2014-Mar-26 22:04:33.553467
2014-Mar-26 22:04:34.554734
2014-Mar-26 22:04:43.684300
Error time
2014-Mar-26 22:04:54.694440
Error time
2014-Mar-26 22:05:05.694371
Error time
2014-Mar-26 22:05:11.669329
Error time
what can be wrong ?
Upvotes: 1
Views: 374
Reputation: 393769
I haven't spotted anything glaring. Though, of course, you never mention whether/when/where you class Stop()
and Start()
.
I made the implementation marginally simpler and fixed a few potential races:
m_isRunning
kludge; instead, intentionally cancel the timer and detect cancellation so you can end the thread. If the thread is joinable()
, it's runningfixed a race condition where you cancel the timer from the thread that calls Stop()
, whereas the timer is being async-awaited by the io_service thread, byt posting the cancel to the io_service thread too:
void Stop()
{
if(m_thread.joinable())
{
m_service.post(boost::bind(&deadline_timer::cancel, &m_timer));
m_thread.join();
m_service.stop();
m_service.reset();
}
}
fixed a cosmetic thing that could have led to confusion where you use a different timestamp for printing than you do for calculating the elapsed time
Update
So I downloaded Qt5 and goaded my compiler to use it. /GREAT SUCCESS/. And indeed, I got the same behaviour (but far worse, sometimes). The strange thing was it blocked more on the terminal than the eventual output would show. This made me suspect it's maybe Qt that's intercepting/reopening the stdin/stdout streams for it's own purposes and adding some blocking/buffering behaviour in the process.
I checked this by writing the timer trace to a separate file instead, and indeed this removed the symptoms (when left running for over hald an hour). So this is likely the cause of your issue. Just don't write to stdout from your timer thread and your spurious blocking issue should be gone.
Here's the code I used to test this, with the above suggestions incorporated:
#include <boost/date_time/posix_time/posix_time_io.hpp>
#include <boost/asio.hpp>
#include <boost/thread.hpp>
#include <boost/shared_ptr.hpp>
#include <QtGui/QGuiApplication>
#include <QtCore/QCoreApplication>
#include <fstream>
using namespace boost::asio;
namespace pt = boost::posix_time;
class CCTimer : boost::noncopyable
{
io_service m_service;
deadline_timer m_timer;
pt::ptime m_starttime;
int m_interval_seconds;
boost::thread m_thread;
std::ofstream ofs;
public:
CCTimer(int interval)
: m_service(),
m_timer(m_service),
m_interval_seconds(interval)
{
assert(m_interval_seconds>0);
}
~CCTimer()
{
Stop();
}
void Start()
{
if(!m_thread.joinable())
{
ofs.open("output.log");
Reset(m_interval_seconds);
m_thread = boost::thread(boost::bind(&io_service::run, &m_service));
}
}
void Stop()
{
if(m_thread.joinable())
{
m_service.post(boost::bind(&deadline_timer::cancel, &m_timer));
m_thread.join();
ofs.close();
m_service.stop();
m_service.reset();
}
}
private:
static pt::ptime now() { return pt::microsec_clock::universal_time(); }
void Reset(int durationTime)
{
m_timer.expires_from_now(pt::seconds(durationTime));
m_starttime = now();
m_timer.async_wait(boost::bind(&CCTimer::Elapsed, this, placeholders::error));
}
void Elapsed(const boost::system::error_code& errorCode)
{
pt::ptime const event = now();
pt::time_duration const elapsed = event - m_starttime;
if (errorCode != error::operation_aborted && ofs.is_open() && ofs.good())
{
ofs << event << " (" << elapsed.total_milliseconds() << "ms) ";
if(elapsed.seconds() > m_interval_seconds) {
ofs << " Overdue!" << std::endl;
} else {
ofs << "" << std::endl;
}
Reset(m_interval_seconds);
} else
{
ofs << "Stopped (" << elapsed.total_milliseconds() << "ms)" << std::endl;
}
}
};
int main(int argc, char** argv)
{
QGuiApplication app(argc, argv);
CCTimer timer(1);
timer.Start();
return app.exec();
}
Old suggestions:
I'd have to assume you're doing something in the rest of the code that inhibits the CCTimer thread
io_service
/thread?Upvotes: 0