1 /* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
2  * Copyright 2016-2018 Pierre Ossman for Cendio AB
3  *
4  * This is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This software is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this software; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
17  * USA.
18  */
19 
20 // -=- Timer.cxx
21 
22 #include <stdio.h>
23 #include <sys/time.h>
24 
25 #include <rfb/Timer.h>
26 #include <rfb/util.h>
27 #include <rfb/LogWriter.h>
28 
29 using namespace rfb;
30 
31 #ifndef __NO_DEFINE_VLOG__
32 static LogWriter vlog("Timer");
33 #endif
34 
35 
36 // Millisecond timeout processing helper functions
37 
addMillis(timeval inTime,int millis)38 inline static timeval addMillis(timeval inTime, int millis) {
39   int secs = millis / 1000;
40   millis = millis % 1000;
41   inTime.tv_sec += secs;
42   inTime.tv_usec += millis * 1000;
43   if (inTime.tv_usec >= 1000000) {
44     inTime.tv_sec++;
45     inTime.tv_usec -= 1000000;
46   }
47   return inTime;
48 }
49 
diffTimeMillis(timeval later,timeval earlier)50 inline static int diffTimeMillis(timeval later, timeval earlier) {
51   return ((later.tv_sec - earlier.tv_sec) * 1000) + ((later.tv_usec - earlier.tv_usec) / 1000);
52 }
53 
54 std::list<Timer*> Timer::pending;
55 
checkTimeouts()56 int Timer::checkTimeouts() {
57   timeval start;
58 
59   if (pending.empty())
60     return 0;
61 
62   gettimeofday(&start, 0);
63   while (pending.front()->isBefore(start)) {
64     Timer* timer;
65     timeval before;
66 
67     timer = pending.front();
68     pending.pop_front();
69 
70     gettimeofday(&before, 0);
71     if (timer->cb->handleTimeout(timer)) {
72       timeval now;
73 
74       gettimeofday(&now, 0);
75 
76       timer->dueTime = addMillis(timer->dueTime, timer->timeoutMs);
77       if (timer->isBefore(now)) {
78         // Time has jumped forwards, or we're not getting enough
79         // CPU time for the timers
80 
81         timer->dueTime = addMillis(before, timer->timeoutMs);
82         if (timer->isBefore(now))
83           timer->dueTime = now;
84       }
85 
86       insertTimer(timer);
87     } else if (pending.empty()) {
88       return 0;
89     }
90   }
91   return getNextTimeout();
92 }
93 
getNextTimeout()94 int Timer::getNextTimeout() {
95   timeval now;
96   gettimeofday(&now, 0);
97   int toWait = __rfbmax(1, pending.front()->getRemainingMs());
98   if (toWait > pending.front()->timeoutMs) {
99     if (toWait - pending.front()->timeoutMs < 1000) {
100       vlog.info("gettimeofday is broken...");
101       return toWait;
102     }
103     // Time has jumped backwards!
104     vlog.info("time has moved backwards!");
105     pending.front()->dueTime = now;
106     toWait = 1;
107   }
108   return toWait;
109 }
110 
insertTimer(Timer * t)111 void Timer::insertTimer(Timer* t) {
112   std::list<Timer*>::iterator i;
113   for (i=pending.begin(); i!=pending.end(); i++) {
114     if (t->isBefore((*i)->dueTime)) {
115       pending.insert(i, t);
116       return;
117     }
118   }
119   pending.push_back(t);
120 }
121 
start(int timeoutMs_)122 void Timer::start(int timeoutMs_) {
123   timeval now;
124   gettimeofday(&now, 0);
125   stop();
126   timeoutMs = timeoutMs_;
127   // The rest of the code assumes non-zero timeout
128   if (timeoutMs <= 0)
129     timeoutMs = 1;
130   dueTime = addMillis(now, timeoutMs);
131   insertTimer(this);
132 }
133 
stop()134 void Timer::stop() {
135   pending.remove(this);
136 }
137 
isStarted()138 bool Timer::isStarted() {
139   std::list<Timer*>::iterator i;
140   for (i=pending.begin(); i!=pending.end(); i++) {
141     if (*i == this)
142       return true;
143   }
144   return false;
145 }
146 
getTimeoutMs()147 int Timer::getTimeoutMs() {
148   return timeoutMs;
149 }
150 
getRemainingMs()151 int Timer::getRemainingMs() {
152   timeval now;
153   gettimeofday(&now, 0);
154   return __rfbmax(0, diffTimeMillis(dueTime, now));
155 }
156 
isBefore(timeval other)157 bool Timer::isBefore(timeval other) {
158   return (dueTime.tv_sec < other.tv_sec) ||
159     ((dueTime.tv_sec == other.tv_sec) &&
160      (dueTime.tv_usec < other.tv_usec));
161 }
162