2011-10-25 07:51:19 +00:00
|
|
|
/****************************************************************************
|
|
|
|
**
|
2015-01-28 08:44:43 +00:00
|
|
|
** Copyright (C) 2015 The Qt Company Ltd.
|
|
|
|
** Contact: http://www.qt.io/licensing/
|
2011-10-25 07:51:19 +00:00
|
|
|
**
|
|
|
|
** This file is part of the QtCore module of the Qt Toolkit.
|
|
|
|
**
|
2014-08-21 13:51:22 +00:00
|
|
|
** $QT_BEGIN_LICENSE:LGPL21$
|
2012-09-19 12:28:29 +00:00
|
|
|
** Commercial License Usage
|
|
|
|
** Licensees holding valid commercial Qt licenses may use this file in
|
|
|
|
** accordance with the commercial license agreement provided with the
|
|
|
|
** Software or, alternatively, in accordance with the terms contained in
|
2015-01-28 08:44:43 +00:00
|
|
|
** a written agreement between you and The Qt Company. For licensing terms
|
|
|
|
** and conditions see http://www.qt.io/terms-conditions. For further
|
|
|
|
** information use the contact form at http://www.qt.io/contact-us.
|
2012-09-19 12:28:29 +00:00
|
|
|
**
|
2011-10-25 07:51:19 +00:00
|
|
|
** GNU Lesser General Public License Usage
|
2012-09-19 12:28:29 +00:00
|
|
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
2014-08-21 13:51:22 +00:00
|
|
|
** General Public License version 2.1 or version 3 as published by the Free
|
|
|
|
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
|
|
|
|
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
|
|
|
|
** following information to ensure the GNU Lesser General Public License
|
|
|
|
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
|
|
|
|
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
2012-09-19 12:28:29 +00:00
|
|
|
**
|
2015-01-28 08:44:43 +00:00
|
|
|
** As a special exception, The Qt Company gives you certain additional
|
|
|
|
** rights. These rights are described in The Qt Company LGPL Exception
|
2011-10-25 07:51:19 +00:00
|
|
|
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
|
|
|
**
|
|
|
|
** $QT_END_LICENSE$
|
|
|
|
**
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
#include <qelapsedtimer.h>
|
|
|
|
#include <qcoreapplication.h>
|
|
|
|
|
|
|
|
#include "private/qcore_unix_p.h"
|
|
|
|
#include "private/qtimerinfo_unix_p.h"
|
|
|
|
#include "private/qobject_p.h"
|
|
|
|
#include "private/qabstracteventdispatcher_p.h"
|
|
|
|
|
2011-12-21 10:34:07 +00:00
|
|
|
#ifdef QTIMERINFO_DEBUG
|
|
|
|
# include <QDebug>
|
|
|
|
# include <QThread>
|
|
|
|
#endif
|
|
|
|
|
2011-10-25 07:51:19 +00:00
|
|
|
#include <sys/times.h>
|
|
|
|
|
|
|
|
QT_BEGIN_NAMESPACE
|
|
|
|
|
|
|
|
Q_CORE_EXPORT bool qt_disable_lowpriority_timers=false;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Internal functions for manipulating timer data structures. The
|
|
|
|
* timerBitVec array is used for keeping track of timer identifiers.
|
|
|
|
*/
|
|
|
|
|
|
|
|
QTimerInfoList::QTimerInfoList()
|
|
|
|
{
|
|
|
|
#if (_POSIX_MONOTONIC_CLOCK-0 <= 0) && !defined(Q_OS_MAC) && !defined(Q_OS_NACL)
|
|
|
|
if (!QElapsedTimer::isMonotonic()) {
|
|
|
|
// not using monotonic timers, initialize the timeChanged() machinery
|
|
|
|
previousTime = qt_gettime();
|
|
|
|
|
|
|
|
tms unused;
|
|
|
|
previousTicks = times(&unused);
|
|
|
|
|
|
|
|
ticksPerSecond = sysconf(_SC_CLK_TCK);
|
|
|
|
msPerTick = 1000/ticksPerSecond;
|
|
|
|
} else {
|
|
|
|
// detected monotonic timers
|
2012-08-10 14:14:48 +00:00
|
|
|
previousTime.tv_sec = previousTime.tv_nsec = 0;
|
2011-10-25 07:51:19 +00:00
|
|
|
previousTicks = 0;
|
|
|
|
ticksPerSecond = 0;
|
|
|
|
msPerTick = 0;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
firstTimerInfo = 0;
|
|
|
|
}
|
|
|
|
|
2012-08-10 14:14:48 +00:00
|
|
|
timespec QTimerInfoList::updateCurrentTime()
|
2011-10-25 07:51:19 +00:00
|
|
|
{
|
|
|
|
return (currentTime = qt_gettime());
|
|
|
|
}
|
|
|
|
|
|
|
|
#if ((_POSIX_MONOTONIC_CLOCK-0 <= 0) && !defined(Q_OS_MAC) && !defined(Q_OS_INTEGRITY)) || defined(QT_BOOTSTRAPPED)
|
|
|
|
|
2013-03-05 12:42:11 +00:00
|
|
|
timespec qAbsTimespec(const timespec &t)
|
2011-10-25 07:51:19 +00:00
|
|
|
{
|
2012-08-10 14:14:48 +00:00
|
|
|
timespec tmp = t;
|
2011-10-25 07:51:19 +00:00
|
|
|
if (tmp.tv_sec < 0) {
|
|
|
|
tmp.tv_sec = -tmp.tv_sec - 1;
|
2012-08-10 14:14:48 +00:00
|
|
|
tmp.tv_nsec -= 1000000000;
|
2011-10-25 07:51:19 +00:00
|
|
|
}
|
2012-08-10 14:14:48 +00:00
|
|
|
if (tmp.tv_sec == 0 && tmp.tv_nsec < 0) {
|
|
|
|
tmp.tv_nsec = -tmp.tv_nsec;
|
2011-10-25 07:51:19 +00:00
|
|
|
}
|
2012-08-10 14:14:48 +00:00
|
|
|
return normalizedTimespec(tmp);
|
2011-10-25 07:51:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2013-10-02 14:51:05 +00:00
|
|
|
Returns \c true if the real time clock has changed by more than 10%
|
2011-10-25 07:51:19 +00:00
|
|
|
relative to the processor time since the last time this function was
|
|
|
|
called. This presumably means that the system time has been changed.
|
|
|
|
|
|
|
|
If /a delta is nonzero, delta is set to our best guess at how much the system clock was changed.
|
|
|
|
*/
|
2012-08-10 14:14:48 +00:00
|
|
|
bool QTimerInfoList::timeChanged(timespec *delta)
|
2011-10-25 07:51:19 +00:00
|
|
|
{
|
|
|
|
#ifdef Q_OS_NACL
|
|
|
|
Q_UNUSED(delta)
|
|
|
|
return false; // Calling "times" crashes.
|
|
|
|
#endif
|
|
|
|
struct tms unused;
|
|
|
|
clock_t currentTicks = times(&unused);
|
|
|
|
|
|
|
|
clock_t elapsedTicks = currentTicks - previousTicks;
|
2012-08-10 14:14:48 +00:00
|
|
|
timespec elapsedTime = currentTime - previousTime;
|
2011-10-25 07:51:19 +00:00
|
|
|
|
2012-08-10 14:14:48 +00:00
|
|
|
timespec elapsedTimeTicks;
|
2011-10-25 07:51:19 +00:00
|
|
|
elapsedTimeTicks.tv_sec = elapsedTicks / ticksPerSecond;
|
2012-08-10 14:14:48 +00:00
|
|
|
elapsedTimeTicks.tv_nsec = (((elapsedTicks * 1000) / ticksPerSecond) % 1000) * 1000 * 1000;
|
2011-10-25 07:51:19 +00:00
|
|
|
|
2012-08-10 14:14:48 +00:00
|
|
|
timespec dummy;
|
2011-10-25 07:51:19 +00:00
|
|
|
if (!delta)
|
|
|
|
delta = &dummy;
|
|
|
|
*delta = elapsedTime - elapsedTimeTicks;
|
|
|
|
|
|
|
|
previousTicks = currentTicks;
|
|
|
|
previousTime = currentTime;
|
|
|
|
|
|
|
|
// If tick drift is more than 10% off compared to realtime, we assume that the clock has
|
|
|
|
// been set. Of course, we have to allow for the tick granularity as well.
|
2012-08-10 14:14:48 +00:00
|
|
|
timespec tickGranularity;
|
2011-10-25 07:51:19 +00:00
|
|
|
tickGranularity.tv_sec = 0;
|
2012-08-10 14:14:48 +00:00
|
|
|
tickGranularity.tv_nsec = msPerTick * 1000 * 1000;
|
2013-03-05 12:42:11 +00:00
|
|
|
return elapsedTimeTicks < ((qAbsTimespec(*delta) - tickGranularity) * 10);
|
2011-10-25 07:51:19 +00:00
|
|
|
}
|
|
|
|
|
2012-01-05 12:45:36 +00:00
|
|
|
/*
|
|
|
|
repair broken timer
|
|
|
|
*/
|
2012-08-10 14:14:48 +00:00
|
|
|
void QTimerInfoList::timerRepair(const timespec &diff)
|
2012-01-05 12:45:36 +00:00
|
|
|
{
|
|
|
|
// repair all timers
|
|
|
|
for (int i = 0; i < size(); ++i) {
|
2013-06-15 09:49:45 +00:00
|
|
|
QTimerInfo *t = at(i);
|
2012-01-05 12:45:36 +00:00
|
|
|
t->timeout = t->timeout + diff;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-10-25 07:51:19 +00:00
|
|
|
void QTimerInfoList::repairTimersIfNeeded()
|
|
|
|
{
|
|
|
|
if (QElapsedTimer::isMonotonic())
|
|
|
|
return;
|
2012-08-10 14:14:48 +00:00
|
|
|
timespec delta;
|
2011-10-25 07:51:19 +00:00
|
|
|
if (timeChanged(&delta))
|
|
|
|
timerRepair(delta);
|
|
|
|
}
|
|
|
|
|
|
|
|
#else // !(_POSIX_MONOTONIC_CLOCK-0 <= 0) && !defined(QT_BOOTSTRAPPED)
|
|
|
|
|
|
|
|
void QTimerInfoList::repairTimersIfNeeded()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/*
|
|
|
|
insert timer info into list
|
|
|
|
*/
|
|
|
|
void QTimerInfoList::timerInsert(QTimerInfo *ti)
|
|
|
|
{
|
|
|
|
int index = size();
|
|
|
|
while (index--) {
|
2013-06-15 09:49:45 +00:00
|
|
|
const QTimerInfo * const t = at(index);
|
2011-10-25 07:51:19 +00:00
|
|
|
if (!(ti->timeout < t->timeout))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
insert(index+1, ti);
|
|
|
|
}
|
|
|
|
|
2012-08-10 14:14:48 +00:00
|
|
|
inline timespec &operator+=(timespec &t1, int ms)
|
2011-12-21 10:33:56 +00:00
|
|
|
{
|
|
|
|
t1.tv_sec += ms / 1000;
|
2012-08-10 14:14:48 +00:00
|
|
|
t1.tv_nsec += ms % 1000 * 1000 * 1000;
|
|
|
|
return normalizedTimespec(t1);
|
2011-12-21 10:33:56 +00:00
|
|
|
}
|
|
|
|
|
2012-08-10 14:14:48 +00:00
|
|
|
inline timespec operator+(const timespec &t1, int ms)
|
2011-12-21 10:33:56 +00:00
|
|
|
{
|
2012-08-10 14:14:48 +00:00
|
|
|
timespec t2 = t1;
|
2011-12-21 10:33:56 +00:00
|
|
|
return t2 += ms;
|
|
|
|
}
|
|
|
|
|
2012-08-10 14:14:48 +00:00
|
|
|
static timespec roundToMillisecond(timespec val)
|
2011-10-25 07:51:19 +00:00
|
|
|
{
|
|
|
|
// always round up
|
|
|
|
// worst case scenario is that the first trigger of a 1-ms timer is 0.999 ms late
|
|
|
|
|
2012-08-10 14:14:48 +00:00
|
|
|
int ns = val.tv_nsec % (1000 * 1000);
|
|
|
|
val.tv_nsec += 1000 * 1000 - ns;
|
|
|
|
return normalizedTimespec(val);
|
2011-10-25 07:51:19 +00:00
|
|
|
}
|
|
|
|
|
2011-12-21 10:34:07 +00:00
|
|
|
#ifdef QTIMERINFO_DEBUG
|
|
|
|
QDebug operator<<(QDebug s, timeval tv)
|
|
|
|
{
|
2015-01-23 15:19:11 +00:00
|
|
|
QDebugStateSaver saver(s);
|
2011-12-21 10:34:07 +00:00
|
|
|
s.nospace() << tv.tv_sec << "." << qSetFieldWidth(6) << qSetPadChar(QChar(48)) << tv.tv_usec << reset;
|
2015-01-23 15:19:11 +00:00
|
|
|
return s;
|
2011-12-21 10:34:07 +00:00
|
|
|
}
|
|
|
|
QDebug operator<<(QDebug s, Qt::TimerType t)
|
|
|
|
{
|
2015-01-23 15:19:11 +00:00
|
|
|
QDebugStateSaver saver(s);
|
2011-12-21 10:34:07 +00:00
|
|
|
s << (t == Qt::PreciseTimer ? "P" :
|
|
|
|
t == Qt::CoarseTimer ? "C" : "VC");
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2012-08-10 14:14:48 +00:00
|
|
|
static void calculateCoarseTimerTimeout(QTimerInfo *t, timespec currentTime)
|
2011-12-21 10:34:07 +00:00
|
|
|
{
|
|
|
|
// The coarse timer works like this:
|
|
|
|
// - interval under 40 ms: round to even
|
|
|
|
// - between 40 and 99 ms: round to multiple of 4
|
|
|
|
// - otherwise: try to wake up at a multiple of 25 ms, with a maximum error of 5%
|
|
|
|
//
|
|
|
|
// We try to wake up at the following second-fraction, in order of preference:
|
|
|
|
// 0 ms
|
|
|
|
// 500 ms
|
|
|
|
// 250 ms or 750 ms
|
|
|
|
// 200, 400, 600, 800 ms
|
|
|
|
// other multiples of 100
|
|
|
|
// other multiples of 50
|
|
|
|
// other multiples of 25
|
|
|
|
//
|
|
|
|
// The objective is to make most timers wake up at the same time, thereby reducing CPU wakeups.
|
|
|
|
|
2013-06-15 09:49:45 +00:00
|
|
|
uint interval = uint(t->interval);
|
|
|
|
uint msec = uint(t->timeout.tv_nsec) / 1000 / 1000;
|
2011-12-21 10:34:07 +00:00
|
|
|
Q_ASSERT(interval >= 20);
|
|
|
|
|
|
|
|
// Calculate how much we can round and still keep within 5% error
|
|
|
|
uint absMaxRounding = interval / 20;
|
|
|
|
|
|
|
|
if (interval < 100 && interval != 25 && interval != 50 && interval != 75) {
|
|
|
|
// special mode for timers of less than 100 ms
|
|
|
|
if (interval < 50) {
|
|
|
|
// round to even
|
|
|
|
// round towards multiples of 50 ms
|
2013-06-15 09:49:45 +00:00
|
|
|
bool roundUp = (msec % 50) >= 25;
|
2011-12-21 10:34:07 +00:00
|
|
|
msec >>= 1;
|
|
|
|
msec |= uint(roundUp);
|
|
|
|
msec <<= 1;
|
|
|
|
} else {
|
|
|
|
// round to multiple of 4
|
|
|
|
// round towards multiples of 100 ms
|
2013-06-15 09:49:45 +00:00
|
|
|
bool roundUp = (msec % 100) >= 50;
|
2011-12-21 10:34:07 +00:00
|
|
|
msec >>= 2;
|
|
|
|
msec |= uint(roundUp);
|
|
|
|
msec <<= 2;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
uint min = qMax<int>(0, msec - absMaxRounding);
|
|
|
|
uint max = qMin(1000u, msec + absMaxRounding);
|
|
|
|
|
|
|
|
// find the boundary that we want, according to the rules above
|
|
|
|
// extra rules:
|
|
|
|
// 1) whatever the interval, we'll take any round-to-the-second timeout
|
|
|
|
if (min == 0) {
|
|
|
|
msec = 0;
|
|
|
|
goto recalculate;
|
|
|
|
} else if (max == 1000) {
|
|
|
|
msec = 1000;
|
|
|
|
goto recalculate;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint wantedBoundaryMultiple;
|
|
|
|
|
|
|
|
// 2) if the interval is a multiple of 500 ms and > 5000 ms, we'll always round
|
|
|
|
// towards a round-to-the-second
|
|
|
|
// 3) if the interval is a multiple of 500 ms, we'll round towards the nearest
|
|
|
|
// multiple of 500 ms
|
|
|
|
if ((interval % 500) == 0) {
|
|
|
|
if (interval >= 5000) {
|
|
|
|
msec = msec >= 500 ? max : min;
|
|
|
|
goto recalculate;
|
|
|
|
} else {
|
|
|
|
wantedBoundaryMultiple = 500;
|
|
|
|
}
|
|
|
|
} else if ((interval % 50) == 0) {
|
|
|
|
// 4) same for multiples of 250, 200, 100, 50
|
|
|
|
uint mult50 = interval / 50;
|
|
|
|
if ((mult50 % 4) == 0) {
|
|
|
|
// multiple of 200
|
|
|
|
wantedBoundaryMultiple = 200;
|
|
|
|
} else if ((mult50 % 2) == 0) {
|
|
|
|
// multiple of 100
|
|
|
|
wantedBoundaryMultiple = 100;
|
|
|
|
} else if ((mult50 % 5) == 0) {
|
|
|
|
// multiple of 250
|
|
|
|
wantedBoundaryMultiple = 250;
|
|
|
|
} else {
|
|
|
|
// multiple of 50
|
|
|
|
wantedBoundaryMultiple = 50;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
wantedBoundaryMultiple = 25;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint base = msec / wantedBoundaryMultiple * wantedBoundaryMultiple;
|
|
|
|
uint middlepoint = base + wantedBoundaryMultiple / 2;
|
|
|
|
if (msec < middlepoint)
|
|
|
|
msec = qMax(base, min);
|
|
|
|
else
|
|
|
|
msec = qMin(base + wantedBoundaryMultiple, max);
|
|
|
|
}
|
|
|
|
|
|
|
|
recalculate:
|
|
|
|
if (msec == 1000u) {
|
|
|
|
++t->timeout.tv_sec;
|
2012-08-10 14:14:48 +00:00
|
|
|
t->timeout.tv_nsec = 0;
|
2011-12-21 10:34:07 +00:00
|
|
|
} else {
|
2012-08-10 14:14:48 +00:00
|
|
|
t->timeout.tv_nsec = msec * 1000 * 1000;
|
2011-12-21 10:34:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (t->timeout < currentTime)
|
|
|
|
t->timeout += interval;
|
|
|
|
}
|
|
|
|
|
2012-08-10 14:14:48 +00:00
|
|
|
static void calculateNextTimeout(QTimerInfo *t, timespec currentTime)
|
2011-12-21 10:34:07 +00:00
|
|
|
{
|
|
|
|
switch (t->timerType) {
|
|
|
|
case Qt::PreciseTimer:
|
|
|
|
case Qt::CoarseTimer:
|
|
|
|
t->timeout += t->interval;
|
|
|
|
if (t->timeout < currentTime) {
|
|
|
|
t->timeout = currentTime;
|
|
|
|
t->timeout += t->interval;
|
|
|
|
}
|
2012-01-05 12:56:01 +00:00
|
|
|
#ifdef QTIMERINFO_DEBUG
|
|
|
|
t->expected += t->interval;
|
|
|
|
if (t->expected < currentTime) {
|
|
|
|
t->expected = currentTime;
|
|
|
|
t->expected += t->interval;
|
|
|
|
}
|
|
|
|
#endif
|
2011-12-21 10:34:07 +00:00
|
|
|
if (t->timerType == Qt::CoarseTimer)
|
|
|
|
calculateCoarseTimerTimeout(t, currentTime);
|
|
|
|
return;
|
|
|
|
|
|
|
|
case Qt::VeryCoarseTimer:
|
|
|
|
// we don't need to take care of the microsecond component of t->interval
|
|
|
|
t->timeout.tv_sec += t->interval;
|
|
|
|
if (t->timeout.tv_sec <= currentTime.tv_sec)
|
|
|
|
t->timeout.tv_sec = currentTime.tv_sec + t->interval;
|
2012-01-05 12:56:01 +00:00
|
|
|
#ifdef QTIMERINFO_DEBUG
|
2011-12-21 10:34:07 +00:00
|
|
|
t->expected.tv_sec += t->interval;
|
2012-01-05 12:56:01 +00:00
|
|
|
if (t->expected.tv_sec <= currentTime.tv_sec)
|
|
|
|
t->expected.tv_sec = currentTime.tv_sec + t->interval;
|
|
|
|
#endif
|
2011-12-21 10:34:07 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef QTIMERINFO_DEBUG
|
|
|
|
if (t->timerType != Qt::PreciseTimer)
|
|
|
|
qDebug() << "timer" << t->timerType << hex << t->id << dec << "interval" << t->interval
|
|
|
|
<< "originally expected at" << t->expected << "will fire at" << t->timeout
|
|
|
|
<< "or" << (t->timeout - t->expected) << "s late";
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2011-10-25 07:51:19 +00:00
|
|
|
/*
|
|
|
|
Returns the time to wait for the next timer, or null if no timers
|
|
|
|
are waiting.
|
|
|
|
*/
|
2012-08-10 14:14:48 +00:00
|
|
|
bool QTimerInfoList::timerWait(timespec &tm)
|
2011-10-25 07:51:19 +00:00
|
|
|
{
|
2012-08-10 14:14:48 +00:00
|
|
|
timespec currentTime = updateCurrentTime();
|
2011-10-25 07:51:19 +00:00
|
|
|
repairTimersIfNeeded();
|
|
|
|
|
|
|
|
// Find first waiting timer not already active
|
|
|
|
QTimerInfo *t = 0;
|
|
|
|
for (QTimerInfoList::const_iterator it = constBegin(); it != constEnd(); ++it) {
|
|
|
|
if (!(*it)->activateRef) {
|
|
|
|
t = *it;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!t)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (currentTime < t->timeout) {
|
|
|
|
// time to wait
|
|
|
|
tm = roundToMillisecond(t->timeout - currentTime);
|
|
|
|
} else {
|
|
|
|
// no time to wait
|
|
|
|
tm.tv_sec = 0;
|
2012-08-10 14:14:48 +00:00
|
|
|
tm.tv_nsec = 0;
|
2011-10-25 07:51:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
Add a remainingTime() method to the public interface of the QTimer class
It is an extension coming from the use case when you, for instance, need to
implement a countdown timer in client codes, and manually maintain a dedicated
variable for counting down with the help of yet another Timer. There might be
other use cases as well. The returned value is meant to be in milliseconds, as
the method documentation says, since it is reasonable, and consistent with the
rest (ie. the interval accessor).
The elapsed time is already being tracked inside the event dispatcher, thus the
effort is only exposing that for all platforms supported according to the
desired timer identifier, and propagating up to the QTimer public API. It is
done by using the QTimerInfoList class in the glib and unix dispatchers, and the
WinTimeInfo struct for the windows dispatcher.
It might be a good idea to to establish a QWinTimerInfo
(qtimerinfo_win{_p.h,cpp}) in the future for resembling the interface for
windows with the glib/unix management so that it would be consistent. That would
mean abstracting out a base class (~interface) for the timer info classes.
Something like that QAbstractTimerInfo.
Test: Build test only on (Arch)Linux, Windows and Mac. I have also run the unit
tests and they passed as well.
Change-Id: Ie37b3aff909313ebc92e511e27d029abb070f110
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
Reviewed-by: Bradley T. Hughes <bradley.hughes@nokia.com>
2012-02-23 05:41:30 +00:00
|
|
|
/*
|
|
|
|
Returns the timer's remaining time in milliseconds with the given timerId, or
|
|
|
|
null if there is nothing left. If the timer id is not found in the list, the
|
|
|
|
returned value will be -1. If the timer is overdue, the returned value will be 0.
|
|
|
|
*/
|
|
|
|
int QTimerInfoList::timerRemainingTime(int timerId)
|
|
|
|
{
|
2012-08-10 14:14:48 +00:00
|
|
|
timespec currentTime = updateCurrentTime();
|
Add a remainingTime() method to the public interface of the QTimer class
It is an extension coming from the use case when you, for instance, need to
implement a countdown timer in client codes, and manually maintain a dedicated
variable for counting down with the help of yet another Timer. There might be
other use cases as well. The returned value is meant to be in milliseconds, as
the method documentation says, since it is reasonable, and consistent with the
rest (ie. the interval accessor).
The elapsed time is already being tracked inside the event dispatcher, thus the
effort is only exposing that for all platforms supported according to the
desired timer identifier, and propagating up to the QTimer public API. It is
done by using the QTimerInfoList class in the glib and unix dispatchers, and the
WinTimeInfo struct for the windows dispatcher.
It might be a good idea to to establish a QWinTimerInfo
(qtimerinfo_win{_p.h,cpp}) in the future for resembling the interface for
windows with the glib/unix management so that it would be consistent. That would
mean abstracting out a base class (~interface) for the timer info classes.
Something like that QAbstractTimerInfo.
Test: Build test only on (Arch)Linux, Windows and Mac. I have also run the unit
tests and they passed as well.
Change-Id: Ie37b3aff909313ebc92e511e27d029abb070f110
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
Reviewed-by: Bradley T. Hughes <bradley.hughes@nokia.com>
2012-02-23 05:41:30 +00:00
|
|
|
repairTimersIfNeeded();
|
2012-08-10 14:14:48 +00:00
|
|
|
timespec tm = {0, 0};
|
Add a remainingTime() method to the public interface of the QTimer class
It is an extension coming from the use case when you, for instance, need to
implement a countdown timer in client codes, and manually maintain a dedicated
variable for counting down with the help of yet another Timer. There might be
other use cases as well. The returned value is meant to be in milliseconds, as
the method documentation says, since it is reasonable, and consistent with the
rest (ie. the interval accessor).
The elapsed time is already being tracked inside the event dispatcher, thus the
effort is only exposing that for all platforms supported according to the
desired timer identifier, and propagating up to the QTimer public API. It is
done by using the QTimerInfoList class in the glib and unix dispatchers, and the
WinTimeInfo struct for the windows dispatcher.
It might be a good idea to to establish a QWinTimerInfo
(qtimerinfo_win{_p.h,cpp}) in the future for resembling the interface for
windows with the glib/unix management so that it would be consistent. That would
mean abstracting out a base class (~interface) for the timer info classes.
Something like that QAbstractTimerInfo.
Test: Build test only on (Arch)Linux, Windows and Mac. I have also run the unit
tests and they passed as well.
Change-Id: Ie37b3aff909313ebc92e511e27d029abb070f110
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
Reviewed-by: Bradley T. Hughes <bradley.hughes@nokia.com>
2012-02-23 05:41:30 +00:00
|
|
|
|
|
|
|
for (int i = 0; i < count(); ++i) {
|
2013-06-15 09:49:45 +00:00
|
|
|
QTimerInfo *t = at(i);
|
Add a remainingTime() method to the public interface of the QTimer class
It is an extension coming from the use case when you, for instance, need to
implement a countdown timer in client codes, and manually maintain a dedicated
variable for counting down with the help of yet another Timer. There might be
other use cases as well. The returned value is meant to be in milliseconds, as
the method documentation says, since it is reasonable, and consistent with the
rest (ie. the interval accessor).
The elapsed time is already being tracked inside the event dispatcher, thus the
effort is only exposing that for all platforms supported according to the
desired timer identifier, and propagating up to the QTimer public API. It is
done by using the QTimerInfoList class in the glib and unix dispatchers, and the
WinTimeInfo struct for the windows dispatcher.
It might be a good idea to to establish a QWinTimerInfo
(qtimerinfo_win{_p.h,cpp}) in the future for resembling the interface for
windows with the glib/unix management so that it would be consistent. That would
mean abstracting out a base class (~interface) for the timer info classes.
Something like that QAbstractTimerInfo.
Test: Build test only on (Arch)Linux, Windows and Mac. I have also run the unit
tests and they passed as well.
Change-Id: Ie37b3aff909313ebc92e511e27d029abb070f110
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
Reviewed-by: Bradley T. Hughes <bradley.hughes@nokia.com>
2012-02-23 05:41:30 +00:00
|
|
|
if (t->id == timerId) {
|
|
|
|
if (currentTime < t->timeout) {
|
|
|
|
// time to wait
|
|
|
|
tm = roundToMillisecond(t->timeout - currentTime);
|
2012-08-10 14:14:48 +00:00
|
|
|
return tm.tv_sec*1000 + tm.tv_nsec/1000/1000;
|
Add a remainingTime() method to the public interface of the QTimer class
It is an extension coming from the use case when you, for instance, need to
implement a countdown timer in client codes, and manually maintain a dedicated
variable for counting down with the help of yet another Timer. There might be
other use cases as well. The returned value is meant to be in milliseconds, as
the method documentation says, since it is reasonable, and consistent with the
rest (ie. the interval accessor).
The elapsed time is already being tracked inside the event dispatcher, thus the
effort is only exposing that for all platforms supported according to the
desired timer identifier, and propagating up to the QTimer public API. It is
done by using the QTimerInfoList class in the glib and unix dispatchers, and the
WinTimeInfo struct for the windows dispatcher.
It might be a good idea to to establish a QWinTimerInfo
(qtimerinfo_win{_p.h,cpp}) in the future for resembling the interface for
windows with the glib/unix management so that it would be consistent. That would
mean abstracting out a base class (~interface) for the timer info classes.
Something like that QAbstractTimerInfo.
Test: Build test only on (Arch)Linux, Windows and Mac. I have also run the unit
tests and they passed as well.
Change-Id: Ie37b3aff909313ebc92e511e27d029abb070f110
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
Reviewed-by: Bradley T. Hughes <bradley.hughes@nokia.com>
2012-02-23 05:41:30 +00:00
|
|
|
} else {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifndef QT_NO_DEBUG
|
|
|
|
qWarning("QTimerInfoList::timerRemainingTime: timer id %i not found", timerId);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2011-12-21 10:33:33 +00:00
|
|
|
void QTimerInfoList::registerTimer(int timerId, int interval, Qt::TimerType timerType, QObject *object)
|
2011-10-25 07:51:19 +00:00
|
|
|
{
|
|
|
|
QTimerInfo *t = new QTimerInfo;
|
|
|
|
t->id = timerId;
|
2011-12-21 10:33:56 +00:00
|
|
|
t->interval = interval;
|
2011-12-21 10:33:33 +00:00
|
|
|
t->timerType = timerType;
|
2011-10-25 07:51:19 +00:00
|
|
|
t->obj = object;
|
|
|
|
t->activateRef = 0;
|
|
|
|
|
2012-08-10 14:14:48 +00:00
|
|
|
timespec expected = updateCurrentTime() + interval;
|
2012-01-05 12:56:01 +00:00
|
|
|
|
2011-12-21 10:34:07 +00:00
|
|
|
switch (timerType) {
|
|
|
|
case Qt::PreciseTimer:
|
|
|
|
// high precision timer is based on millisecond precision
|
|
|
|
// so no adjustment is necessary
|
2012-01-05 12:56:01 +00:00
|
|
|
t->timeout = expected;
|
2011-12-21 10:34:07 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case Qt::CoarseTimer:
|
|
|
|
// this timer has up to 5% coarseness
|
|
|
|
// so our boundaries are 20 ms and 20 s
|
|
|
|
// below 20 ms, 5% inaccuracy is below 1 ms, so we convert to high precision
|
|
|
|
// above 20 s, 5% inaccuracy is above 1 s, so we convert to VeryCoarseTimer
|
|
|
|
if (interval >= 20000) {
|
|
|
|
t->timerType = Qt::VeryCoarseTimer;
|
|
|
|
// fall through
|
|
|
|
} else {
|
2012-01-05 12:56:01 +00:00
|
|
|
t->timeout = expected;
|
2011-12-21 10:34:07 +00:00
|
|
|
if (interval <= 20) {
|
|
|
|
t->timerType = Qt::PreciseTimer;
|
|
|
|
// no adjustment is necessary
|
|
|
|
} else if (interval <= 20000) {
|
|
|
|
calculateCoarseTimerTimeout(t, currentTime);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
// fall through
|
|
|
|
case Qt::VeryCoarseTimer:
|
|
|
|
// the very coarse timer is based on full second precision,
|
|
|
|
// so we keep the interval in seconds (round to closest second)
|
|
|
|
t->interval /= 500;
|
|
|
|
t->interval += 1;
|
|
|
|
t->interval >>= 1;
|
|
|
|
t->timeout.tv_sec = currentTime.tv_sec + t->interval;
|
2012-08-10 14:14:48 +00:00
|
|
|
t->timeout.tv_nsec = 0;
|
2011-12-21 10:34:07 +00:00
|
|
|
|
|
|
|
// if we're past the half-second mark, increase the timeout again
|
2012-08-10 14:14:48 +00:00
|
|
|
if (currentTime.tv_nsec > 500*1000*1000)
|
2011-12-21 10:34:07 +00:00
|
|
|
++t->timeout.tv_sec;
|
|
|
|
}
|
|
|
|
|
2011-10-25 07:51:19 +00:00
|
|
|
timerInsert(t);
|
2011-12-21 10:34:07 +00:00
|
|
|
|
|
|
|
#ifdef QTIMERINFO_DEBUG
|
2012-01-05 12:56:01 +00:00
|
|
|
t->expected = expected;
|
2011-12-21 10:34:07 +00:00
|
|
|
t->cumulativeError = 0;
|
|
|
|
t->count = 0;
|
|
|
|
if (t->timerType != Qt::PreciseTimer)
|
|
|
|
qDebug() << "timer" << t->timerType << hex <<t->id << dec << "interval" << t->interval << "expected at"
|
|
|
|
<< t->expected << "will fire first at" << t->timeout;
|
|
|
|
#endif
|
2011-10-25 07:51:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool QTimerInfoList::unregisterTimer(int timerId)
|
|
|
|
{
|
|
|
|
// set timer inactive
|
|
|
|
for (int i = 0; i < count(); ++i) {
|
2013-06-15 09:49:45 +00:00
|
|
|
QTimerInfo *t = at(i);
|
2011-10-25 07:51:19 +00:00
|
|
|
if (t->id == timerId) {
|
|
|
|
// found it
|
|
|
|
removeAt(i);
|
|
|
|
if (t == firstTimerInfo)
|
|
|
|
firstTimerInfo = 0;
|
|
|
|
if (t->activateRef)
|
|
|
|
*(t->activateRef) = 0;
|
|
|
|
delete t;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// id not found
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool QTimerInfoList::unregisterTimers(QObject *object)
|
|
|
|
{
|
|
|
|
if (isEmpty())
|
|
|
|
return false;
|
|
|
|
for (int i = 0; i < count(); ++i) {
|
2013-06-15 09:49:45 +00:00
|
|
|
QTimerInfo *t = at(i);
|
2011-10-25 07:51:19 +00:00
|
|
|
if (t->obj == object) {
|
|
|
|
// object found
|
|
|
|
removeAt(i);
|
|
|
|
if (t == firstTimerInfo)
|
|
|
|
firstTimerInfo = 0;
|
|
|
|
if (t->activateRef)
|
|
|
|
*(t->activateRef) = 0;
|
|
|
|
delete t;
|
|
|
|
// move back one so that we don't skip the new current item
|
|
|
|
--i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2011-12-21 10:33:33 +00:00
|
|
|
QList<QAbstractEventDispatcher::TimerInfo> QTimerInfoList::registeredTimers(QObject *object) const
|
2011-10-25 07:51:19 +00:00
|
|
|
{
|
2011-12-21 10:33:33 +00:00
|
|
|
QList<QAbstractEventDispatcher::TimerInfo> list;
|
2011-10-25 07:51:19 +00:00
|
|
|
for (int i = 0; i < count(); ++i) {
|
2013-06-15 09:49:45 +00:00
|
|
|
const QTimerInfo * const t = at(i);
|
2011-12-21 10:34:07 +00:00
|
|
|
if (t->obj == object) {
|
|
|
|
list << QAbstractEventDispatcher::TimerInfo(t->id,
|
|
|
|
(t->timerType == Qt::VeryCoarseTimer
|
|
|
|
? t->interval * 1000
|
|
|
|
: t->interval),
|
|
|
|
t->timerType);
|
|
|
|
}
|
2011-10-25 07:51:19 +00:00
|
|
|
}
|
|
|
|
return list;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
Activate pending timers, returning how many where activated.
|
|
|
|
*/
|
|
|
|
int QTimerInfoList::activateTimers()
|
|
|
|
{
|
|
|
|
if (qt_disable_lowpriority_timers || isEmpty())
|
|
|
|
return 0; // nothing to do
|
|
|
|
|
|
|
|
int n_act = 0, maxCount = 0;
|
|
|
|
firstTimerInfo = 0;
|
|
|
|
|
2012-08-10 14:14:48 +00:00
|
|
|
timespec currentTime = updateCurrentTime();
|
2011-12-21 10:34:07 +00:00
|
|
|
// qDebug() << "Thread" << QThread::currentThreadId() << "woken up at" << currentTime;
|
2011-10-25 07:51:19 +00:00
|
|
|
repairTimersIfNeeded();
|
|
|
|
|
|
|
|
|
|
|
|
// Find out how many timer have expired
|
|
|
|
for (QTimerInfoList::const_iterator it = constBegin(); it != constEnd(); ++it) {
|
|
|
|
if (currentTime < (*it)->timeout)
|
|
|
|
break;
|
|
|
|
maxCount++;
|
|
|
|
}
|
|
|
|
|
|
|
|
//fire the timers.
|
|
|
|
while (maxCount--) {
|
|
|
|
if (isEmpty())
|
|
|
|
break;
|
|
|
|
|
|
|
|
QTimerInfo *currentTimerInfo = first();
|
|
|
|
if (currentTime < currentTimerInfo->timeout)
|
|
|
|
break; // no timer has expired
|
|
|
|
|
|
|
|
if (!firstTimerInfo) {
|
|
|
|
firstTimerInfo = currentTimerInfo;
|
|
|
|
} else if (firstTimerInfo == currentTimerInfo) {
|
|
|
|
// avoid sending the same timer multiple times
|
|
|
|
break;
|
|
|
|
} else if (currentTimerInfo->interval < firstTimerInfo->interval
|
|
|
|
|| currentTimerInfo->interval == firstTimerInfo->interval) {
|
|
|
|
firstTimerInfo = currentTimerInfo;
|
|
|
|
}
|
|
|
|
|
|
|
|
// remove from list
|
|
|
|
removeFirst();
|
|
|
|
|
2011-12-21 10:34:07 +00:00
|
|
|
#ifdef QTIMERINFO_DEBUG
|
|
|
|
float diff;
|
|
|
|
if (currentTime < currentTimerInfo->expected) {
|
|
|
|
// early
|
|
|
|
timeval early = currentTimerInfo->expected - currentTime;
|
|
|
|
diff = -(early.tv_sec + early.tv_usec / 1000000.0);
|
|
|
|
} else {
|
|
|
|
timeval late = currentTime - currentTimerInfo->expected;
|
|
|
|
diff = late.tv_sec + late.tv_usec / 1000000.0;
|
|
|
|
}
|
|
|
|
currentTimerInfo->cumulativeError += diff;
|
|
|
|
++currentTimerInfo->count;
|
|
|
|
if (currentTimerInfo->timerType != Qt::PreciseTimer)
|
|
|
|
qDebug() << "timer" << currentTimerInfo->timerType << hex << currentTimerInfo->id << dec << "interval"
|
|
|
|
<< currentTimerInfo->interval << "firing at" << currentTime
|
|
|
|
<< "(orig" << currentTimerInfo->expected << "scheduled at" << currentTimerInfo->timeout
|
|
|
|
<< ") off by" << diff << "activation" << currentTimerInfo->count
|
|
|
|
<< "avg error" << (currentTimerInfo->cumulativeError / currentTimerInfo->count);
|
|
|
|
#endif
|
|
|
|
|
2011-10-25 07:51:19 +00:00
|
|
|
// determine next timeout time
|
2011-12-21 10:34:07 +00:00
|
|
|
calculateNextTimeout(currentTimerInfo, currentTime);
|
2011-10-25 07:51:19 +00:00
|
|
|
|
|
|
|
// reinsert timer
|
|
|
|
timerInsert(currentTimerInfo);
|
2011-12-21 10:33:56 +00:00
|
|
|
if (currentTimerInfo->interval > 0)
|
2011-10-25 07:51:19 +00:00
|
|
|
n_act++;
|
|
|
|
|
|
|
|
if (!currentTimerInfo->activateRef) {
|
|
|
|
// send event, but don't allow it to recurse
|
|
|
|
currentTimerInfo->activateRef = ¤tTimerInfo;
|
|
|
|
|
|
|
|
QTimerEvent e(currentTimerInfo->id);
|
|
|
|
QCoreApplication::sendEvent(currentTimerInfo->obj, &e);
|
|
|
|
|
|
|
|
if (currentTimerInfo)
|
|
|
|
currentTimerInfo->activateRef = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
firstTimerInfo = 0;
|
2011-12-21 10:34:07 +00:00
|
|
|
// qDebug() << "Thread" << QThread::currentThreadId() << "activated" << n_act << "timers";
|
2011-10-25 07:51:19 +00:00
|
|
|
return n_act;
|
|
|
|
}
|
|
|
|
|
|
|
|
QT_END_NAMESPACE
|