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) && !defined(Q_OS_NACL)
68 if (!QElapsedTimer::isMonotonic()) {
69 // not using monotonic timers, initialize the timeChanged() machinery
70 previousTime = qt_gettime();
71
72 tms unused;
73 previousTicks = times(buffer: &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(t&: 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#ifdef Q_OS_NACL
119 Q_UNUSED(delta)
120 return false; // Calling "times" crashes.
121#endif
122 struct tms unused;
123 clock_t currentTicks = times(buffer: &unused);
124
125 clock_t elapsedTicks = currentTicks - previousTicks;
126 timespec elapsedTime = currentTime - previousTime;
127
128 timespec elapsedTimeTicks;
129 elapsedTimeTicks.tv_sec = elapsedTicks / ticksPerSecond;
130 elapsedTimeTicks.tv_nsec = (((elapsedTicks * 1000) / ticksPerSecond) % 1000) * 1000 * 1000;
131
132 timespec dummy;
133 if (!delta)
134 delta = &dummy;
135 *delta = elapsedTime - elapsedTimeTicks;
136
137 previousTicks = currentTicks;
138 previousTime = currentTime;
139
140 // If tick drift is more than 10% off compared to realtime, we assume that the clock has
141 // been set. Of course, we have to allow for the tick granularity as well.
142 timespec tickGranularity;
143 tickGranularity.tv_sec = 0;
144 tickGranularity.tv_nsec = msPerTick * 1000 * 1000;
145 return elapsedTimeTicks < ((qAbsTimespec(t: *delta) - tickGranularity) * 10);
146}
147
148/*
149 repair broken timer
150*/
151void QTimerInfoList::timerRepair(const timespec &diff)
152{
153 // repair all timers
154 for (int i = 0; i < size(); ++i) {
155 QTimerInfo *t = at(i);
156 t->timeout = t->timeout + diff;
157 }
158}
159
160void QTimerInfoList::repairTimersIfNeeded()
161{
162 if (QElapsedTimer::isMonotonic())
163 return;
164 timespec delta;
165 if (timeChanged(delta: &delta))
166 timerRepair(diff: delta);
167}
168
169#else // !(_POSIX_MONOTONIC_CLOCK-0 <= 0) && !defined(QT_BOOTSTRAPPED)
170
171void QTimerInfoList::repairTimersIfNeeded()
172{
173}
174
175#endif
176
177/*
178 insert timer info into list
179*/
180void QTimerInfoList::timerInsert(QTimerInfo *ti)
181{
182 int index = size();
183 while (index--) {
184 const QTimerInfo * const t = at(i: index);
185 if (!(ti->timeout < t->timeout))
186 break;
187 }
188 insert(i: index+1, t: ti);
189}
190
191inline timespec &operator+=(timespec &t1, int ms)
192{
193 t1.tv_sec += ms / 1000;
194 t1.tv_nsec += ms % 1000 * 1000 * 1000;
195 return normalizedTimespec(t&: t1);
196}
197
198inline timespec operator+(const timespec &t1, int ms)
199{
200 timespec t2 = t1;
201 return t2 += ms;
202}
203
204static timespec roundToMillisecond(timespec val)
205{
206 // always round up
207 // worst case scenario is that the first trigger of a 1-ms timer is 0.999 ms late
208
209 int ns = val.tv_nsec % (1000 * 1000);
210 val.tv_nsec += 1000 * 1000 - ns;
211 return normalizedTimespec(t&: val);
212}
213
214#ifdef QTIMERINFO_DEBUG
215QDebug operator<<(QDebug s, timeval tv)
216{
217 QDebugStateSaver saver(s);
218 s.nospace() << tv.tv_sec << "." << qSetFieldWidth(6) << qSetPadChar(QChar(48)) << tv.tv_usec << Qt::reset;
219 return s;
220}
221QDebug operator<<(QDebug s, Qt::TimerType t)
222{
223 QDebugStateSaver saver(s);
224 s << (t == Qt::PreciseTimer ? "P" :
225 t == Qt::CoarseTimer ? "C" : "VC");
226 return s;
227}
228#endif
229
230static void calculateCoarseTimerTimeout(QTimerInfo *t, timespec currentTime)
231{
232 // The coarse timer works like this:
233 // - interval under 40 ms: round to even
234 // - between 40 and 99 ms: round to multiple of 4
235 // - otherwise: try to wake up at a multiple of 25 ms, with a maximum error of 5%
236 //
237 // We try to wake up at the following second-fraction, in order of preference:
238 // 0 ms
239 // 500 ms
240 // 250 ms or 750 ms
241 // 200, 400, 600, 800 ms
242 // other multiples of 100
243 // other multiples of 50
244 // other multiples of 25
245 //
246 // The objective is to make most timers wake up at the same time, thereby reducing CPU wakeups.
247
248 uint interval = uint(t->interval);
249 uint msec = uint(t->timeout.tv_nsec) / 1000 / 1000;
250 Q_ASSERT(interval >= 20);
251
252 // Calculate how much we can round and still keep within 5% error
253 uint absMaxRounding = interval / 20;
254
255 if (interval < 100 && interval != 25 && interval != 50 && interval != 75) {
256 // special mode for timers of less than 100 ms
257 if (interval < 50) {
258 // round to even
259 // round towards multiples of 50 ms
260 bool roundUp = (msec % 50) >= 25;
261 msec >>= 1;
262 msec |= uint(roundUp);
263 msec <<= 1;
264 } else {
265 // round to multiple of 4
266 // round towards multiples of 100 ms
267 bool roundUp = (msec % 100) >= 50;
268 msec >>= 2;
269 msec |= uint(roundUp);
270 msec <<= 2;
271 }
272 } else {
273 uint min = qMax<int>(a: 0, b: msec - absMaxRounding);
274 uint max = qMin(a: 1000u, b: msec + absMaxRounding);
275
276 // find the boundary that we want, according to the rules above
277 // extra rules:
278 // 1) whatever the interval, we'll take any round-to-the-second timeout
279 if (min == 0) {
280 msec = 0;
281 goto recalculate;
282 } else if (max == 1000) {
283 msec = 1000;
284 goto recalculate;
285 }
286
287 uint wantedBoundaryMultiple;
288
289 // 2) if the interval is a multiple of 500 ms and > 5000 ms, we'll always round
290 // towards a round-to-the-second
291 // 3) if the interval is a multiple of 500 ms, we'll round towards the nearest
292 // multiple of 500 ms
293 if ((interval % 500) == 0) {
294 if (interval >= 5000) {
295 msec = msec >= 500 ? max : min;
296 goto recalculate;
297 } else {
298 wantedBoundaryMultiple = 500;
299 }
300 } else if ((interval % 50) == 0) {
301 // 4) same for multiples of 250, 200, 100, 50
302 uint mult50 = interval / 50;
303 if ((mult50 % 4) == 0) {
304 // multiple of 200
305 wantedBoundaryMultiple = 200;
306 } else if ((mult50 % 2) == 0) {
307 // multiple of 100
308 wantedBoundaryMultiple = 100;
309 } else if ((mult50 % 5) == 0) {
310 // multiple of 250
311 wantedBoundaryMultiple = 250;
312 } else {
313 // multiple of 50
314 wantedBoundaryMultiple = 50;
315 }
316 } else {
317 wantedBoundaryMultiple = 25;
318 }
319
320 uint base = msec / wantedBoundaryMultiple * wantedBoundaryMultiple;
321 uint middlepoint = base + wantedBoundaryMultiple / 2;
322 if (msec < middlepoint)
323 msec = qMax(a: base, b: min);
324 else
325 msec = qMin(a: base + wantedBoundaryMultiple, b: max);
326 }
327
328recalculate:
329 if (msec == 1000u) {
330 ++t->timeout.tv_sec;
331 t->timeout.tv_nsec = 0;
332 } else {
333 t->timeout.tv_nsec = msec * 1000 * 1000;
334 }
335
336 if (t->timeout < currentTime)
337 t->timeout += interval;
338}
339
340static void calculateNextTimeout(QTimerInfo *t, timespec currentTime)
341{
342 switch (t->timerType) {
343 case Qt::PreciseTimer:
344 case Qt::CoarseTimer:
345 t->timeout += t->interval;
346 if (t->timeout < currentTime) {
347 t->timeout = currentTime;
348 t->timeout += t->interval;
349 }
350#ifdef QTIMERINFO_DEBUG
351 t->expected += t->interval;
352 if (t->expected < currentTime) {
353 t->expected = currentTime;
354 t->expected += t->interval;
355 }
356#endif
357 if (t->timerType == Qt::CoarseTimer)
358 calculateCoarseTimerTimeout(t, currentTime);
359 return;
360
361 case Qt::VeryCoarseTimer:
362 // we don't need to take care of the microsecond component of t->interval
363 t->timeout.tv_sec += t->interval;
364 if (t->timeout.tv_sec <= currentTime.tv_sec)
365 t->timeout.tv_sec = currentTime.tv_sec + t->interval;
366#ifdef QTIMERINFO_DEBUG
367 t->expected.tv_sec += t->interval;
368 if (t->expected.tv_sec <= currentTime.tv_sec)
369 t->expected.tv_sec = currentTime.tv_sec + t->interval;
370#endif
371 return;
372 }
373
374#ifdef QTIMERINFO_DEBUG
375 if (t->timerType != Qt::PreciseTimer)
376 qDebug() << "timer" << t->timerType << Qt::hex << t->id << Qt::dec << "interval" << t->interval
377 << "originally expected at" << t->expected << "will fire at" << t->timeout
378 << "or" << (t->timeout - t->expected) << "s late";
379#endif
380}
381
382/*
383 Returns the time to wait for the next timer, or null if no timers
384 are waiting.
385*/
386bool QTimerInfoList::timerWait(timespec &tm)
387{
388 timespec currentTime = updateCurrentTime();
389 repairTimersIfNeeded();
390
391 // Find first waiting timer not already active
392 QTimerInfo *t = nullptr;
393 for (QTimerInfoList::const_iterator it = constBegin(); it != constEnd(); ++it) {
394 if (!(*it)->activateRef) {
395 t = *it;
396 break;
397 }
398 }
399
400 if (!t)
401 return false;
402
403 if (currentTime < t->timeout) {
404 // time to wait
405 tm = roundToMillisecond(val: t->timeout - currentTime);
406 } else {
407 // no time to wait
408 tm.tv_sec = 0;
409 tm.tv_nsec = 0;
410 }
411
412 return true;
413}
414
415/*
416 Returns the timer's remaining time in milliseconds with the given timerId, or
417 null if there is nothing left. If the timer id is not found in the list, the
418 returned value will be -1. If the timer is overdue, the returned value will be 0.
419*/
420int QTimerInfoList::timerRemainingTime(int timerId)
421{
422 timespec currentTime = updateCurrentTime();
423 repairTimersIfNeeded();
424 timespec tm = {.tv_sec: 0, .tv_nsec: 0};
425
426 for (int i = 0; i < count(); ++i) {
427 QTimerInfo *t = at(i);
428 if (t->id == timerId) {
429 if (currentTime < t->timeout) {
430 // time to wait
431 tm = roundToMillisecond(val: t->timeout - currentTime);
432 return tm.tv_sec*1000 + tm.tv_nsec/1000/1000;
433 } else {
434 return 0;
435 }
436 }
437 }
438
439#ifndef QT_NO_DEBUG
440 qWarning(msg: "QTimerInfoList::timerRemainingTime: timer id %i not found", timerId);
441#endif
442
443 return -1;
444}
445
446void QTimerInfoList::registerTimer(int timerId, int interval, Qt::TimerType timerType, QObject *object)
447{
448 QTimerInfo *t = new QTimerInfo;
449 t->id = timerId;
450 t->interval = interval;
451 t->timerType = timerType;
452 t->obj = object;
453 t->activateRef = nullptr;
454
455 timespec expected = updateCurrentTime() + interval;
456
457 switch (timerType) {
458 case Qt::PreciseTimer:
459 // high precision timer is based on millisecond precision
460 // so no adjustment is necessary
461 t->timeout = expected;
462 break;
463
464 case Qt::CoarseTimer:
465 // this timer has up to 5% coarseness
466 // so our boundaries are 20 ms and 20 s
467 // below 20 ms, 5% inaccuracy is below 1 ms, so we convert to high precision
468 // above 20 s, 5% inaccuracy is above 1 s, so we convert to VeryCoarseTimer
469 if (interval >= 20000) {
470 t->timerType = Qt::VeryCoarseTimer;
471 } else {
472 t->timeout = expected;
473 if (interval <= 20) {
474 t->timerType = Qt::PreciseTimer;
475 // no adjustment is necessary
476 } else if (interval <= 20000) {
477 calculateCoarseTimerTimeout(t, currentTime);
478 }
479 break;
480 }
481 Q_FALLTHROUGH();
482 case Qt::VeryCoarseTimer:
483 // the very coarse timer is based on full second precision,
484 // so we keep the interval in seconds (round to closest second)
485 t->interval /= 500;
486 t->interval += 1;
487 t->interval >>= 1;
488 t->timeout.tv_sec = currentTime.tv_sec + t->interval;
489 t->timeout.tv_nsec = 0;
490
491 // if we're past the half-second mark, increase the timeout again
492 if (currentTime.tv_nsec > 500*1000*1000)
493 ++t->timeout.tv_sec;
494 }
495
496 timerInsert(ti: t);
497
498#ifdef QTIMERINFO_DEBUG
499 t->expected = expected;
500 t->cumulativeError = 0;
501 t->count = 0;
502 if (t->timerType != Qt::PreciseTimer)
503 qDebug() << "timer" << t->timerType << Qt::hex <<t->id << Qt::dec << "interval" << t->interval << "expected at"
504 << t->expected << "will fire first at" << t->timeout;
505#endif
506}
507
508bool QTimerInfoList::unregisterTimer(int timerId)
509{
510 // set timer inactive
511 for (int i = 0; i < count(); ++i) {
512 QTimerInfo *t = at(i);
513 if (t->id == timerId) {
514 // found it
515 removeAt(i);
516 if (t == firstTimerInfo)
517 firstTimerInfo = nullptr;
518 if (t->activateRef)
519 *(t->activateRef) = nullptr;
520 delete t;
521 return true;
522 }
523 }
524 // id not found
525 return false;
526}
527
528bool QTimerInfoList::unregisterTimers(QObject *object)
529{
530 if (isEmpty())
531 return false;
532 for (int i = 0; i < count(); ++i) {
533 QTimerInfo *t = at(i);
534 if (t->obj == object) {
535 // object found
536 removeAt(i);
537 if (t == firstTimerInfo)
538 firstTimerInfo = nullptr;
539 if (t->activateRef)
540 *(t->activateRef) = nullptr;
541 delete t;
542 // move back one so that we don't skip the new current item
543 --i;
544 }
545 }
546 return true;
547}
548
549QList<QAbstractEventDispatcher::TimerInfo> QTimerInfoList::registeredTimers(QObject *object) const
550{
551 QList<QAbstractEventDispatcher::TimerInfo> list;
552 for (int i = 0; i < count(); ++i) {
553 const QTimerInfo * const t = at(i);
554 if (t->obj == object) {
555 list << QAbstractEventDispatcher::TimerInfo(t->id,
556 (t->timerType == Qt::VeryCoarseTimer
557 ? t->interval * 1000
558 : t->interval),
559 t->timerType);
560 }
561 }
562 return list;
563}
564
565/*
566 Activate pending timers, returning how many where activated.
567*/
568int QTimerInfoList::activateTimers()
569{
570 if (qt_disable_lowpriority_timers || isEmpty())
571 return 0; // nothing to do
572
573 int n_act = 0, maxCount = 0;
574 firstTimerInfo = nullptr;
575
576 timespec currentTime = updateCurrentTime();
577 // qDebug() << "Thread" << QThread::currentThreadId() << "woken up at" << currentTime;
578 repairTimersIfNeeded();
579
580
581 // Find out how many timer have expired
582 for (QTimerInfoList::const_iterator it = constBegin(); it != constEnd(); ++it) {
583 if (currentTime < (*it)->timeout)
584 break;
585 maxCount++;
586 }
587
588 //fire the timers.
589 while (maxCount--) {
590 if (isEmpty())
591 break;
592
593 QTimerInfo *currentTimerInfo = constFirst();
594 if (currentTime < currentTimerInfo->timeout)
595 break; // no timer has expired
596
597 if (!firstTimerInfo) {
598 firstTimerInfo = currentTimerInfo;
599 } else if (firstTimerInfo == currentTimerInfo) {
600 // avoid sending the same timer multiple times
601 break;
602 } else if (currentTimerInfo->interval < firstTimerInfo->interval
603 || currentTimerInfo->interval == firstTimerInfo->interval) {
604 firstTimerInfo = currentTimerInfo;
605 }
606
607 // remove from list
608 removeFirst();
609
610#ifdef QTIMERINFO_DEBUG
611 float diff;
612 if (currentTime < currentTimerInfo->expected) {
613 // early
614 timeval early = currentTimerInfo->expected - currentTime;
615 diff = -(early.tv_sec + early.tv_usec / 1000000.0);
616 } else {
617 timeval late = currentTime - currentTimerInfo->expected;
618 diff = late.tv_sec + late.tv_usec / 1000000.0;
619 }
620 currentTimerInfo->cumulativeError += diff;
621 ++currentTimerInfo->count;
622 if (currentTimerInfo->timerType != Qt::PreciseTimer)
623 qDebug() << "timer" << currentTimerInfo->timerType << Qt::hex << currentTimerInfo->id << Qt::dec << "interval"
624 << currentTimerInfo->interval << "firing at" << currentTime
625 << "(orig" << currentTimerInfo->expected << "scheduled at" << currentTimerInfo->timeout
626 << ") off by" << diff << "activation" << currentTimerInfo->count
627 << "avg error" << (currentTimerInfo->cumulativeError / currentTimerInfo->count);
628#endif
629
630 // determine next timeout time
631 calculateNextTimeout(t: currentTimerInfo, currentTime);
632
633 // reinsert timer
634 timerInsert(ti: currentTimerInfo);
635 if (currentTimerInfo->interval > 0)
636 n_act++;
637
638 if (!currentTimerInfo->activateRef) {
639 // send event, but don't allow it to recurse
640 currentTimerInfo->activateRef = &currentTimerInfo;
641
642 QTimerEvent e(currentTimerInfo->id);
643 QCoreApplication::sendEvent(receiver: currentTimerInfo->obj, event: &e);
644
645 if (currentTimerInfo)
646 currentTimerInfo->activateRef = nullptr;
647 }
648 }
649
650 firstTimerInfo = nullptr;
651 // qDebug() << "Thread" << QThread::currentThreadId() << "activated" << n_act << "timers";
652 return n_act;
653}
654
655QT_END_NAMESPACE
656

source code of qtbase/src/corelib/kernel/qtimerinfo_unix.cpp