OLD | NEW |
(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 |
OLD | NEW |