Fix bug: handle the list of Timers not in-place

With commit 541c8c4 we switched from an (manually) ordered list to a
std::set<> to handle the active timers. The code which checks for overdue
timers now traverses and modifies the std::set<> in place. This might
lead to an infinite loop. Examples of such bad behavior are "flickering of
the tooltip" (bug #3590078) or crashes (bug #3600143) or just insanely high
cpu load when autoraising windows or submenus.

We now make a copy of the std::set<> traverse this instead of the original.
This commit is contained in:
Mathias Gumz 2013-01-12 09:24:11 +01:00
parent 06655f6d7f
commit 4d307dcd10

View file

@ -52,6 +52,7 @@
#endif #endif
#include <cstdio> #include <cstdio>
#include <vector>
#include <set> #include <set>
@ -195,32 +196,35 @@ void Timer::updateTimers(int fd) {
return; return;
} }
now = FbTime::now(); // stoping / restarting the timers modifies the list in an upredictable
for (it = s_timerlist.begin(); it != s_timerlist.end(); ) { // way. to avoid problems such as infinite loops we save the current
// (ordered) list of timers into a list and work on it.
ssize_t i;
const ssize_t ts = s_timerlist.size();
std::vector<FbTk::Timer*> timers;
timers.reserve(ts);
for (it = s_timerlist.begin(); it != s_timerlist.end(); ++it ) {
timers.push_back(*it);
}
now = FbTime::now();
for (i = 0; i < ts; ++i) {
FbTk::Timer* t = timers[i];
// t->fireTimeout() might add timers to the list
// this invalidates 'it'. thus we store the current timer
Timer* t = *it;
if (now < t->getEndTime()) { if (now < t->getEndTime()) {
break; break;
} }
t->fireTimeout(); t->fireTimeout();
t->stop();
// find the iterator to the timer again
// and continue working on the list
it = s_timerlist.find(t);
it++;
s_timerlist.erase(t);
if (! t->doOnce()) { // restart the current timer if (! t->doOnce()) { // restart the current timer
t->m_timing = false;
t->start(); t->start();
} else {
t->stop();
} }
} }
} }