Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(481)

Side by Side Diff: Source/platform/TimerHeap.cpp

Issue 959263002: WIP - not ready for review (Closed) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: Decouple TimerHeap and ThreadTimers. Created 5 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « Source/platform/TimerHeap.h ('k') | Source/platform/TimerHeapEntry.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "config.h"
6 #include "platform/TimerHeap.h"
7
8 #include "platform/PlatformThreadData.h"
9 #include "platform/ThreadTimers.h"
10 #include "platform/Timer.h"
11 #include <limits.h>
12 #include <limits>
13 #include <math.h>
14
15 namespace blink {
16
17 class TimerHeapReference;
18
19 TimerQueueObserver::TimerQueueObserver()
20 {
21 }
22
23 TimerQueueObserver::~TimerQueueObserver()
24 {
25 }
26
27 ThreadTimers& threadTimers()
28 {
29 return PlatformThreadData::current().threadTimers();
30 }
31
32 // Timers are stored in a heap data structure, used to implement a
33 // priority queue. This allows us to efficiently determine which
34 // timer needs to fire the soonest. Then we set a single shared
35 // system timer to fire at that time.
36 //
37 // When a timer's "next fire time" changes, we need to move it around
38 // in the priority queue.
39 TimerHeap& TimerHeap::get()
40 {
41 return threadTimers().timerHeap();
42 }
43
44 class TimerHeapPointer {
45 public:
46 TimerHeapPointer(TimerBase** pointer)
47 : m_pointer(pointer)
48 {
49 }
50 TimerHeapReference operator*() const;
51 TimerBase* operator->() const { return *m_pointer; }
52
53 private:
54 TimerBase** m_pointer;
55 };
56
57 class TimerHeapReference {
58 public:
59 TimerHeapReference(TimerBase*& reference)
60 : m_reference(reference)
61 {
62 }
63 operator TimerBase*() const { return m_reference; }
64 TimerHeapPointer operator&() const { return &m_reference; }
65 TimerHeapReference& operator=(TimerBase*);
66 TimerHeapReference& operator=(TimerHeapReference);
67
68 private:
69 TimerBase*& m_reference;
70 };
71
72 inline TimerHeapReference TimerHeapPointer::operator*() const
73 {
74 return *m_pointer;
75 }
76
77 inline TimerHeapReference& TimerHeapReference::operator=(TimerBase* timer)
78 {
79 m_reference = timer;
80 TimerHeapEntry& entry = timer->heapEntry();
81 ASSERT_WITH_SECURITY_IMPLICATION(entry.m_owner->m_heap.data() <= &m_referenc e && &m_reference < entry.m_owner->m_heap.data() + entry.m_owner->m_heap.size()) ;
82 entry.m_heapIndex = &m_reference - entry.m_owner->m_heap.data();
83 return *this;
84 }
85
86 inline TimerHeapReference& TimerHeapReference::operator=(TimerHeapReference b)
87 {
88 TimerBase* timer = b;
89 return * this = timer;
90 }
91
92 inline void swap(TimerHeapReference a, TimerHeapReference b)
93 {
94 TimerBase* timerA = a;
95 TimerBase* timerB = b;
96
97 // Invoke the assignment operator, since that takes care of
98 // updating m_heapIndex.
99 a = timerB;
100 b = timerA;
101 }
102
103 // Class to represent iterators in the heap when calling the standard
104 // library heap algorithms. Uses a custom pointer and reference type
105 // that update indices for pointers in the heap.
106 class TimerHeapIterator : public std::iterator<std::random_access_iterator_tag, TimerBase*, ptrdiff_t, TimerHeapPointer, TimerHeapReference> {
107 public:
108 explicit TimerHeapIterator(TimerBase** pointer)
109 : m_pointer(pointer)
110 {
111 checkConsistency();
112 }
113
114 TimerHeapIterator& operator++()
115 {
116 checkConsistency();
117 ++m_pointer;
118 checkConsistency();
119 return *this;
120 }
121 TimerHeapIterator operator++(int)
122 {
123 checkConsistency(1);
124 return TimerHeapIterator(m_pointer++);
125 }
126
127 TimerHeapIterator& operator--()
128 {
129 checkConsistency();
130 --m_pointer;
131 checkConsistency();
132 return *this;
133 }
134 TimerHeapIterator operator--(int)
135 {
136 checkConsistency(-1);
137 return TimerHeapIterator(m_pointer--);
138 }
139
140 TimerHeapIterator& operator+=(ptrdiff_t i)
141 {
142 checkConsistency();
143 m_pointer += i;
144 checkConsistency();
145 return *this;
146 }
147 TimerHeapIterator& operator-=(ptrdiff_t i)
148 {
149 checkConsistency();
150 m_pointer -= i;
151 checkConsistency();
152 return *this;
153 }
154
155 TimerHeapReference operator*() const { return TimerHeapReference(*m_pointer) ; }
156 TimerHeapReference operator[](ptrdiff_t i) const { return TimerHeapReference (m_pointer[i]); }
157 TimerBase* operator->() const { return *m_pointer; }
158
159 private:
160 void checkConsistency(ptrdiff_t offset = 0) const
161 {
162 ASSERT(m_pointer >= TimerHeap::get().m_heap.data());
163 ASSERT(m_pointer <= TimerHeap::get().m_heap.data() + TimerHeap::get().m_ heap.size());
164 ASSERT_UNUSED(offset, m_pointer + offset >= TimerHeap::get().m_heap.data ());
165 ASSERT_UNUSED(offset, m_pointer + offset <= TimerHeap::get().m_heap.data () + TimerHeap::get().m_heap.size());
166 }
167
168 friend bool operator==(TimerHeapIterator, TimerHeapIterator);
169 friend bool operator!=(TimerHeapIterator, TimerHeapIterator);
170 friend bool operator<(TimerHeapIterator, TimerHeapIterator);
171 friend bool operator>(TimerHeapIterator, TimerHeapIterator);
172 friend bool operator<=(TimerHeapIterator, TimerHeapIterator);
173 friend bool operator>=(TimerHeapIterator, TimerHeapIterator);
174
175 friend TimerHeapIterator operator+(TimerHeapIterator, size_t);
176 friend TimerHeapIterator operator+(size_t, TimerHeapIterator);
177
178 friend TimerHeapIterator operator-(TimerHeapIterator, size_t);
179 friend ptrdiff_t operator-(TimerHeapIterator, TimerHeapIterator);
180
181 TimerBase** m_pointer;
182 };
183
184 inline bool operator==(TimerHeapIterator a, TimerHeapIterator b) { return a.m_po inter == b.m_pointer; }
185 inline bool operator!=(TimerHeapIterator a, TimerHeapIterator b) { return a.m_po inter != b.m_pointer; }
186 inline bool operator<(TimerHeapIterator a, TimerHeapIterator b) { return a.m_poi nter < b.m_pointer; }
187 inline bool operator>(TimerHeapIterator a, TimerHeapIterator b) { return a.m_poi nter > b.m_pointer; }
188 inline bool operator<=(TimerHeapIterator a, TimerHeapIterator b) { return a.m_po inter <= b.m_pointer; }
189 inline bool operator>=(TimerHeapIterator a, TimerHeapIterator b) { return a.m_po inter >= b.m_pointer; }
190
191 inline TimerHeapIterator operator+(TimerHeapIterator a, size_t b) { return Timer HeapIterator(a.m_pointer + b); }
192 inline TimerHeapIterator operator+(size_t a, TimerHeapIterator b) { return Timer HeapIterator(a + b.m_pointer); }
193
194 inline TimerHeapIterator operator-(TimerHeapIterator a, size_t b) { return Timer HeapIterator(a.m_pointer - b); }
195 inline ptrdiff_t operator-(TimerHeapIterator a, TimerHeapIterator b) { return a. m_pointer - b.m_pointer; }
196
197 class TimerHeapLessThanFunction {
198 public:
199 bool operator()(const TimerBase*, const TimerBase*) const;
200 };
201
202 inline bool TimerHeapLessThanFunction::operator()(const TimerBase* a, const Time rBase* b) const
203 {
204 // The comparisons below are "backwards" because the heap puts the
205 // greatest element first but we want the lowest time to be the
206 // first one in the heap.
207
208 // Check if one of the values has the "forced minumum for popping"
209 // value. Only one entry can be popped at once, so only one entry
210 // can have the special value at once.
211 if (a->heapEntry().m_state == TimerHeapEntry::TimerHeapEntryInHeap_ForcedMin imumForPopping) {
212 ASSERT(a == b || b->heapEntry().m_state == TimerHeapEntry::TimerHeapEntr yInHeap);
213 return false;
214 }
215 if (b->heapEntry().m_state == TimerHeapEntry::TimerHeapEntryInHeap_ForcedMin imumForPopping) {
216 ASSERT(a == b || a->heapEntry().m_state == TimerHeapEntry::TimerHeapEntr yInHeap);
217 return true;
218 }
219
220 double aFireTime = a->heapEntry().value();
221 double bFireTime = b->heapEntry().value();
222 if (bFireTime != aFireTime)
223 return bFireTime < aFireTime;
224
225 // We need to look at the difference of the insertion orders
226 // instead of comparing the two outright in case of overflow.
227 unsigned difference = a->heapEntry().m_heapInsertionOrder - b->heapEntry().m _heapInsertionOrder;
228 return difference < std::numeric_limits<unsigned>::max() / 2;
229 }
230
231 TimerBase* TimerHeap::first() const
232 {
233 ASSERT(currentThread() == m_thread);
234 return m_heap.first();
235 }
236
237 bool TimerHeap::isEmpty() const
238 {
239 ASSERT(currentThread() == m_thread);
240 return m_heap.isEmpty();
241 }
242
243 void TimerHeap::pop(TimerBase* timer)
244 {
245 // Temporarily force this timer to have the minimum key so we can pop it.
246 TimerHeapEntry& entry = timer->heapEntry();
247 ASSERT(entry.inHeap());
248 entry.m_state = TimerHeapEntry::TimerHeapEntryInHeap_ForcedMinimumForPopping ;
249 didDecreaseKey(timer);
250
251 ASSERT(timer == m_heap.first());
252 TimerBase** heapData = m_heap.data();
253 pop_heap(TimerHeapIterator(heapData), TimerHeapIterator(heapData + m_heap.si ze()), TimerHeapLessThanFunction());
254 ASSERT(timer == m_heap.last());
255 }
256
257 void TimerHeap::remove(TimerBase* timer)
258 {
259 TimerHeapEntry& entry = timer->heapEntry();
260 ASSERT(this == entry.m_owner);
261 ASSERT(timer->nextFireTime() == 0);
262
263 pop(timer);
264 m_heap.removeLast();
265
266 HashSet<TimerBase*>::iterator it = m_entries.find(timer);
267 ASSERT(it != m_entries.end());
268 m_entries.remove(it);
269
270 entry.m_state = TimerHeapEntry::TimerHeapEntryNotInHeap;
271 entry.m_heapIndex = std::numeric_limits<std::size_t>::max();
272
273 check();
274 }
275
276 void TimerHeap::didDecreaseKey(TimerBase* timer)
277 {
278 TimerHeapEntry& entry = timer->heapEntry();
279 ASSERT(this == entry.m_owner);
280 TimerBase** heapData = m_heap.data();
281 push_heap(TimerHeapIterator(heapData), TimerHeapIterator(heapData + entry.m_ heapIndex + 1), TimerHeapLessThanFunction());
282 }
283
284 void TimerHeap::didIncreaseKey(TimerBase* timer)
285 {
286 pop(timer);
287 timer->heapEntry().m_state = TimerHeapEntry::TimerHeapEntryInHeap;
288 didDecreaseKey(timer);
289 }
290
291 void TimerHeap::insert(TimerBase* timer)
292 {
293 TimerHeapEntry& entry = timer->heapEntry();
294 ASSERT(this == entry.m_owner);
295 ASSERT(!entry.inHeap());
296
297 entry.m_heapInsertionOrder = m_currentHeapInsertionOrder++;
298 entry.m_heapIndex = m_heap.size();
299 entry.m_state = TimerHeapEntry::TimerHeapEntryInHeap;
300
301 m_heap.append(timer);
302
303 HashSet<TimerBase*>::AddResult result = m_entries.add(timer);
304 ASSERT(result.isNewEntry);
305
306 didDecreaseKey(timer);
307
308 check(entry);
309 }
310
311 bool TimerHeap::hasValidHeapPosition(const TimerHeapEntry& entry) const
312 {
313 ASSERT(currentThread() == m_thread);
314 ASSERT(this == entry.m_owner);
315 if (!entry.inHeap())
316 return false;
317 #if ENABLE(ASSERT)
318 TimerBase* timer = m_heap[entry.m_heapIndex];
319 #endif
320 ASSERT(&timer->heapEntry() == &entry);
321 ASSERT(m_entries.find(timer) != m_entries.end());
322 if (!parentHeapPropertyHolds(entry))
323 return false;
324 unsigned childIndex1 = 2 * entry.m_heapIndex + 1;
325 unsigned childIndex2 = childIndex1 + 1;
326 return childHeapPropertyHolds(entry, childIndex1) && childHeapPropertyHolds( entry, childIndex2);
327 }
328
329 bool TimerHeap::parentHeapPropertyHolds(const TimerHeapEntry& current) const
330 {
331 ASSERT(current.inHeap());
332 if (current.m_heapIndex == 0)
333 return true; // This is the root of the heap.
334 unsigned parentIndex = (current.m_heapIndex - 1) / 2;
335 TimerHeapLessThanFunction compareHeapPosition;
336 return compareHeapPosition(m_heap[current.m_heapIndex], m_heap[parentIndex]) ;
337 }
338
339 bool TimerHeap::childHeapPropertyHolds(const TimerHeapEntry& current, size_t chi ldIndex) const
340 {
341 ASSERT(current.inHeap());
342 if (childIndex >= m_heap.size())
343 return true; // This is a leaf.
344 TimerHeapLessThanFunction compareHeapPosition;
345 return compareHeapPosition(m_heap[childIndex], m_heap[current.m_heapIndex]);
346 }
347
348 void TimerHeap::check() const
349 {
350 ASSERT_WITH_SECURITY_IMPLICATION(m_entries.size() == m_heap.size());
351 for (HashSet<TimerBase*>::const_iterator it = m_entries.begin(); it != m_ent ries.end(); ++it) {
352 check((*it)->heapEntry());
353 }
354 }
355
356 void TimerHeap::check(const TimerHeapEntry& entry) const
357 {
358 // Is the entry associated with the right heap.
359 ASSERT_WITH_SECURITY_IMPLICATION(this == entry.m_owner);
360
361 // The "forced minimum for popping" state should only exist transiently.
362 ASSERT_WITH_SECURITY_IMPLICATION(entry.m_state != TimerHeapEntry::TimerHeapE ntryInHeap_ForcedMinimumForPopping);
363
364 // Sanity-check the heap index.
365 ASSERT_WITH_SECURITY_IMPLICATION(!entry.inHeap() || entry.m_heapIndex < m_he ap.size());
366
367 // If in the heap, is the heap index accurate?
368 ASSERT_WITH_SECURITY_IMPLICATION(!entry.inHeap() || &m_heap[entry.m_heapInde x]->heapEntry() == &entry);
369
370 // Timers should be in the heap if and only if they have a
371 // non-zero next fire time.
372 ASSERT_WITH_SECURITY_IMPLICATION(!entry.inHeap() || (entry.value() != 0));
373
374 ASSERT_WITH_SECURITY_IMPLICATION(!entry.inHeap() || hasValidHeapPosition(ent ry));
375 }
376
377 void TimerHeap::didChangeValue(TimerBase& timer, double oldValue)
378 {
379 ASSERT(currentThread() == m_thread);
380
381 TimerHeapEntry& entry = timer.heapEntry();
382 if (entry.value() && hasValidHeapPosition(entry))
383 return;
384 bool wasInHeap = entry.inHeap();
385 size_t oldHeapIndex = entry.m_heapIndex;
386 bool wasFirstInHeap = wasInHeap && oldHeapIndex == 0;
387 if (!oldValue)
388 insert(&timer);
389 else if (!entry.value())
390 remove(&timer);
391 else if (entry.value() < oldValue)
392 didDecreaseKey(&timer);
393 else
394 didIncreaseKey(&timer);
395 ASSERT(wasInHeap != entry.inHeap() || entry.m_heapIndex != oldHeapIndex);
396 check(entry);
397
398 bool isFirstInHeap = entry.inHeap() && entry.m_heapIndex == 0;
399 if (m_observer && (wasFirstInHeap || isFirstInHeap))
400 m_observer->nextFiringTimerChanged();
401 }
402
403 TimerHeap::TimerHeap(TimerQueueObserver* observer)
404 : m_observer(observer)
405 , m_thread(currentThread())
406 , m_currentHeapInsertionOrder(0)
407 {
408 }
409
410 TimerHeap::~TimerHeap()
411 {
412 ASSERT(currentThread() == m_thread);
413 }
414
415 } // namespace blink
OLDNEW
« no previous file with comments | « Source/platform/TimerHeap.h ('k') | Source/platform/TimerHeapEntry.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698