• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2006, 2008 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25 
26 #include "config.h"
27 #include "Timer.h"
28 
29 #include "SharedTimer.h"
30 #include <limits.h>
31 #include <limits>
32 #include <math.h>
33 #include <wtf/CurrentTime.h>
34 #include <wtf/HashSet.h>
35 #include <wtf/Vector.h>
36 
37 using namespace std;
38 
39 namespace WebCore {
40 
41 // Timers are stored in a heap data structure, used to implement a priority queue.
42 // This allows us to efficiently determine which timer needs to fire the soonest.
43 // Then we set a single shared system timer to fire at that time.
44 //
45 // When a timer's "next fire time" changes, we need to move it around in the priority queue.
46 
47 // ----------------
48 
49 #ifdef ANDROID_FIX // it is removed in http://trac.webkit.org/changeset/40080, but Android needs it
50 static bool deferringTimers;
51 #endif
52 static Vector<TimerBase*>* timerHeap;
53 static HashSet<const TimerBase*>* timersReadyToFire;
54 
55 // ----------------
56 
57 // Class to represent elements in the heap when calling the standard library heap algorithms.
58 // Maintains the m_heapIndex value in the timers themselves, which allows us to do efficient
59 // modification of the heap.
60 class TimerHeapElement {
61 public:
TimerHeapElement(int i)62     explicit TimerHeapElement(int i) : m_index(i), m_timer((*timerHeap)[m_index]) { checkConsistency(); }
63 
64     TimerHeapElement(const TimerHeapElement&);
65     TimerHeapElement& operator=(const TimerHeapElement&);
66 
timer() const67     TimerBase* timer() const { return m_timer; }
68 
checkConsistency() const69     void checkConsistency() const
70     {
71         ASSERT(m_index >= 0);
72         ASSERT(m_index < (timerHeap ? static_cast<int>(timerHeap->size()) : 0));
73     }
74 
75 private:
76     TimerHeapElement();
77 
78     int m_index;
79     TimerBase* m_timer;
80 };
81 
TimerHeapElement(const TimerHeapElement & o)82 inline TimerHeapElement::TimerHeapElement(const TimerHeapElement& o)
83     : m_index(-1), m_timer(o.timer())
84 {
85 }
86 
operator =(const TimerHeapElement & o)87 inline TimerHeapElement& TimerHeapElement::operator=(const TimerHeapElement& o)
88 {
89     TimerBase* t = o.timer();
90     m_timer = t;
91     if (m_index != -1) {
92         checkConsistency();
93         (*timerHeap)[m_index] = t;
94         t->m_heapIndex = m_index;
95     }
96     return *this;
97 }
98 
operator <(const TimerHeapElement & a,const TimerHeapElement & b)99 inline bool operator<(const TimerHeapElement& a, const TimerHeapElement& b)
100 {
101     // The comparisons below are "backwards" because the heap puts the largest
102     // element first and we want the lowest time to be the first one in the heap.
103     double aFireTime = a.timer()->m_nextFireTime;
104     double bFireTime = b.timer()->m_nextFireTime;
105     if (bFireTime != aFireTime)
106         return bFireTime < aFireTime;
107 
108     // We need to look at the difference of the insertion orders instead of comparing the two
109     // outright in case of overflow.
110     unsigned difference = a.timer()->m_heapInsertionOrder - b.timer()->m_heapInsertionOrder;
111     return difference < UINT_MAX / 2;
112 }
113 
114 // ----------------
115 
116 // Class to represent iterators in the heap when calling the standard library heap algorithms.
117 // Returns TimerHeapElement for elements in the heap rather than the TimerBase pointers themselves.
118 class TimerHeapIterator : public iterator<random_access_iterator_tag, TimerHeapElement, int> {
119 public:
TimerHeapIterator()120     TimerHeapIterator() : m_index(-1) { }
TimerHeapIterator(int i)121     TimerHeapIterator(int i) : m_index(i) { checkConsistency(); }
122 
operator ++()123     TimerHeapIterator& operator++() { checkConsistency(); ++m_index; checkConsistency(); return *this; }
operator ++(int)124     TimerHeapIterator operator++(int) { checkConsistency(); checkConsistency(1); return m_index++; }
125 
operator --()126     TimerHeapIterator& operator--() { checkConsistency(); --m_index; checkConsistency(); return *this; }
operator --(int)127     TimerHeapIterator operator--(int) { checkConsistency(); checkConsistency(-1); return m_index--; }
128 
operator +=(int i)129     TimerHeapIterator& operator+=(int i) { checkConsistency(); m_index += i; checkConsistency(); return *this; }
operator -=(int i)130     TimerHeapIterator& operator-=(int i) { checkConsistency(); m_index -= i; checkConsistency(); return *this; }
131 
operator *() const132     TimerHeapElement operator*() const { return TimerHeapElement(m_index); }
operator [](int i) const133     TimerHeapElement operator[](int i) const { return TimerHeapElement(m_index + i); }
134 
index() const135     int index() const { return m_index; }
136 
checkConsistency(int offset=0) const137     void checkConsistency(int offset = 0) const
138     {
139         ASSERT_UNUSED(offset, m_index + offset >= 0);
140         ASSERT_UNUSED(offset, m_index + offset <= (timerHeap ? static_cast<int>(timerHeap->size()) : 0));
141     }
142 
143 private:
144     int m_index;
145 };
146 
operator ==(TimerHeapIterator a,TimerHeapIterator b)147 inline bool operator==(TimerHeapIterator a, TimerHeapIterator b) { return a.index() == b.index(); }
operator !=(TimerHeapIterator a,TimerHeapIterator b)148 inline bool operator!=(TimerHeapIterator a, TimerHeapIterator b) { return a.index() != b.index(); }
operator <(TimerHeapIterator a,TimerHeapIterator b)149 inline bool operator<(TimerHeapIterator a, TimerHeapIterator b) { return a.index() < b.index(); }
150 
operator +(TimerHeapIterator a,int b)151 inline TimerHeapIterator operator+(TimerHeapIterator a, int b) { return a.index() + b; }
operator +(int a,TimerHeapIterator b)152 inline TimerHeapIterator operator+(int a, TimerHeapIterator b) { return a + b.index(); }
153 
operator -(TimerHeapIterator a,int b)154 inline TimerHeapIterator operator-(TimerHeapIterator a, int b) { return a.index() - b; }
operator -(TimerHeapIterator a,TimerHeapIterator b)155 inline int operator-(TimerHeapIterator a, TimerHeapIterator b) { return a.index() - b.index(); }
156 
157 // ----------------
158 
updateSharedTimer()159 void updateSharedTimer()
160 {
161 #ifdef ANDROID_FIX // it is removed in http://trac.webkit.org/changeset/40080, but Android needs it
162     if (timersReadyToFire || deferringTimers || !timerHeap || timerHeap->isEmpty())
163 #else
164     if (timersReadyToFire || !timerHeap || timerHeap->isEmpty())
165 #endif
166         stopSharedTimer();
167     else
168         setSharedTimerFireTime(timerHeap->first()->m_nextFireTime);
169 }
170 
171 // ----------------
172 
TimerBase()173 TimerBase::TimerBase()
174     : m_nextFireTime(0), m_repeatInterval(0), m_heapIndex(-1)
175 {
176     // We only need to do this once, but probably not worth trying to optimize it.
177     setSharedTimerFiredFunction(sharedTimerFired);
178 }
179 
~TimerBase()180 TimerBase::~TimerBase()
181 {
182     stop();
183 
184     ASSERT(!inHeap());
185 }
186 
start(double nextFireInterval,double repeatInterval)187 void TimerBase::start(double nextFireInterval, double repeatInterval)
188 {
189     m_repeatInterval = repeatInterval;
190     setNextFireTime(currentTime() + nextFireInterval);
191 }
192 
stop()193 void TimerBase::stop()
194 {
195     m_repeatInterval = 0;
196     setNextFireTime(0);
197 
198     ASSERT(m_nextFireTime == 0);
199     ASSERT(m_repeatInterval == 0);
200     ASSERT(!inHeap());
201 }
202 
isActive() const203 bool TimerBase::isActive() const
204 {
205     return m_nextFireTime || (timersReadyToFire && timersReadyToFire->contains(this));
206 }
207 
nextFireInterval() const208 double TimerBase::nextFireInterval() const
209 {
210     ASSERT(isActive());
211     double current = currentTime();
212     if (m_nextFireTime < current)
213         return 0;
214     return m_nextFireTime - current;
215 }
216 
checkHeapIndex() const217 inline void TimerBase::checkHeapIndex() const
218 {
219     ASSERT(timerHeap);
220     ASSERT(!timerHeap->isEmpty());
221     ASSERT(m_heapIndex >= 0);
222     ASSERT(m_heapIndex < static_cast<int>(timerHeap->size()));
223     ASSERT((*timerHeap)[m_heapIndex] == this);
224 }
225 
checkConsistency() const226 inline void TimerBase::checkConsistency() const
227 {
228     // Timers should be in the heap if and only if they have a non-zero next fire time.
229     ASSERT(inHeap() == (m_nextFireTime != 0));
230     if (inHeap())
231         checkHeapIndex();
232 }
233 
heapDecreaseKey()234 void TimerBase::heapDecreaseKey()
235 {
236     ASSERT(m_nextFireTime != 0);
237     checkHeapIndex();
238     push_heap(TimerHeapIterator(0), TimerHeapIterator(m_heapIndex + 1));
239     checkHeapIndex();
240 }
241 
heapDelete()242 inline void TimerBase::heapDelete()
243 {
244     ASSERT(m_nextFireTime == 0);
245     heapPop();
246     timerHeap->removeLast();
247     m_heapIndex = -1;
248 }
249 
heapDeleteMin()250 inline void TimerBase::heapDeleteMin()
251 {
252     ASSERT(m_nextFireTime == 0);
253     heapPopMin();
254     timerHeap->removeLast();
255     m_heapIndex = -1;
256 }
257 
heapIncreaseKey()258 inline void TimerBase::heapIncreaseKey()
259 {
260     ASSERT(m_nextFireTime != 0);
261     heapPop();
262     heapDecreaseKey();
263 }
264 
heapInsert()265 inline void TimerBase::heapInsert()
266 {
267     ASSERT(!inHeap());
268     if (!timerHeap)
269         timerHeap = new Vector<TimerBase*>;
270     timerHeap->append(this);
271     m_heapIndex = timerHeap->size() - 1;
272     heapDecreaseKey();
273 }
274 
heapPop()275 inline void TimerBase::heapPop()
276 {
277     // Temporarily force this timer to have the minimum key so we can pop it.
278     double fireTime = m_nextFireTime;
279     m_nextFireTime = -numeric_limits<double>::infinity();
280     heapDecreaseKey();
281     heapPopMin();
282     m_nextFireTime = fireTime;
283 }
284 
heapPopMin()285 void TimerBase::heapPopMin()
286 {
287     ASSERT(this == timerHeap->first());
288     checkHeapIndex();
289     pop_heap(TimerHeapIterator(0), TimerHeapIterator(timerHeap->size()));
290     checkHeapIndex();
291     ASSERT(this == timerHeap->last());
292 }
293 
setNextFireTime(double newTime)294 void TimerBase::setNextFireTime(double newTime)
295 {
296     // Keep heap valid while changing the next-fire time.
297 
298     if (timersReadyToFire)
299         timersReadyToFire->remove(this);
300 
301     double oldTime = m_nextFireTime;
302     if (oldTime != newTime) {
303         m_nextFireTime = newTime;
304         static unsigned currentHeapInsertionOrder;
305         m_heapInsertionOrder = currentHeapInsertionOrder++;
306 
307         bool wasFirstTimerInHeap = m_heapIndex == 0;
308 
309         if (oldTime == 0)
310             heapInsert();
311         else if (newTime == 0)
312             heapDelete();
313         else if (newTime < oldTime)
314             heapDecreaseKey();
315         else
316             heapIncreaseKey();
317 
318         bool isFirstTimerInHeap = m_heapIndex == 0;
319 
320         if (wasFirstTimerInHeap || isFirstTimerInHeap)
321             updateSharedTimer();
322     }
323 
324     checkConsistency();
325 }
326 
collectFiringTimers(double fireTime,Vector<TimerBase * > & firingTimers)327 void TimerBase::collectFiringTimers(double fireTime, Vector<TimerBase*>& firingTimers)
328 {
329     while (!timerHeap->isEmpty() && timerHeap->first()->m_nextFireTime <= fireTime) {
330         TimerBase* timer = timerHeap->first();
331         firingTimers.append(timer);
332         timersReadyToFire->add(timer);
333         timer->m_nextFireTime = 0;
334         timer->heapDeleteMin();
335     }
336 }
337 
fireTimers(double fireTime,const Vector<TimerBase * > & firingTimers)338 void TimerBase::fireTimers(double fireTime, const Vector<TimerBase*>& firingTimers)
339 {
340     int size = firingTimers.size();
341     for (int i = 0; i != size; ++i) {
342         TimerBase* timer = firingTimers[i];
343 
344         // If not in the set, this timer has been deleted or re-scheduled in another timer's fired function.
345         // So either we don't want to fire it at all or we will fire it next time the shared timer goes off.
346         // It might even have been deleted; that's OK because we won't do anything else with the pointer.
347         if (!timersReadyToFire->contains(timer))
348             continue;
349 
350         // Setting the next fire time has a side effect of removing the timer from the firing timers set.
351         double interval = timer->repeatInterval();
352         timer->setNextFireTime(interval ? fireTime + interval : 0);
353 
354         // Once the timer has been fired, it may be deleted, so do nothing else with it after this point.
355         timer->fired();
356 
357         // Catch the case where the timer asked timers to fire in a nested event loop.
358         if (!timersReadyToFire)
359             break;
360     }
361 }
362 
sharedTimerFired()363 void TimerBase::sharedTimerFired()
364 {
365     // Do a re-entrancy check.
366     if (timersReadyToFire)
367         return;
368 
369     double fireTime = currentTime();
370     Vector<TimerBase*> firingTimers;
371     HashSet<const TimerBase*> firingTimersSet;
372 
373     timersReadyToFire = &firingTimersSet;
374 
375     collectFiringTimers(fireTime, firingTimers);
376     fireTimers(fireTime, firingTimers);
377 
378     timersReadyToFire = 0;
379 
380     updateSharedTimer();
381 }
382 
fireTimersInNestedEventLoop()383 void TimerBase::fireTimersInNestedEventLoop()
384 {
385     timersReadyToFire = 0;
386     updateSharedTimer();
387 }
388 
389 #ifdef ANDROID_FIX // it is removed in http://trac.webkit.org/changeset/40080, but Android needs it
390 // ----------------
391 
isDeferringTimers()392 bool isDeferringTimers()
393 {
394     return deferringTimers;
395 }
396 
setDeferringTimers(bool shouldDefer)397 void setDeferringTimers(bool shouldDefer)
398 {
399     if (shouldDefer == deferringTimers)
400         return;
401     deferringTimers = shouldDefer;
402     updateSharedTimer();
403 }
404 #endif
405 
406 }
407