1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Copyright (C) 2016 Intel Corporation.
5** Contact: https://www.qt.io/licensing/
6**
7** This file is part of the QtCore module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial License Usage
11** Licensees holding valid commercial Qt licenses may use this file in
12** accordance with the commercial license agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and The Qt Company. For licensing terms
15** and conditions see https://www.qt.io/terms-conditions. For further
16** information use the contact form at https://www.qt.io/contact-us.
17**
18** GNU Lesser General Public License Usage
19** Alternatively, this file may be used under the terms of the GNU Lesser
20** General Public License version 3 as published by the Free Software
21** Foundation and appearing in the file LICENSE.LGPL3 included in the
22** packaging of this file. Please review the following information to
23** ensure the GNU Lesser General Public License version 3 requirements
24** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
25**
26** GNU General Public License Usage
27** Alternatively, this file may be used under the terms of the GNU
28** General Public License version 2.0 or (at your option) the GNU General
29** Public license version 3 or any later version approved by the KDE Free
30** Qt Foundation. The licenses are as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
32** included in the packaging of this file. Please review the following
33** information to ensure the GNU General Public License requirements will
34** be met: https://www.gnu.org/licenses/gpl-2.0.html and
35** https://www.gnu.org/licenses/gpl-3.0.html.
36**
37** $QT_END_LICENSE$
38**
39****************************************************************************/
40
41#include <qelapsedtimer.h>
42#include <qcoreapplication.h>
43
44#include "private/qcore_unix_p.h"
45#include "private/qtimerinfo_unix_p.h"
46#include "private/qobject_p.h"
47#include "private/qabstracteventdispatcher_p.h"
48
49#ifdef QTIMERINFO_DEBUG
50# include <QDebug>
51# include <QThread>
52#endif
53
54#include <sys/times.h>
55
56QT_BEGIN_NAMESPACE
57
58Q_CORE_EXPORT bool qt_disable_lowpriority_timers=false;
59
60/*
61 * Internal functions for manipulating timer data structures. The
62 * timerBitVec array is used for keeping track of timer identifiers.
63 */
64
65QTimerInfoList::QTimerInfoList()
66{
67#if (_POSIX_MONOTONIC_CLOCK-0 <= 0) && !defined(Q_OS_MAC)
68 if (!QElapsedTimer::isMonotonic()) {
69 // not using monotonic timers, initialize the timeChanged() machinery
70 previousTime = qt_gettime();
71
72 tms unused;
73 previousTicks = times(&unused);
74
75 ticksPerSecond = sysconf(_SC_CLK_TCK);
76 msPerTick = 1000/ticksPerSecond;
77 } else {
78 // detected monotonic timers
79 previousTime.tv_sec = previousTime.tv_nsec = 0;
80 previousTicks = 0;
81 ticksPerSecond = 0;
82 msPerTick = 0;
83 }
84#endif
85
86 firstTimerInfo = nullptr;
87}
88
89timespec QTimerInfoList::updateCurrentTime()
90{
91 return (currentTime = qt_gettime());
92}
93
94#if ((_POSIX_MONOTONIC_CLOCK-0 <= 0) && !defined(Q_OS_MAC) && !defined(Q_OS_INTEGRITY)) || defined(QT_BOOTSTRAPPED)
95
96timespec qAbsTimespec(const timespec &t)
97{
98 timespec tmp = t;
99 if (tmp.tv_sec < 0) {
100 tmp.tv_sec = -tmp.tv_sec - 1;
101 tmp.tv_nsec -= 1000000000;
102 }
103 if (tmp.tv_sec == 0 && tmp.tv_nsec < 0) {
104 tmp.tv_nsec = -tmp.tv_nsec;
105 }
106 return normalizedTimespec(tmp);
107}
108
109/*
110 Returns \c true if the real time clock has changed by more than 10%
111 relative to the processor time since the last time this function was
112 called. This presumably means that the system time has been changed.
113
114 If /a delta is nonzero, delta is set to our best guess at how much the system clock was changed.
115*/
116bool QTimerInfoList::timeChanged(timespec *delta)
117{
118 struct tms unused;
119 clock_t currentTicks = times(&unused);
120
121 clock_t elapsedTicks = currentTicks - previousTicks;
122 timespec elapsedTime = currentTime - previousTime;
123
124 timespec elapsedTimeTicks;
125 elapsedTimeTicks.tv_sec = elapsedTicks / ticksPerSecond;
126 elapsedTimeTicks.tv_nsec = (((elapsedTicks * 1000) / ticksPerSecond) % 1000) * 1000 * 1000;
127
128 timespec dummy;
129 if (!delta)
130 delta = &dummy;
131 *delta = elapsedTime - elapsedTimeTicks;
132
133 previousTicks = currentTicks;
134 previousTime = currentTime;
135
136 // If tick drift is more than 10% off compared to realtime, we assume that the clock has
137 // been set. Of course, we have to allow for the tick granularity as well.
138 timespec tickGranularity;
139 tickGranularity.tv_sec = 0;
140 tickGranularity.tv_nsec = msPerTick * 1000 * 1000;
141 return elapsedTimeTicks < ((qAbsTimespec(*delta) - tickGranularity) * 10);
142}
143
144/*
145 repair broken timer
146*/
147void QTimerInfoList::timerRepair(const timespec &diff)
148{
149 // repair all timers
150 for (int i = 0; i < size(); ++i) {
151 QTimerInfo *t = at(i);
152 t->timeout = t->timeout + diff;
153 }
154}
155
156void QTimerInfoList::repairTimersIfNeeded()
157{
158 if (QElapsedTimer::isMonotonic())
159 return;
160 timespec delta;
161 if (timeChanged(&delta))
162 timerRepair(delta);
163}
164
165#else // !(_POSIX_MONOTONIC_CLOCK-0 <= 0) && !defined(QT_BOOTSTRAPPED)
166
167void QTimerInfoList::repairTimersIfNeeded()
168{
169}
170
171#endif
172
173/*
174 insert timer info into list
175*/
176void QTimerInfoList::timerInsert(QTimerInfo *ti)
177{
178 int index = size();
179 while (index--) {
180 const QTimerInfo * const t = at(index);
181 if (!(ti->timeout < t->timeout))
182 break;
183 }
184 insert(index+1, ti);
185}
186
187inline timespec &operator+=(timespec &t1, int ms)
188{
189 t1.tv_sec += ms / 1000;
190 t1.tv_nsec += ms % 1000 * 1000 * 1000;
191 return normalizedTimespec(t1);
192}
193
194inline timespec operator+(const timespec &t1, int ms)
195{
196 timespec t2 = t1;
197 return t2 += ms;
198}
199
200static timespec roundToMillisecond(timespec val)
201{
202 // always round up
203 // worst case scenario is that the first trigger of a 1-ms timer is 0.999 ms late
204
205 int ns = val.tv_nsec % (1000 * 1000);
206 val.tv_nsec += 1000 * 1000 - ns;
207 return normalizedTimespec(val);
208}
209
210#ifdef QTIMERINFO_DEBUG
211QDebug operator<<(QDebug s, timeval tv)
212{
213 QDebugStateSaver saver(s);
214 s.nospace() << tv.tv_sec << "." << qSetFieldWidth(6) << qSetPadChar(QChar(48)) << tv.tv_usec << Qt::reset;
215 return s;
216}
217QDebug operator<<(QDebug s, Qt::TimerType t)
218{
219 QDebugStateSaver saver(s);
220 s << (t == Qt::PreciseTimer ? "P" :
221 t == Qt::CoarseTimer ? "C" : "VC");
222 return s;
223}
224#endif
225
226static void calculateCoarseTimerTimeout(QTimerInfo *t, timespec currentTime)
227{
228 // The coarse timer works like this:
229 // - interval under 40 ms: round to even
230 // - between 40 and 99 ms: round to multiple of 4
231 // - otherwise: try to wake up at a multiple of 25 ms, with a maximum error of 5%
232 //
233 // We try to wake up at the following second-fraction, in order of preference:
234 // 0 ms
235 // 500 ms
236 // 250 ms or 750 ms
237 // 200, 400, 600, 800 ms
238 // other multiples of 100
239 // other multiples of 50
240 // other multiples of 25
241 //
242 // The objective is to make most timers wake up at the same time, thereby reducing CPU wakeups.
243
244 uint interval = uint(t->interval);
245 uint msec = uint(t->timeout.tv_nsec) / 1000 / 1000;
246 Q_ASSERT(interval >= 20);
247
248 // Calculate how much we can round and still keep within 5% error
249 uint absMaxRounding = interval / 20;
250
251 if (interval < 100 && interval != 25 && interval != 50 && interval != 75) {
252 // special mode for timers of less than 100 ms
253 if (interval < 50) {
254 // round to even
255 // round towards multiples of 50 ms
256 bool roundUp = (msec % 50) >= 25;
257 msec >>= 1;
258 msec |= uint(roundUp);
259 msec <<= 1;
260 } else {
261 // round to multiple of 4
262 // round towards multiples of 100 ms
263 bool roundUp = (msec % 100) >= 50;
264 msec >>= 2;
265 msec |= uint(roundUp);
266 msec <<= 2;
267 }
268 } else {
269 uint min = qMax<int>(0, msec - absMaxRounding);
270 uint max = qMin(1000u, msec + absMaxRounding);
271
272 // find the boundary that we want, according to the rules above
273 // extra rules:
274 // 1) whatever the interval, we'll take any round-to-the-second timeout
275 if (min == 0) {
276 msec = 0;
277 goto recalculate;
278 } else if (max == 1000) {
279 msec = 1000;
280 goto recalculate;
281 }
282
283 uint wantedBoundaryMultiple;
284
285 // 2) if the interval is a multiple of 500 ms and > 5000 ms, we'll always round
286 // towards a round-to-the-second
287 // 3) if the interval is a multiple of 500 ms, we'll round towards the nearest
288 // multiple of 500 ms
289 if ((interval % 500) == 0) {
290 if (interval >= 5000) {
291 msec = msec >= 500 ? max : min;
292 goto recalculate;
293 } else {
294 wantedBoundaryMultiple = 500;
295 }
296 } else if ((interval % 50) == 0) {
297 // 4) same for multiples of 250, 200, 100, 50
298 uint mult50 = interval / 50;
299 if ((mult50 % 4) == 0) {
300 // multiple of 200
301 wantedBoundaryMultiple = 200;
302 } else if ((mult50 % 2) == 0) {
303 // multiple of 100
304 wantedBoundaryMultiple = 100;
305 } else if ((mult50 % 5) == 0) {
306 // multiple of 250
307 wantedBoundaryMultiple = 250;
308 } else {
309 // multiple of 50
310 wantedBoundaryMultiple = 50;
311 }
312 } else {
313 wantedBoundaryMultiple = 25;
314 }
315
316 uint base = msec / wantedBoundaryMultiple * wantedBoundaryMultiple;
317 uint middlepoint = base + wantedBoundaryMultiple / 2;
318 if (msec < middlepoint)
319 msec = qMax(base, min);
320 else
321 msec = qMin(base + wantedBoundaryMultiple, max);
322 }
323
324recalculate:
325 if (msec == 1000u) {
326 ++t->timeout.tv_sec;
327 t->timeout.tv_nsec = 0;
328 } else {
329 t->timeout.tv_nsec = msec * 1000 * 1000;
330 }
331
332 if (t->timeout < currentTime)
333 t->timeout += interval;
334}
335
336static void calculateNextTimeout(QTimerInfo *t, timespec currentTime)
337{
338 switch (t->timerType) {
339 case Qt::PreciseTimer:
340 case Qt::CoarseTimer:
341 t->timeout += t->interval;
342 if (t->timeout < currentTime) {
343 t->timeout = currentTime;
344 t->timeout += t->interval;
345 }
346#ifdef QTIMERINFO_DEBUG
347 t->expected += t->interval;
348 if (t->expected < currentTime) {
349 t->expected = currentTime;
350 t->expected += t->interval;
351 }
352#endif
353 if (t->timerType == Qt::CoarseTimer)
354 calculateCoarseTimerTimeout(t, currentTime);
355 return;
356
357 case Qt::VeryCoarseTimer:
358 // we don't need to take care of the microsecond component of t->interval
359 t->timeout.tv_sec += t->interval;
360 if (t->timeout.tv_sec <= currentTime.tv_sec)
361 t->timeout.tv_sec = currentTime.tv_sec + t->interval;
362#ifdef QTIMERINFO_DEBUG
363 t->expected.tv_sec += t->interval;
364 if (t->expected.tv_sec <= currentTime.tv_sec)
365 t->expected.tv_sec = currentTime.tv_sec + t->interval;
366#endif
367 return;
368 }
369
370#ifdef QTIMERINFO_DEBUG
371 if (t->timerType != Qt::PreciseTimer)
372 qDebug() << "timer" << t->timerType << Qt::hex << t->id << Qt::dec << "interval" << t->interval
373 << "originally expected at" << t->expected << "will fire at" << t->timeout
374 << "or" << (t->timeout - t->expected) << "s late";
375#endif
376}
377
378/*
379 Returns the time to wait for the next timer, or null if no timers
380 are waiting.
381*/
382bool QTimerInfoList::timerWait(timespec &tm)
383{
384 timespec currentTime = updateCurrentTime();
385 repairTimersIfNeeded();
386
387 // Find first waiting timer not already active
388 QTimerInfo *t = nullptr;
389 for (QTimerInfoList::const_iterator it = constBegin(); it != constEnd(); ++it) {
390 if (!(*it)->activateRef) {
391 t = *it;
392 break;
393 }
394 }
395
396 if (!t)
397 return false;
398
399 if (currentTime < t->timeout) {
400 // time to wait
401 tm = roundToMillisecond(t->timeout - currentTime);
402 } else {
403 // no time to wait
404 tm.tv_sec = 0;
405 tm.tv_nsec = 0;
406 }
407
408 return true;
409}
410
411/*
412 Returns the timer's remaining time in milliseconds with the given timerId, or
413 null if there is nothing left. If the timer id is not found in the list, the
414 returned value will be -1. If the timer is overdue, the returned value will be 0.
415*/
416int QTimerInfoList::timerRemainingTime(int timerId)
417{
418 timespec currentTime = updateCurrentTime();
419 repairTimersIfNeeded();
420 timespec tm = {0, 0};
421
422 for (int i = 0; i < count(); ++i) {
423 QTimerInfo *t = at(i);
424 if (t->id == timerId) {
425 if (currentTime < t->timeout) {
426 // time to wait
427 tm = roundToMillisecond(t->timeout - currentTime);
428 return tm.tv_sec*1000 + tm.tv_nsec/1000/1000;
429 } else {
430 return 0;
431 }
432 }
433 }
434
435#ifndef QT_NO_DEBUG
436 qWarning("QTimerInfoList::timerRemainingTime: timer id %i not found", timerId);
437#endif
438
439 return -1;
440}
441
442void QTimerInfoList::registerTimer(int timerId, qint64 interval, Qt::TimerType timerType, QObject *object)
443{
444 QTimerInfo *t = new QTimerInfo;
445 t->id = timerId;
446 t->interval = interval;
447 t->timerType = timerType;
448 t->obj = object;
449 t->activateRef = nullptr;
450
451 timespec expected = updateCurrentTime() + interval;
452
453 switch (timerType) {
454 case Qt::PreciseTimer:
455 // high precision timer is based on millisecond precision
456 // so no adjustment is necessary
457 t->timeout = expected;
458 break;
459
460 case Qt::CoarseTimer:
461 // this timer has up to 5% coarseness
462 // so our boundaries are 20 ms and 20 s
463 // below 20 ms, 5% inaccuracy is below 1 ms, so we convert to high precision
464 // above 20 s, 5% inaccuracy is above 1 s, so we convert to VeryCoarseTimer
465 if (interval >= 20000) {
466 t->timerType = Qt::VeryCoarseTimer;
467 } else {
468 t->timeout = expected;
469 if (interval <= 20) {
470 t->timerType = Qt::PreciseTimer;
471 // no adjustment is necessary
472 } else if (interval <= 20000) {
473 calculateCoarseTimerTimeout(t, currentTime);
474 }
475 break;
476 }
477 Q_FALLTHROUGH();
478 case Qt::VeryCoarseTimer:
479 // the very coarse timer is based on full second precision,
480 // so we keep the interval in seconds (round to closest second)
481 t->interval /= 500;
482 t->interval += 1;
483 t->interval >>= 1;
484 t->timeout.tv_sec = currentTime.tv_sec + t->interval;
485 t->timeout.tv_nsec = 0;
486
487 // if we're past the half-second mark, increase the timeout again
488 if (currentTime.tv_nsec > 500*1000*1000)
489 ++t->timeout.tv_sec;
490 }
491
492 timerInsert(t);
493
494#ifdef QTIMERINFO_DEBUG
495 t->expected = expected;
496 t->cumulativeError = 0;
497 t->count = 0;
498 if (t->timerType != Qt::PreciseTimer)
499 qDebug() << "timer" << t->timerType << Qt::hex <<t->id << Qt::dec << "interval" << t->interval << "expected at"
500 << t->expected << "will fire first at" << t->timeout;
501#endif
502}
503
504bool QTimerInfoList::unregisterTimer(int timerId)
505{
506 // set timer inactive
507 for (int i = 0; i < count(); ++i) {
508 QTimerInfo *t = at(i);
509 if (t->id == timerId) {
510 // found it
511 removeAt(i);
512 if (t == firstTimerInfo)
513 firstTimerInfo = nullptr;
514 if (t->activateRef)
515 *(t->activateRef) = nullptr;
516 delete t;
517 return true;
518 }
519 }
520 // id not found
521 return false;
522}
523
524bool QTimerInfoList::unregisterTimers(QObject *object)
525{
526 if (isEmpty())
527 return false;
528 for (int i = 0; i < count(); ++i) {
529 QTimerInfo *t = at(i);
530 if (t->obj == object) {
531 // object found
532 removeAt(i);
533 if (t == firstTimerInfo)
534 firstTimerInfo = nullptr;
535 if (t->activateRef)
536 *(t->activateRef) = nullptr;
537 delete t;
538 // move back one so that we don't skip the new current item
539 --i;
540 }
541 }
542 return true;
543}
544
545QList<QAbstractEventDispatcher::TimerInfo> QTimerInfoList::registeredTimers(QObject *object) const
546{
547 QList<QAbstractEventDispatcher::TimerInfo> list;
548 for (int i = 0; i < count(); ++i) {
549 const QTimerInfo * const t = at(i);
550 if (t->obj == object) {
551 list << QAbstractEventDispatcher::TimerInfo(t->id,
552 (t->timerType == Qt::VeryCoarseTimer
553 ? t->interval * 1000
554 : t->interval),
555 t->timerType);
556 }
557 }
558 return list;
559}
560
561/*
562 Activate pending timers, returning how many where activated.
563*/
564int QTimerInfoList::activateTimers()
565{
566 if (qt_disable_lowpriority_timers || isEmpty())
567 return 0; // nothing to do
568
569 int n_act = 0, maxCount = 0;
570 firstTimerInfo = nullptr;
571
572 timespec currentTime = updateCurrentTime();
573 // qDebug() << "Thread" << QThread::currentThreadId() << "woken up at" << currentTime;
574 repairTimersIfNeeded();
575
576
577 // Find out how many timer have expired
578 for (QTimerInfoList::const_iterator it = constBegin(); it != constEnd(); ++it) {
579 if (currentTime < (*it)->timeout)
580 break;
581 maxCount++;
582 }
583
584 //fire the timers.
585 while (maxCount--) {
586 if (isEmpty())
587 break;
588
589 QTimerInfo *currentTimerInfo = constFirst();
590 if (currentTime < currentTimerInfo->timeout)
591 break; // no timer has expired
592
593 if (!firstTimerInfo) {
594 firstTimerInfo = currentTimerInfo;
595 } else if (firstTimerInfo == currentTimerInfo) {
596 // avoid sending the same timer multiple times
597 break;
598 } else if (currentTimerInfo->interval < firstTimerInfo->interval
599 || currentTimerInfo->interval == firstTimerInfo->interval) {
600 firstTimerInfo = currentTimerInfo;
601 }
602
603 // remove from list
604 removeFirst();
605
606#ifdef QTIMERINFO_DEBUG
607 float diff;
608 if (currentTime < currentTimerInfo->expected) {
609 // early
610 timeval early = currentTimerInfo->expected - currentTime;
611 diff = -(early.tv_sec + early.tv_usec / 1000000.0);
612 } else {
613 timeval late = currentTime - currentTimerInfo->expected;
614 diff = late.tv_sec + late.tv_usec / 1000000.0;
615 }
616 currentTimerInfo->cumulativeError += diff;
617 ++currentTimerInfo->count;
618 if (currentTimerInfo->timerType != Qt::PreciseTimer)
619 qDebug() << "timer" << currentTimerInfo->timerType << Qt::hex << currentTimerInfo->id << Qt::dec << "interval"
620 << currentTimerInfo->interval << "firing at" << currentTime
621 << "(orig" << currentTimerInfo->expected << "scheduled at" << currentTimerInfo->timeout
622 << ") off by" << diff << "activation" << currentTimerInfo->count
623 << "avg error" << (currentTimerInfo->cumulativeError / currentTimerInfo->count);
624#endif
625
626 // determine next timeout time
627 calculateNextTimeout(currentTimerInfo, currentTime);
628
629 // reinsert timer
630 timerInsert(currentTimerInfo);
631 if (currentTimerInfo->interval > 0)
632 n_act++;
633
634 if (!currentTimerInfo->activateRef) {
635 // send event, but don't allow it to recurse
636 currentTimerInfo->activateRef = &currentTimerInfo;
637
638 QTimerEvent e(currentTimerInfo->id);
639 QCoreApplication::sendEvent(currentTimerInfo->obj, &e);
640
641 if (currentTimerInfo)
642 currentTimerInfo->activateRef = nullptr;
643 }
644 }
645
646 firstTimerInfo = nullptr;
647 // qDebug() << "Thread" << QThread::currentThreadId() << "activated" << n_act << "timers";
648 return n_act;
649}
650
651QT_END_NAMESPACE
652