OLD | NEW |
1 /* | 1 /* |
2 * Copyright (C) 2006, 2008 Apple Inc. All rights reserved. | 2 * Copyright (C) 2006, 2008 Apple Inc. All rights reserved. |
3 * Copyright (C) 2009 Google Inc. All rights reserved. | 3 * Copyright (C) 2009 Google Inc. All rights reserved. |
4 * | 4 * |
5 * Redistribution and use in source and binary forms, with or without | 5 * Redistribution and use in source and binary forms, with or without |
6 * modification, are permitted provided that the following conditions | 6 * modification, are permitted provided that the following conditions |
7 * are met: | 7 * are met: |
8 * 1. Redistributions of source code must retain the above copyright | 8 * 1. Redistributions of source code must retain the above copyright |
9 * notice, this list of conditions and the following disclaimer. | 9 * notice, this list of conditions and the following disclaimer. |
10 * 2. Redistributions in binary form must reproduce the above copyright | 10 * 2. Redistributions in binary form must reproduce the above copyright |
11 * notice, this list of conditions and the following disclaimer in the | 11 * notice, this list of conditions and the following disclaimer in the |
12 * documentation and/or other materials provided with the distribution. | 12 * documentation and/or other materials provided with the distribution. |
13 * | 13 * |
14 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY | 14 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY |
15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | 15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR | 17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR |
18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | 18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | 19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | 20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY | 21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
25 */ | 25 */ |
26 | 26 |
27 #include "config.h" | 27 #include "config.h" |
28 #include "platform/Timer.h" | 28 #include "platform/Timer.h" |
29 | 29 |
30 #include "platform/PlatformThreadData.h" | 30 #include "platform/scheduler/Scheduler.h" |
31 #include "platform/ThreadTimers.h" | 31 #include "public/platform/Platform.h" |
32 #include "wtf/Atomics.h" | 32 #include "wtf/Atomics.h" |
33 #include "wtf/CurrentTime.h" | 33 #include "wtf/CurrentTime.h" |
34 #include "wtf/HashSet.h" | 34 #include "wtf/HashSet.h" |
| 35 #include "wtf/MainThread.h" |
35 #include <limits.h> | 36 #include <limits.h> |
36 #include <math.h> | 37 #include <math.h> |
37 #include <limits> | 38 #include <limits> |
38 | 39 |
39 namespace blink { | 40 namespace blink { |
40 | 41 |
41 class TimerHeapReference; | |
42 | |
43 // Timers are stored in a heap data structure, used to implement a priority queu
e. | |
44 // This allows us to efficiently determine which timer needs to fire the soonest
. | |
45 // Then we set a single shared system timer to fire at that time. | |
46 // | |
47 // When a timer's "next fire time" changes, we need to move it around in the pri
ority queue. | |
48 static Vector<TimerBase*>& threadGlobalTimerHeap() | |
49 { | |
50 return PlatformThreadData::current().threadTimers().timerHeap(); | |
51 } | |
52 // ---------------- | |
53 | |
54 class TimerHeapPointer { | |
55 public: | |
56 TimerHeapPointer(TimerBase** pointer) : m_pointer(pointer) { } | |
57 TimerHeapReference operator*() const; | |
58 TimerBase* operator->() const { return *m_pointer; } | |
59 private: | |
60 TimerBase** m_pointer; | |
61 }; | |
62 | |
63 class TimerHeapReference { | |
64 public: | |
65 TimerHeapReference(TimerBase*& reference) : m_reference(reference) { } | |
66 operator TimerBase*() const { return m_reference; } | |
67 TimerHeapPointer operator&() const { return &m_reference; } | |
68 TimerHeapReference& operator=(TimerBase*); | |
69 TimerHeapReference& operator=(TimerHeapReference); | |
70 private: | |
71 TimerBase*& m_reference; | |
72 }; | |
73 | |
74 inline TimerHeapReference TimerHeapPointer::operator*() const | |
75 { | |
76 return *m_pointer; | |
77 } | |
78 | |
79 inline TimerHeapReference& TimerHeapReference::operator=(TimerBase* timer) | |
80 { | |
81 m_reference = timer; | |
82 Vector<TimerBase*>& heap = timer->timerHeap(); | |
83 if (&m_reference >= heap.data() && &m_reference < heap.data() + heap.size()) | |
84 timer->m_heapIndex = &m_reference - heap.data(); | |
85 return *this; | |
86 } | |
87 | |
88 inline TimerHeapReference& TimerHeapReference::operator=(TimerHeapReference b) | |
89 { | |
90 TimerBase* timer = b; | |
91 return *this = timer; | |
92 } | |
93 | |
94 inline void swap(TimerHeapReference a, TimerHeapReference b) | |
95 { | |
96 TimerBase* timerA = a; | |
97 TimerBase* timerB = b; | |
98 | |
99 // Invoke the assignment operator, since that takes care of updating m_heapI
ndex. | |
100 a = timerB; | |
101 b = timerA; | |
102 } | |
103 | |
104 // ---------------- | |
105 | |
106 // Class to represent iterators in the heap when calling the standard library he
ap algorithms. | |
107 // Uses a custom pointer and reference type that update indices for pointers in
the heap. | |
108 class TimerHeapIterator : public std::iterator<std::random_access_iterator_tag,
TimerBase*, ptrdiff_t, TimerHeapPointer, TimerHeapReference> { | |
109 public: | |
110 explicit TimerHeapIterator(TimerBase** pointer) : m_pointer(pointer) { check
Consistency(); } | |
111 | |
112 TimerHeapIterator& operator++() { checkConsistency(); ++m_pointer; checkCons
istency(); return *this; } | |
113 TimerHeapIterator operator++(int) { checkConsistency(1); return TimerHeapIte
rator(m_pointer++); } | |
114 | |
115 TimerHeapIterator& operator--() { checkConsistency(); --m_pointer; checkCons
istency(); return *this; } | |
116 TimerHeapIterator operator--(int) { checkConsistency(-1); return TimerHeapIt
erator(m_pointer--); } | |
117 | |
118 TimerHeapIterator& operator+=(ptrdiff_t i) { checkConsistency(); m_pointer +
= i; checkConsistency(); return *this; } | |
119 TimerHeapIterator& operator-=(ptrdiff_t i) { checkConsistency(); m_pointer -
= i; checkConsistency(); return *this; } | |
120 | |
121 TimerHeapReference operator*() const { return TimerHeapReference(*m_pointer)
; } | |
122 TimerHeapReference operator[](ptrdiff_t i) const { return TimerHeapReference
(m_pointer[i]); } | |
123 TimerBase* operator->() const { return *m_pointer; } | |
124 | |
125 private: | |
126 void checkConsistency(ptrdiff_t offset = 0) const | |
127 { | |
128 ASSERT(m_pointer >= threadGlobalTimerHeap().data()); | |
129 ASSERT(m_pointer <= threadGlobalTimerHeap().data() + threadGlobalTimerHe
ap().size()); | |
130 ASSERT_UNUSED(offset, m_pointer + offset >= threadGlobalTimerHeap().data
()); | |
131 ASSERT_UNUSED(offset, m_pointer + offset <= threadGlobalTimerHeap().data
() + threadGlobalTimerHeap().size()); | |
132 } | |
133 | |
134 friend bool operator==(TimerHeapIterator, TimerHeapIterator); | |
135 friend bool operator!=(TimerHeapIterator, TimerHeapIterator); | |
136 friend bool operator<(TimerHeapIterator, TimerHeapIterator); | |
137 friend bool operator>(TimerHeapIterator, TimerHeapIterator); | |
138 friend bool operator<=(TimerHeapIterator, TimerHeapIterator); | |
139 friend bool operator>=(TimerHeapIterator, TimerHeapIterator); | |
140 | |
141 friend TimerHeapIterator operator+(TimerHeapIterator, size_t); | |
142 friend TimerHeapIterator operator+(size_t, TimerHeapIterator); | |
143 | |
144 friend TimerHeapIterator operator-(TimerHeapIterator, size_t); | |
145 friend ptrdiff_t operator-(TimerHeapIterator, TimerHeapIterator); | |
146 | |
147 TimerBase** m_pointer; | |
148 }; | |
149 | |
150 inline bool operator==(TimerHeapIterator a, TimerHeapIterator b) { return a.m_po
inter == b.m_pointer; } | |
151 inline bool operator!=(TimerHeapIterator a, TimerHeapIterator b) { return a.m_po
inter != b.m_pointer; } | |
152 inline bool operator<(TimerHeapIterator a, TimerHeapIterator b) { return a.m_poi
nter < b.m_pointer; } | |
153 inline bool operator>(TimerHeapIterator a, TimerHeapIterator b) { return a.m_poi
nter > b.m_pointer; } | |
154 inline bool operator<=(TimerHeapIterator a, TimerHeapIterator b) { return a.m_po
inter <= b.m_pointer; } | |
155 inline bool operator>=(TimerHeapIterator a, TimerHeapIterator b) { return a.m_po
inter >= b.m_pointer; } | |
156 | |
157 inline TimerHeapIterator operator+(TimerHeapIterator a, size_t b) { return Timer
HeapIterator(a.m_pointer + b); } | |
158 inline TimerHeapIterator operator+(size_t a, TimerHeapIterator b) { return Timer
HeapIterator(a + b.m_pointer); } | |
159 | |
160 inline TimerHeapIterator operator-(TimerHeapIterator a, size_t b) { return Timer
HeapIterator(a.m_pointer - b); } | |
161 inline ptrdiff_t operator-(TimerHeapIterator a, TimerHeapIterator b) { return a.
m_pointer - b.m_pointer; } | |
162 | |
163 // ---------------- | |
164 | |
165 class TimerHeapLessThanFunction { | |
166 public: | |
167 bool operator()(const TimerBase*, const TimerBase*) const; | |
168 }; | |
169 | |
170 inline bool TimerHeapLessThanFunction::operator()(const TimerBase* a, const Time
rBase* b) const | |
171 { | |
172 // The comparisons below are "backwards" because the heap puts the largest | |
173 // element first and we want the lowest time to be the first one in the heap
. | |
174 double aFireTime = a->m_nextFireTime; | |
175 double bFireTime = b->m_nextFireTime; | |
176 if (bFireTime != aFireTime) | |
177 return bFireTime < aFireTime; | |
178 | |
179 // We need to look at the difference of the insertion orders instead of comp
aring the two | |
180 // outright in case of overflow. | |
181 unsigned difference = a->m_heapInsertionOrder - b->m_heapInsertionOrder; | |
182 return difference < std::numeric_limits<unsigned>::max() / 2; | |
183 } | |
184 | |
185 // ---------------- | |
186 | |
187 TimerBase::TimerBase() | 42 TimerBase::TimerBase() |
188 : m_nextFireTime(0) | 43 : m_nextFireTime(0) |
189 , m_unalignedNextFireTime(0) | 44 , m_unalignedNextFireTime(0) |
190 , m_repeatInterval(0) | 45 , m_repeatInterval(0) |
191 , m_heapIndex(-1) | 46 , m_cancellableTimerTask(nullptr) |
192 , m_cachedThreadGlobalTimerHeap(0) | |
193 #if ENABLE(ASSERT) | 47 #if ENABLE(ASSERT) |
194 , m_thread(currentThread()) | 48 , m_thread(currentThread()) |
195 #endif | 49 #endif |
196 { | 50 { |
197 } | 51 } |
198 | 52 |
199 TimerBase::~TimerBase() | 53 TimerBase::~TimerBase() |
200 { | 54 { |
201 stop(); | 55 stop(); |
202 ASSERT(!inHeap()); | |
203 } | 56 } |
204 | 57 |
205 void TimerBase::start(double nextFireInterval, double repeatInterval, const WebT
raceLocation& caller) | 58 void TimerBase::start(double nextFireInterval, double repeatInterval, const WebT
raceLocation& caller) |
206 { | 59 { |
207 ASSERT(m_thread == currentThread()); | 60 ASSERT(m_thread == currentThread()); |
208 | 61 |
209 m_location = caller; | 62 m_location = caller; |
210 m_repeatInterval = repeatInterval; | 63 m_repeatInterval = repeatInterval; |
211 setNextFireTime(monotonicallyIncreasingTime() + nextFireInterval); | 64 setNextFireTime(monotonicallyIncreasingTime(), nextFireInterval); |
212 } | 65 } |
213 | 66 |
214 void TimerBase::stop() | 67 void TimerBase::stop() |
215 { | 68 { |
216 ASSERT(m_thread == currentThread()); | 69 ASSERT(m_thread == currentThread()); |
217 | 70 |
218 m_repeatInterval = 0; | 71 m_repeatInterval = 0; |
219 setNextFireTime(0); | 72 m_nextFireTime = 0; |
220 | 73 if (m_cancellableTimerTask) { |
221 ASSERT(m_nextFireTime == 0); | 74 m_cancellableTimerTask->cancel(); |
222 ASSERT(m_repeatInterval == 0); | 75 m_cancellableTimerTask = nullptr; |
223 ASSERT(!inHeap()); | 76 } |
224 } | 77 } |
225 | 78 |
226 double TimerBase::nextFireInterval() const | 79 double TimerBase::nextFireInterval() const |
227 { | 80 { |
228 ASSERT(isActive()); | 81 ASSERT(isActive()); |
229 double current = monotonicallyIncreasingTime(); | 82 double current = monotonicallyIncreasingTime(); |
230 if (m_nextFireTime < current) | 83 if (m_nextFireTime < current) |
231 return 0; | 84 return 0; |
232 return m_nextFireTime - current; | 85 return m_nextFireTime - current; |
233 } | 86 } |
234 | 87 |
235 inline void TimerBase::checkHeapIndex() const | 88 void TimerBase::setNextFireTime(double now, double delay) |
236 { | |
237 ASSERT(timerHeap() == threadGlobalTimerHeap()); | |
238 ASSERT(!timerHeap().isEmpty()); | |
239 ASSERT(m_heapIndex >= 0); | |
240 ASSERT(m_heapIndex < static_cast<int>(timerHeap().size())); | |
241 ASSERT(timerHeap()[m_heapIndex] == this); | |
242 } | |
243 | |
244 inline void TimerBase::checkConsistency() const | |
245 { | |
246 // Timers should be in the heap if and only if they have a non-zero next fir
e time. | |
247 ASSERT(inHeap() == (m_nextFireTime != 0)); | |
248 if (inHeap()) | |
249 checkHeapIndex(); | |
250 } | |
251 | |
252 void TimerBase::heapDecreaseKey() | |
253 { | |
254 ASSERT(m_nextFireTime != 0); | |
255 checkHeapIndex(); | |
256 TimerBase** heapData = timerHeap().data(); | |
257 push_heap(TimerHeapIterator(heapData), TimerHeapIterator(heapData + m_heapIn
dex + 1), TimerHeapLessThanFunction()); | |
258 checkHeapIndex(); | |
259 } | |
260 | |
261 inline void TimerBase::heapDelete() | |
262 { | |
263 ASSERT(m_nextFireTime == 0); | |
264 heapPop(); | |
265 timerHeap().removeLast(); | |
266 m_heapIndex = -1; | |
267 } | |
268 | |
269 void TimerBase::heapDeleteMin() | |
270 { | |
271 ASSERT(m_nextFireTime == 0); | |
272 heapPopMin(); | |
273 timerHeap().removeLast(); | |
274 m_heapIndex = -1; | |
275 } | |
276 | |
277 inline void TimerBase::heapIncreaseKey() | |
278 { | |
279 ASSERT(m_nextFireTime != 0); | |
280 heapPop(); | |
281 heapDecreaseKey(); | |
282 } | |
283 | |
284 inline void TimerBase::heapInsert() | |
285 { | |
286 ASSERT(!inHeap()); | |
287 timerHeap().append(this); | |
288 m_heapIndex = timerHeap().size() - 1; | |
289 heapDecreaseKey(); | |
290 } | |
291 | |
292 inline void TimerBase::heapPop() | |
293 { | |
294 // Temporarily force this timer to have the minimum key so we can pop it. | |
295 double fireTime = m_nextFireTime; | |
296 m_nextFireTime = -std::numeric_limits<double>::infinity(); | |
297 heapDecreaseKey(); | |
298 heapPopMin(); | |
299 m_nextFireTime = fireTime; | |
300 } | |
301 | |
302 void TimerBase::heapPopMin() | |
303 { | |
304 ASSERT(this == timerHeap().first()); | |
305 checkHeapIndex(); | |
306 Vector<TimerBase*>& heap = timerHeap(); | |
307 TimerBase** heapData = heap.data(); | |
308 pop_heap(TimerHeapIterator(heapData), TimerHeapIterator(heapData + heap.size
()), TimerHeapLessThanFunction()); | |
309 checkHeapIndex(); | |
310 ASSERT(this == timerHeap().last()); | |
311 } | |
312 | |
313 static inline bool parentHeapPropertyHolds(const TimerBase* current, const Vecto
r<TimerBase*>& heap, unsigned currentIndex) | |
314 { | |
315 if (!currentIndex) | |
316 return true; | |
317 unsigned parentIndex = (currentIndex - 1) / 2; | |
318 TimerHeapLessThanFunction compareHeapPosition; | |
319 return compareHeapPosition(current, heap[parentIndex]); | |
320 } | |
321 | |
322 static inline bool childHeapPropertyHolds(const TimerBase* current, const Vector
<TimerBase*>& heap, unsigned childIndex) | |
323 { | |
324 if (childIndex >= heap.size()) | |
325 return true; | |
326 TimerHeapLessThanFunction compareHeapPosition; | |
327 return compareHeapPosition(heap[childIndex], current); | |
328 } | |
329 | |
330 bool TimerBase::hasValidHeapPosition() const | |
331 { | |
332 ASSERT(m_nextFireTime); | |
333 if (!inHeap()) | |
334 return false; | |
335 // Check if the heap property still holds with the new fire time. If it does
we don't need to do anything. | |
336 // This assumes that the STL heap is a standard binary heap. In an unlikely
event it is not, the assertions | |
337 // in updateHeapIfNeeded() will get hit. | |
338 const Vector<TimerBase*>& heap = timerHeap(); | |
339 if (!parentHeapPropertyHolds(this, heap, m_heapIndex)) | |
340 return false; | |
341 unsigned childIndex1 = 2 * m_heapIndex + 1; | |
342 unsigned childIndex2 = childIndex1 + 1; | |
343 return childHeapPropertyHolds(this, heap, childIndex1) && childHeapPropertyH
olds(this, heap, childIndex2); | |
344 } | |
345 | |
346 void TimerBase::updateHeapIfNeeded(double oldTime) | |
347 { | |
348 if (m_nextFireTime && hasValidHeapPosition()) | |
349 return; | |
350 #if ENABLE(ASSERT) | |
351 int oldHeapIndex = m_heapIndex; | |
352 #endif | |
353 if (!oldTime) | |
354 heapInsert(); | |
355 else if (!m_nextFireTime) | |
356 heapDelete(); | |
357 else if (m_nextFireTime < oldTime) | |
358 heapDecreaseKey(); | |
359 else | |
360 heapIncreaseKey(); | |
361 ASSERT(m_heapIndex != oldHeapIndex); | |
362 ASSERT(!inHeap() || hasValidHeapPosition()); | |
363 } | |
364 | |
365 void TimerBase::setNextFireTime(double newUnalignedTime) | |
366 { | 89 { |
367 ASSERT(m_thread == currentThread()); | 90 ASSERT(m_thread == currentThread()); |
368 | 91 |
369 if (m_unalignedNextFireTime != newUnalignedTime) | 92 m_unalignedNextFireTime = now + delay; |
370 m_unalignedNextFireTime = newUnalignedTime; | |
371 | |
372 // Accessing thread global data is slow. Cache the heap pointer. | |
373 if (!m_cachedThreadGlobalTimerHeap) | |
374 m_cachedThreadGlobalTimerHeap = &threadGlobalTimerHeap(); | |
375 | 93 |
376 // Keep heap valid while changing the next-fire time. | 94 // Keep heap valid while changing the next-fire time. |
377 double oldTime = m_nextFireTime; | 95 double newTime = alignedFireTime(m_unalignedNextFireTime); |
378 double newTime = alignedFireTime(newUnalignedTime); | 96 if (m_nextFireTime != newTime) { |
379 if (oldTime != newTime) { | |
380 m_nextFireTime = newTime; | 97 m_nextFireTime = newTime; |
381 static unsigned currentHeapInsertionOrder; | 98 // Round the delay up to the nearest millisecond to be consistant with t
he |
382 m_heapInsertionOrder = atomicAdd(¤tHeapInsertionOrder, 1); | 99 // previous behavior of BlinkPlatformImpl::setSharedTimerFireInterval. |
| 100 long long delayMs = static_cast<long long>(ceil((newTime - now) * 1000.0
)); |
| 101 if (delayMs < 0) |
| 102 delayMs = 0; |
| 103 if (m_cancellableTimerTask) |
| 104 m_cancellableTimerTask->cancel(); |
| 105 m_cancellableTimerTask = new CancellableTimerTask(this); |
383 | 106 |
384 bool wasFirstTimerInHeap = m_heapIndex == 0; | 107 // Currently only the main thread has a scheduler, so we need to check w
hich thread we are posting from. |
| 108 if (WTF::isMainThread()) |
| 109 Scheduler::shared()->postTimerTask(m_location, m_cancellableTimerTas
k, delayMs); |
| 110 else |
| 111 Platform::current()->currentThread()->postDelayedTask(m_location, m_
cancellableTimerTask, delayMs); |
| 112 } |
| 113 } |
385 | 114 |
386 updateHeapIfNeeded(oldTime); | 115 void TimerBase::CancellableTimerTask::run() |
| 116 { |
| 117 if (m_timer) { |
| 118 m_timer->runInternal(); |
| 119 m_timer = nullptr; |
| 120 } |
| 121 } |
387 | 122 |
388 bool isFirstTimerInHeap = m_heapIndex == 0; | 123 void TimerBase::runInternal() |
| 124 { |
| 125 ASSERT_WITH_MESSAGE(m_thread == currentThread(), "Timer posted by %s %s was
run on a different thread", m_location.functionName(), m_location.fileName()); |
389 | 126 |
390 if (wasFirstTimerInHeap || isFirstTimerInHeap) | 127 m_cancellableTimerTask = nullptr; |
391 PlatformThreadData::current().threadTimers().updateSharedTimer(); | 128 m_nextFireTime = 0; |
392 } | 129 // NOTE repeating timers drift, but it's preserving the functionality of the
old TimerHeap. |
393 | 130 if (m_repeatInterval) |
394 checkConsistency(); | 131 setNextFireTime(monotonicallyIncreasingTime(), m_repeatInterval); |
| 132 fired(); |
395 } | 133 } |
396 | 134 |
397 void TimerBase::fireTimersInNestedEventLoop() | 135 void TimerBase::fireTimersInNestedEventLoop() |
398 { | 136 { |
| 137 ASSERT(false && "IMPLEMENT ME!"); |
399 // Redirect to ThreadTimers. | 138 // Redirect to ThreadTimers. |
400 PlatformThreadData::current().threadTimers().fireTimersInNestedEventLoop(); | 139 // PlatformThreadData::current().threadTimers().fireTimersInNestedEventLoop(
); |
401 } | 140 } |
402 | 141 |
403 void TimerBase::didChangeAlignmentInterval() | 142 void TimerBase::didChangeAlignmentInterval(double now) |
404 { | 143 { |
405 setNextFireTime(m_unalignedNextFireTime); | 144 setNextFireTime(now, m_unalignedNextFireTime - now); |
406 } | 145 } |
407 | 146 |
408 double TimerBase::nextUnalignedFireInterval() const | 147 double TimerBase::nextUnalignedFireInterval() const |
409 { | 148 { |
410 ASSERT(isActive()); | 149 ASSERT(isActive()); |
411 return std::max(m_unalignedNextFireTime - monotonicallyIncreasingTime(), 0.0
); | 150 return std::max(m_unalignedNextFireTime - monotonicallyIncreasingTime(), 0.0
); |
412 } | 151 } |
413 | 152 |
414 } // namespace blink | 153 } // namespace blink |
OLD | NEW |