user3115083
user3115083

Reputation: 11

Boost Timer in Qt Application

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

Answers (1)

sehe
sehe

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:

  • no useless use of shared pointer for the time thread
  • make the CCTimer non-copyable
  • no more m_isRunning kludge; instead, intentionally cancel the timer and detect cancellation so you can end the thread. If the thread is joinable(), it's running
  • fixed 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

  • are you running under a debugger, profiler, whatnot?
  • are you running under a (overwhelmed) virtualizer?
  • are you doing more on the same io_service/thread?
  • can IO be blocking? In particular do you log the timestamps to an actual console or to a file/pipe? Could the output pipe be blocking the timer thread?

Upvotes: 0

Related Questions