summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBradley T. Hughes <bradley.hughes@nokia.com>2009-07-23 11:21:51 (GMT)
committerBradley T. Hughes <bradley.hughes@nokia.com>2009-07-23 13:20:01 (GMT)
commit9a1e0047d02fd94f92eda6cd6751bfe4cd876cd2 (patch)
tree7cd941821681f138efb1d37a8902948744182431
parent9ad53e9404889b119f26d0a7bc7c70bf93364e23 (diff)
downloadQt-9a1e0047d02fd94f92eda6cd6751bfe4cd876cd2.zip
Qt-9a1e0047d02fd94f92eda6cd6751bfe4cd876cd2.tar.gz
Qt-9a1e0047d02fd94f92eda6cd6751bfe4cd876cd2.tar.bz2
Fix time change detection on UNIX systems without monotonic timers
A few fixes in one: 1. Don't loop on select() when not using monotonic timers... when select returns, the time may have changed, and the offset calculated in the loop may be very wrong on the next iteration. 2. Calculate the elapsed time deltas using timevals instead of integers using milliseconds. This handles changing the time by more than a few hours or days (i.e. months and years) without overflow. 3. When repairing the timers, the diff is already the correct sign, so we should just add the diff. Task-number: 250681 Reviewed-by: Thiago
-rw-r--r--src/corelib/kernel/qeventdispatcher_unix.cpp75
-rw-r--r--src/corelib/kernel/qeventdispatcher_unix_p.h46
2 files changed, 79 insertions, 42 deletions
diff --git a/src/corelib/kernel/qeventdispatcher_unix.cpp b/src/corelib/kernel/qeventdispatcher_unix.cpp
index fac8a28..2139545 100644
--- a/src/corelib/kernel/qeventdispatcher_unix.cpp
+++ b/src/corelib/kernel/qeventdispatcher_unix.cpp
@@ -67,6 +67,25 @@ QT_BEGIN_NAMESPACE
Q_CORE_EXPORT bool qt_disable_lowpriority_timers=false;
+// check for _POSIX_MONOTONIC_CLOCK support
+static bool supportsMonotonicClock()
+{
+ bool returnValue;
+
+#if (_POSIX_MONOTONIC_CLOCK-0 <= 0) && !defined(Q_OS_MAC)
+ returnValue = false;
+# if (_POSIX_MONOTONIC_CLOCK == 0)
+ // detect if the system support monotonic timers
+ long x = sysconf(_SC_MONOTONIC_CLOCK);
+ returnValue = x >= 200112L;
+# endif
+#else
+ returnValue = true;
+#endif
+
+ return returnValue;
+}
+
/*****************************************************************************
UNIX signal handling
*****************************************************************************/
@@ -262,18 +281,11 @@ int QEventDispatcherUNIXPrivate::doSelect(QEventLoop::ProcessEventsFlags flags,
*/
QTimerInfoList::QTimerInfoList()
+ : useMonotonicTimers(supportsMonotonicClock())
{
-#if (_POSIX_MONOTONIC_CLOCK-0 <= 0) && !defined(Q_OS_MAC)
- useMonotonicTimers = false;
-
-# if (_POSIX_MONOTONIC_CLOCK == 0)
- // detect if the system support monotonic timers
- long x = sysconf(_SC_MONOTONIC_CLOCK);
- useMonotonicTimers = x != -1;
-# endif
-
getTime(currentTime);
+#if (_POSIX_MONOTONIC_CLOCK-0 <= 0) && !defined(Q_OS_MAC)
if (!useMonotonicTimers) {
// not using monotonic timers, initialize the timeChanged() machinery
previousTime = currentTime;
@@ -290,9 +302,6 @@ QTimerInfoList::QTimerInfoList()
ticksPerSecond = 0;
msPerTick = 0;
}
-#else
- // using monotonic timers unconditionally
- getTime(currentTime);
#endif
firstTimerInfo = currentTimerInfo = 0;
@@ -306,6 +315,20 @@ timeval QTimerInfoList::updateCurrentTime()
#if ((_POSIX_MONOTONIC_CLOCK-0 <= 0) && !defined(Q_OS_MAC)) || defined(QT_BOOTSTRAPPED)
+template <>
+timeval qAbs(const timeval &t)
+{
+ timeval tmp = t;
+ if (tmp.tv_sec < 0) {
+ tmp.tv_sec = -tmp.tv_sec - 1;
+ tmp.tv_usec -= 1000000;
+ }
+ if (tmp.tv_sec == 0 && tmp.tv_usec < 0) {
+ tmp.tv_usec = -tmp.tv_usec;
+ }
+ return normalizedTimeval(tmp);
+}
+
/*
Returns true if the real time clock has changed by more than 10%
relative to the processor time since the last time this function was
@@ -318,23 +341,27 @@ bool QTimerInfoList::timeChanged(timeval *delta)
tms unused;
clock_t currentTicks = times(&unused);
- int elapsedTicks = currentTicks - previousTicks;
+ clock_t elapsedTicks = currentTicks - previousTicks;
timeval elapsedTime = currentTime - previousTime;
- int elapsedMsecTicks = (elapsedTicks * 1000) / ticksPerSecond;
- int deltaMsecs = (elapsedTime.tv_sec * 1000 + elapsedTime.tv_usec / 1000)
- - elapsedMsecTicks;
- if (delta) {
- delta->tv_sec = deltaMsecs / 1000;
- delta->tv_usec = (deltaMsecs % 1000) * 1000;
- }
+ timeval elapsedTimeTicks;
+ elapsedTimeTicks.tv_sec = elapsedTicks / ticksPerSecond;
+ elapsedTimeTicks.tv_usec = (((elapsedTicks * 1000) / ticksPerSecond) % 1000) * 1000;
+
+ timeval dummy;
+ 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.
-
- return (qAbs(deltaMsecs) - msPerTick) * 10 > elapsedMsecTicks;
+ timeval tickGranularity;
+ tickGranularity.tv_sec = 0;
+ tickGranularity.tv_usec = msPerTick * 1000;
+ return elapsedTimeTicks < ((qAbs(*delta) - tickGranularity) * 10);
}
void QTimerInfoList::getTime(timeval &t)
@@ -424,7 +451,7 @@ void QTimerInfoList::timerRepair(const timeval &diff)
// repair all timers
for (int i = 0; i < size(); ++i) {
register QTimerInfo *t = at(i);
- t->timeout = t->timeout - diff;
+ t->timeout = t->timeout + diff;
}
}
@@ -622,7 +649,7 @@ int QEventDispatcherUNIX::select(int nfds, fd_set *readfds, fd_set *writefds, fd
timeval *timeout)
{
Q_D(QEventDispatcherUNIX);
- if (timeout) {
+ if (timeout && d->timerList.useMonotonicTimers) {
// handle the case where select returns with a timeout, too
// soon.
timeval tvStart = d->timerList.currentTime;
diff --git a/src/corelib/kernel/qeventdispatcher_unix_p.h b/src/corelib/kernel/qeventdispatcher_unix_p.h
index ebba21b..28e7f9b 100644
--- a/src/corelib/kernel/qeventdispatcher_unix_p.h
+++ b/src/corelib/kernel/qeventdispatcher_unix_p.h
@@ -71,6 +71,18 @@ QT_BEGIN_NAMESPACE
#endif
// Internal operator functions for timevals
+inline timeval &normalizedTimeval(timeval &t)
+{
+ while (t.tv_usec > 1000000l) {
+ ++t.tv_sec;
+ t.tv_usec -= 1000000l;
+ }
+ while (t.tv_usec < 0l) {
+ --t.tv_sec;
+ t.tv_usec += 1000000l;
+ }
+ return t;
+}
inline bool operator<(const timeval &t1, const timeval &t2)
{ return t1.tv_sec < t2.tv_sec || (t1.tv_sec == t2.tv_sec && t1.tv_usec < t2.tv_usec); }
inline bool operator==(const timeval &t1, const timeval &t2)
@@ -78,31 +90,29 @@ inline bool operator==(const timeval &t1, const timeval &t2)
inline timeval &operator+=(timeval &t1, const timeval &t2)
{
t1.tv_sec += t2.tv_sec;
- if ((t1.tv_usec += t2.tv_usec) >= 1000000l) {
- ++t1.tv_sec;
- t1.tv_usec -= 1000000l;
- }
- return t1;
+ t1.tv_usec += t2.tv_usec;
+ return normalizedTimeval(t1);
}
inline timeval operator+(const timeval &t1, const timeval &t2)
{
timeval tmp;
tmp.tv_sec = t1.tv_sec + t2.tv_sec;
- if ((tmp.tv_usec = t1.tv_usec + t2.tv_usec) >= 1000000l) {
- ++tmp.tv_sec;
- tmp.tv_usec -= 1000000l;
- }
- return tmp;
+ tmp.tv_usec = t1.tv_usec + t2.tv_usec;
+ return normalizedTimeval(tmp);
}
inline timeval operator-(const timeval &t1, const timeval &t2)
{
timeval tmp;
- tmp.tv_sec = t1.tv_sec - t2.tv_sec;
- if ((tmp.tv_usec = t1.tv_usec - t2.tv_usec) < 0l) {
- --tmp.tv_sec;
- tmp.tv_usec += 1000000l;
- }
- return tmp;
+ tmp.tv_sec = t1.tv_sec - (t2.tv_sec - 1);
+ tmp.tv_usec = t1.tv_usec - (t2.tv_usec + 1000000);
+ return normalizedTimeval(tmp);
+}
+inline timeval operator*(const timeval &t1, int mul)
+{
+ timeval tmp;
+ tmp.tv_sec = t1.tv_sec * mul;
+ tmp.tv_usec = t1.tv_usec * mul;
+ return normalizedTimeval(tmp);
}
// internal timer info
@@ -117,8 +127,6 @@ struct QTimerInfo {
class QTimerInfoList : public QList<QTimerInfo*>
{
#if ((_POSIX_MONOTONIC_CLOCK-0 <= 0) && !defined(Q_OS_MAC)) || defined(QT_BOOTSTRAPPED)
- bool useMonotonicTimers;
-
timeval previousTime;
clock_t previousTicks;
int ticksPerSecond;
@@ -133,6 +141,8 @@ class QTimerInfoList : public QList<QTimerInfo*>
public:
QTimerInfoList();
+ const bool useMonotonicTimers;
+
void getTime(timeval &t);
timeval currentTime;