aboutsummaryrefslogtreecommitdiff
path: root/src/FbTk/Timer.cc
diff options
context:
space:
mode:
authorMathias Gumz <akira at fluxbox dot org>2012-08-28 08:51:55 (GMT)
committerMathias Gumz <akira at fluxbox dot org>2012-08-28 08:51:55 (GMT)
commit541c8c407b7ba8dd10f85bb48bcb5900270b3f84 (patch)
tree71a6abc0f2a43bcfd33f80b3b30b878f234cbf05 /src/FbTk/Timer.cc
parent60a53113e05db443af4d520883ec3145680642a8 (diff)
downloadfluxbox-541c8c407b7ba8dd10f85bb48bcb5900270b3f84.zip
fluxbox-541c8c407b7ba8dd10f85bb48bcb5900270b3f84.tar.bz2
changed timing functions to use a monotonic increasing clock
gettimeofday() is subject to be changed on daylight-saving or to ntp-related (think leap-seconds). even worse, it is subject to be changed BACK in time. this is hard to fix correctly (see commit 45726d3016e and bug #3560509). it is irrelevant for timers to know the nano-seconds since the epoch anyways.
Diffstat (limited to 'src/FbTk/Timer.cc')
-rw-r--r--src/FbTk/Timer.cc232
1 files changed, 87 insertions, 145 deletions
diff --git a/src/FbTk/Timer.cc b/src/FbTk/Timer.cc
index 8b144db..6a478bd 100644
--- a/src/FbTk/Timer.cc
+++ b/src/FbTk/Timer.cc
@@ -1,5 +1,5 @@
1// Timer.cc for FbTk - Fluxbox Toolkit 1// Timer.cc for FbTk - Fluxbox Toolkit
2// Copyright (c) 2003 - 2006 Henrik Kinnunen (fluxgen at fluxbox dot org) 2// Copyright (c) 2003 - 2012 Henrik Kinnunen (fluxgen at fluxbox dot org)
3// 3//
4// Timer.cc for Blackbox - An X11 Window Manager 4// Timer.cc for Blackbox - An X11 Window Manager
5// Copyright (c) 1997 - 2000 Brad Hughes (bhughes at tcac.net) 5// Copyright (c) 1997 - 2000 Brad Hughes (bhughes at tcac.net)
@@ -32,10 +32,6 @@
32#define _GNU_SOURCE 32#define _GNU_SOURCE
33#endif // _GNU_SOURCE 33#endif // _GNU_SOURCE
34 34
35#ifdef HAVE_CONFIG_H
36#include "config.h"
37#endif // HAVE_CONFIG_H
38
39#ifdef HAVE_CASSERT 35#ifdef HAVE_CASSERT
40 #include <cassert> 36 #include <cassert>
41#else 37#else
@@ -55,9 +51,48 @@
55# include <winsock.h> 51# include <winsock.h>
56#endif 52#endif
57 53
58namespace FbTk { 54#include <cstdio>
55#include <set>
56
57
58namespace {
59
60struct TimerCompare {
61 bool operator() (const FbTk::Timer* a, const FbTk::Timer* b) {
62 return a->getEndTime() < b->getEndTime();
63 }
64};
65typedef std::set<FbTk::Timer*, TimerCompare> TimerList;
66
67TimerList s_timerlist;
68
69
70/// add a timer to the static list
71void addTimer(FbTk::Timer *timer) {
72
73 assert(timer);
74 int interval = timer->getInterval();
75
76 // interval timers have their timeout change every time they are started!
77 if (interval != 0) {
78 timer->setTimeout(interval * FbTk::FbTime::IN_SECONDS);
79 }
80
81 s_timerlist.insert(timer);
82}
59 83
60Timer::TimerList Timer::m_timerlist; 84/// remove a timer from the static list
85void removeTimer(FbTk::Timer *timer) {
86
87 assert(timer);
88 s_timerlist.erase(timer);
89}
90
91
92}
93
94
95namespace FbTk {
61 96
62Timer::Timer():m_timing(false), m_once(false), m_interval(0) { 97Timer::Timer():m_timing(false), m_once(false), m_interval(0) {
63 98
@@ -76,22 +111,8 @@ Timer::~Timer() {
76} 111}
77 112
78 113
79void Timer::setTimeout(time_t t) { 114void Timer::setTimeout(uint64_t timeout) {
80 m_timeout.tv_sec = t / 1000; 115 m_timeout = timeout;
81 m_timeout.tv_usec = t;
82 m_timeout.tv_usec -= (m_timeout.tv_sec * 1000);
83 m_timeout.tv_usec *= 1000;
84}
85
86
87void Timer::setTimeout(const timeval &t) {
88 m_timeout.tv_sec = t.tv_sec;
89 m_timeout.tv_usec = t.tv_usec;
90}
91
92void Timer::setTimeout(unsigned int secs, unsigned int usecs) {
93 m_timeout.tv_sec = secs;
94 m_timeout.tv_usec = usecs;
95} 116}
96 117
97void Timer::setCommand(const RefCount<Slot<void> > &cmd) { 118void Timer::setCommand(const RefCount<Slot<void> > &cmd) {
@@ -99,28 +120,24 @@ void Timer::setCommand(const RefCount<Slot<void> > &cmd) {
99} 120}
100 121
101void Timer::start() { 122void Timer::start() {
102 gettimeofday(&m_start, 0); 123
124 m_start = FbTk::FbTime::now();
103 125
104 // only add Timers that actually DO something 126 // only add Timers that actually DO something
105 if ((! m_timing || m_interval != 0) && m_handler) { 127 if ((! m_timing || m_interval != 0) && m_handler) {
106 m_timing = true; 128 m_timing = true;
107 addTimer(this); //add us to the list 129 ::addTimer(this);
108 } 130 }
109} 131}
110 132
111 133
112void Timer::stop() { 134void Timer::stop() {
113 m_timing = false; 135 m_timing = false;
114 removeTimer(this); //remove us from the list 136 ::removeTimer(this);
115} 137}
116 138
117void Timer::makeEndTime(timeval &tm) const { 139uint64_t Timer::getEndTime() const {
118 tm.tv_sec = m_start.tv_sec + m_timeout.tv_sec; 140 return m_start + m_timeout;
119 tm.tv_usec = m_start.tv_usec + m_timeout.tv_usec;
120 if (tm.tv_usec >= 1000000) {
121 tm.tv_usec -= 1000000;
122 tm.tv_sec++;
123 }
124} 141}
125 142
126 143
@@ -129,42 +146,34 @@ void Timer::fireTimeout() {
129 (*m_handler)(); 146 (*m_handler)();
130} 147}
131 148
149
132void Timer::updateTimers(int fd) { 150void Timer::updateTimers(int fd) {
151
133 fd_set rfds; 152 fd_set rfds;
134 timeval now, tm, *timeout = 0; 153 timeval tm;
154 timeval* timeout = 0;
155 TimerList::iterator it;
135 156
136 FD_ZERO(&rfds); 157 FD_ZERO(&rfds);
137 FD_SET(fd, &rfds); 158 FD_SET(fd, &rfds);
138 159
139 bool overdue = false; 160 bool overdue = false;
161 uint64_t now = FbTime::now();
162 uint64_t end_time;
140 163
141 // see, if the first timer in the 164 // see, if the first timer in the
142 // list is overdue 165 // list is overdue
143 if (!m_timerlist.empty()) { 166 if (!s_timerlist.empty()) {
144 gettimeofday(&now, 0);
145
146 Timer *timer = m_timerlist.front();
147
148 timer->makeEndTime(tm);
149 167
150 tm.tv_sec -= now.tv_sec; 168 Timer* timer = *s_timerlist.begin();
151 tm.tv_usec -= now.tv_usec; 169 end_time = timer->getEndTime();
152 170
153 while (tm.tv_usec < 0) { 171 if (end_time < now) {
154 if (tm.tv_sec > 0) {
155 tm.tv_sec--;
156 tm.tv_usec += 1000000;
157 } else {
158 overdue = true;
159 tm.tv_usec = 0;
160 break;
161 }
162 }
163
164 if (tm.tv_sec < 0) { // usec zero-ed above if negative
165 tm.tv_sec = 0;
166 tm.tv_usec = 0;
167 overdue = true; 172 overdue = true;
173 } else {
174 uint64_t diff = (end_time - now);
175 tm.tv_sec = diff / FbTime::IN_SECONDS;
176 tm.tv_usec = diff % FbTime::IN_SECONDS;
168 } 177 }
169 178
170 timeout = &tm; 179 timeout = &tm;
@@ -173,100 +182,41 @@ void Timer::updateTimers(int fd) {
173 // if not overdue, wait for the next xevent via the blocking 182 // if not overdue, wait for the next xevent via the blocking
174 // select(), so OS sends fluxbox to sleep. the select() will 183 // select(), so OS sends fluxbox to sleep. the select() will
175 // time out when the next timer has to be handled 184 // time out when the next timer has to be handled
176 if (!overdue && select(fd + 1, &rfds, 0, 0, timeout) != 0) 185 if (!overdue && select(fd + 1, &rfds, 0, 0, timeout) != 0) {
177 // didn't time out! x events are pending 186 // didn't time out! x events are pending
178 return; 187 return;
179
180 TimerList::iterator it;
181
182 // check for timer timeout
183 gettimeofday(&now, 0);
184
185 // someone set the date of the machine BACK
186 // so we have to adjust the start_time
187 static time_t last_time = 0;
188 if (now.tv_sec < last_time) {
189
190 time_t delta = last_time - now.tv_sec;
191
192 for (it = m_timerlist.begin(); it != m_timerlist.end(); ++it) {
193 (*it)->m_start.tv_sec -= delta;
194 }
195 } 188 }
196 last_time = now.tv_sec;
197
198 189
199 //must check end ...the timer might remove 190 now = FbTime::now();
200 //it self from the list (should be fixed in the future) 191 for (it = s_timerlist.begin(); it != s_timerlist.end(); ) {
201 for(it = m_timerlist.begin(); it != m_timerlist.end(); ) {
202 //This is to make sure we don't get an invalid iterator
203 //when we do fireTimeout
204 Timer &t = *(*it);
205 192
206 t.makeEndTime(tm); 193 // t->fireTimeout() might add timers to the list
207 194 // this invalidates 'it'. thus we store the current
208 if (((now.tv_sec < tm.tv_sec) || 195 // item here
209 (now.tv_sec == tm.tv_sec && now.tv_usec < tm.tv_usec))) 196 Timer* t = *it;
197 if (now < t->getEndTime()) {
210 break; 198 break;
211
212 t.fireTimeout();
213 // restart the current timer so that the start time is updated
214 if (! t.doOnce()) {
215 // must erase so that it's put into the right place in the list
216 it = m_timerlist.erase(it);
217 t.m_timing = false;
218 t.start();
219 } else {
220 // Since the default stop behaviour results in the timer
221 // being removed, we must remove it here, so that the iterator
222 // lives well. Another option would be to add it to another
223 // list, and then just go through that list and stop them all.
224 it = m_timerlist.erase(it);
225 t.stop();
226 } 199 }
227 }
228 200
229} 201 t->fireTimeout();
230 202
231void Timer::addTimer(Timer *timer) { 203 // find the iterator to the timer again
232 assert(timer); 204 // and continue working on the list
233 int interval = timer->getInterval(); 205 it = s_timerlist.find(t);
234 // interval timers have their timeout change every time they are started! 206 it++;
235 timeval tm; 207 s_timerlist.erase(t);
236 if (interval != 0) {
237 tm.tv_sec = timer->getStartTime().tv_sec;
238 tm.tv_usec = timer->getStartTime().tv_usec;
239
240 // now convert to interval
241 tm.tv_sec = interval - (tm.tv_sec % interval) - 1;
242 tm.tv_usec = 1000000 - tm.tv_usec;
243 if (tm.tv_usec == 1000000) {
244 tm.tv_usec = 0;
245 tm.tv_sec += 1;
246 }
247 timer->setTimeout(tm);
248 }
249
250 // set timeval to the time-of-trigger
251 timer->makeEndTime(tm);
252 208
253 // timer list is sorted by trigger time (i.e. start plus timeout) 209 if (! t->doOnce()) { // restart the current timer
254 TimerList::iterator it = m_timerlist.begin(); 210 t->m_timing = false;
255 TimerList::iterator it_end = m_timerlist.end(); 211 t->start();
256 for (; it != it_end; ++it) { 212 } else {
257 timeval trig; 213 t->stop();
258 (*it)->makeEndTime(trig);
259
260 if ((trig.tv_sec > tm.tv_sec) ||
261 (trig.tv_sec == tm.tv_sec &&
262 trig.tv_usec >= tm.tv_usec)) {
263 break;
264 } 214 }
265 } 215 }
266 m_timerlist.insert(it, timer);
267 216
268} 217}
269 218
219
270Command<void> *DelayedCmd::parse(const std::string &command, 220Command<void> *DelayedCmd::parse(const std::string &command,
271 const std::string &args, bool trusted) { 221 const std::string &args, bool trusted) {
272 222
@@ -280,7 +230,7 @@ Command<void> *DelayedCmd::parse(const std::string &command,
280 if (cmd == 0) 230 if (cmd == 0)
281 return 0; 231 return 0;
282 232
283 int delay = 200000; 233 int delay = 200;
284 StringUtil::fromString<int>(args.c_str() + err, delay); 234 StringUtil::fromString<int>(args.c_str() + err, delay);
285 235
286 return new DelayedCmd(cmd, delay); 236 return new DelayedCmd(cmd, delay);
@@ -294,10 +244,7 @@ DelayedCmd::DelayedCmd(const RefCount<Slot<void> > &cmd, unsigned int timeout) {
294} 244}
295 245
296void DelayedCmd::initTimer(unsigned int timeout) { 246void DelayedCmd::initTimer(unsigned int timeout) {
297 timeval to; 247 m_timer.setTimeout(timeout * FbTime::IN_MILLISECONDS);
298 to.tv_sec = timeout/1000000;
299 to.tv_usec = timeout % 1000000;
300 m_timer.setTimeout(to);
301 m_timer.fireOnce(true); 248 m_timer.fireOnce(true);
302} 249}
303 250
@@ -307,9 +254,4 @@ void DelayedCmd::execute() {
307 m_timer.start(); 254 m_timer.start();
308} 255}
309 256
310void Timer::removeTimer(Timer *timer) {
311 assert(timer);
312 m_timerlist.remove(timer);
313}
314
315} // end namespace FbTk 257} // end namespace FbTk