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