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

Unified 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, 10 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « Source/platform/TimerHeap.h ('k') | Source/platform/TimerHeapEntry.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: Source/platform/TimerHeap.cpp
diff --git a/Source/platform/TimerHeap.cpp b/Source/platform/TimerHeap.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..ea0acc013af5e36038575c7e5dd6fdf22bd8a9a8
--- /dev/null
+++ b/Source/platform/TimerHeap.cpp
@@ -0,0 +1,415 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+#include "platform/TimerHeap.h"
+
+#include "platform/PlatformThreadData.h"
+#include "platform/ThreadTimers.h"
+#include "platform/Timer.h"
+#include <limits.h>
+#include <limits>
+#include <math.h>
+
+namespace blink {
+
+class TimerHeapReference;
+
+TimerQueueObserver::TimerQueueObserver()
+{
+}
+
+TimerQueueObserver::~TimerQueueObserver()
+{
+}
+
+ThreadTimers& threadTimers()
+{
+ return PlatformThreadData::current().threadTimers();
+}
+
+// Timers are stored in a heap data structure, used to implement a
+// priority queue. This allows us to efficiently determine which
+// timer needs to fire the soonest. Then we set a single shared
+// system timer to fire at that time.
+//
+// When a timer's "next fire time" changes, we need to move it around
+// in the priority queue.
+TimerHeap& TimerHeap::get()
+{
+ return threadTimers().timerHeap();
+}
+
+class TimerHeapPointer {
+public:
+ TimerHeapPointer(TimerBase** pointer)
+ : m_pointer(pointer)
+ {
+ }
+ TimerHeapReference operator*() const;
+ TimerBase* operator->() const { return *m_pointer; }
+
+private:
+ TimerBase** m_pointer;
+};
+
+class TimerHeapReference {
+public:
+ TimerHeapReference(TimerBase*& reference)
+ : m_reference(reference)
+ {
+ }
+ operator TimerBase*() const { return m_reference; }
+ TimerHeapPointer operator&() const { return &m_reference; }
+ TimerHeapReference& operator=(TimerBase*);
+ TimerHeapReference& operator=(TimerHeapReference);
+
+private:
+ TimerBase*& m_reference;
+};
+
+inline TimerHeapReference TimerHeapPointer::operator*() const
+{
+ return *m_pointer;
+}
+
+inline TimerHeapReference& TimerHeapReference::operator=(TimerBase* timer)
+{
+ m_reference = timer;
+ TimerHeapEntry& entry = timer->heapEntry();
+ ASSERT_WITH_SECURITY_IMPLICATION(entry.m_owner->m_heap.data() <= &m_reference && &m_reference < entry.m_owner->m_heap.data() + entry.m_owner->m_heap.size());
+ entry.m_heapIndex = &m_reference - entry.m_owner->m_heap.data();
+ return *this;
+}
+
+inline TimerHeapReference& TimerHeapReference::operator=(TimerHeapReference b)
+{
+ TimerBase* timer = b;
+ return * this = timer;
+}
+
+inline void swap(TimerHeapReference a, TimerHeapReference b)
+{
+ TimerBase* timerA = a;
+ TimerBase* timerB = b;
+
+ // Invoke the assignment operator, since that takes care of
+ // updating m_heapIndex.
+ a = timerB;
+ b = timerA;
+}
+
+// Class to represent iterators in the heap when calling the standard
+// library heap algorithms. Uses a custom pointer and reference type
+// that update indices for pointers in the heap.
+class TimerHeapIterator : public std::iterator<std::random_access_iterator_tag, TimerBase*, ptrdiff_t, TimerHeapPointer, TimerHeapReference> {
+public:
+ explicit TimerHeapIterator(TimerBase** pointer)
+ : m_pointer(pointer)
+ {
+ checkConsistency();
+ }
+
+ TimerHeapIterator& operator++()
+ {
+ checkConsistency();
+ ++m_pointer;
+ checkConsistency();
+ return *this;
+ }
+ TimerHeapIterator operator++(int)
+ {
+ checkConsistency(1);
+ return TimerHeapIterator(m_pointer++);
+ }
+
+ TimerHeapIterator& operator--()
+ {
+ checkConsistency();
+ --m_pointer;
+ checkConsistency();
+ return *this;
+ }
+ TimerHeapIterator operator--(int)
+ {
+ checkConsistency(-1);
+ return TimerHeapIterator(m_pointer--);
+ }
+
+ TimerHeapIterator& operator+=(ptrdiff_t i)
+ {
+ checkConsistency();
+ m_pointer += i;
+ checkConsistency();
+ return *this;
+ }
+ TimerHeapIterator& operator-=(ptrdiff_t i)
+ {
+ checkConsistency();
+ m_pointer -= i;
+ checkConsistency();
+ return *this;
+ }
+
+ TimerHeapReference operator*() const { return TimerHeapReference(*m_pointer); }
+ TimerHeapReference operator[](ptrdiff_t i) const { return TimerHeapReference(m_pointer[i]); }
+ TimerBase* operator->() const { return *m_pointer; }
+
+private:
+ void checkConsistency(ptrdiff_t offset = 0) const
+ {
+ ASSERT(m_pointer >= TimerHeap::get().m_heap.data());
+ ASSERT(m_pointer <= TimerHeap::get().m_heap.data() + TimerHeap::get().m_heap.size());
+ ASSERT_UNUSED(offset, m_pointer + offset >= TimerHeap::get().m_heap.data());
+ ASSERT_UNUSED(offset, m_pointer + offset <= TimerHeap::get().m_heap.data() + TimerHeap::get().m_heap.size());
+ }
+
+ friend bool operator==(TimerHeapIterator, TimerHeapIterator);
+ friend bool operator!=(TimerHeapIterator, TimerHeapIterator);
+ friend bool operator<(TimerHeapIterator, TimerHeapIterator);
+ friend bool operator>(TimerHeapIterator, TimerHeapIterator);
+ friend bool operator<=(TimerHeapIterator, TimerHeapIterator);
+ friend bool operator>=(TimerHeapIterator, TimerHeapIterator);
+
+ friend TimerHeapIterator operator+(TimerHeapIterator, size_t);
+ friend TimerHeapIterator operator+(size_t, TimerHeapIterator);
+
+ friend TimerHeapIterator operator-(TimerHeapIterator, size_t);
+ friend ptrdiff_t operator-(TimerHeapIterator, TimerHeapIterator);
+
+ TimerBase** m_pointer;
+};
+
+inline bool operator==(TimerHeapIterator a, TimerHeapIterator b) { return a.m_pointer == b.m_pointer; }
+inline bool operator!=(TimerHeapIterator a, TimerHeapIterator b) { return a.m_pointer != b.m_pointer; }
+inline bool operator<(TimerHeapIterator a, TimerHeapIterator b) { return a.m_pointer < b.m_pointer; }
+inline bool operator>(TimerHeapIterator a, TimerHeapIterator b) { return a.m_pointer > b.m_pointer; }
+inline bool operator<=(TimerHeapIterator a, TimerHeapIterator b) { return a.m_pointer <= b.m_pointer; }
+inline bool operator>=(TimerHeapIterator a, TimerHeapIterator b) { return a.m_pointer >= b.m_pointer; }
+
+inline TimerHeapIterator operator+(TimerHeapIterator a, size_t b) { return TimerHeapIterator(a.m_pointer + b); }
+inline TimerHeapIterator operator+(size_t a, TimerHeapIterator b) { return TimerHeapIterator(a + b.m_pointer); }
+
+inline TimerHeapIterator operator-(TimerHeapIterator a, size_t b) { return TimerHeapIterator(a.m_pointer - b); }
+inline ptrdiff_t operator-(TimerHeapIterator a, TimerHeapIterator b) { return a.m_pointer - b.m_pointer; }
+
+class TimerHeapLessThanFunction {
+public:
+ bool operator()(const TimerBase*, const TimerBase*) const;
+};
+
+inline bool TimerHeapLessThanFunction::operator()(const TimerBase* a, const TimerBase* b) const
+{
+ // The comparisons below are "backwards" because the heap puts the
+ // greatest element first but we want the lowest time to be the
+ // first one in the heap.
+
+ // Check if one of the values has the "forced minumum for popping"
+ // value. Only one entry can be popped at once, so only one entry
+ // can have the special value at once.
+ if (a->heapEntry().m_state == TimerHeapEntry::TimerHeapEntryInHeap_ForcedMinimumForPopping) {
+ ASSERT(a == b || b->heapEntry().m_state == TimerHeapEntry::TimerHeapEntryInHeap);
+ return false;
+ }
+ if (b->heapEntry().m_state == TimerHeapEntry::TimerHeapEntryInHeap_ForcedMinimumForPopping) {
+ ASSERT(a == b || a->heapEntry().m_state == TimerHeapEntry::TimerHeapEntryInHeap);
+ return true;
+ }
+
+ double aFireTime = a->heapEntry().value();
+ double bFireTime = b->heapEntry().value();
+ if (bFireTime != aFireTime)
+ return bFireTime < aFireTime;
+
+ // We need to look at the difference of the insertion orders
+ // instead of comparing the two outright in case of overflow.
+ unsigned difference = a->heapEntry().m_heapInsertionOrder - b->heapEntry().m_heapInsertionOrder;
+ return difference < std::numeric_limits<unsigned>::max() / 2;
+}
+
+TimerBase* TimerHeap::first() const
+{
+ ASSERT(currentThread() == m_thread);
+ return m_heap.first();
+}
+
+bool TimerHeap::isEmpty() const
+{
+ ASSERT(currentThread() == m_thread);
+ return m_heap.isEmpty();
+}
+
+void TimerHeap::pop(TimerBase* timer)
+{
+ // Temporarily force this timer to have the minimum key so we can pop it.
+ TimerHeapEntry& entry = timer->heapEntry();
+ ASSERT(entry.inHeap());
+ entry.m_state = TimerHeapEntry::TimerHeapEntryInHeap_ForcedMinimumForPopping;
+ didDecreaseKey(timer);
+
+ ASSERT(timer == m_heap.first());
+ TimerBase** heapData = m_heap.data();
+ pop_heap(TimerHeapIterator(heapData), TimerHeapIterator(heapData + m_heap.size()), TimerHeapLessThanFunction());
+ ASSERT(timer == m_heap.last());
+}
+
+void TimerHeap::remove(TimerBase* timer)
+{
+ TimerHeapEntry& entry = timer->heapEntry();
+ ASSERT(this == entry.m_owner);
+ ASSERT(timer->nextFireTime() == 0);
+
+ pop(timer);
+ m_heap.removeLast();
+
+ HashSet<TimerBase*>::iterator it = m_entries.find(timer);
+ ASSERT(it != m_entries.end());
+ m_entries.remove(it);
+
+ entry.m_state = TimerHeapEntry::TimerHeapEntryNotInHeap;
+ entry.m_heapIndex = std::numeric_limits<std::size_t>::max();
+
+ check();
+}
+
+void TimerHeap::didDecreaseKey(TimerBase* timer)
+{
+ TimerHeapEntry& entry = timer->heapEntry();
+ ASSERT(this == entry.m_owner);
+ TimerBase** heapData = m_heap.data();
+ push_heap(TimerHeapIterator(heapData), TimerHeapIterator(heapData + entry.m_heapIndex + 1), TimerHeapLessThanFunction());
+}
+
+void TimerHeap::didIncreaseKey(TimerBase* timer)
+{
+ pop(timer);
+ timer->heapEntry().m_state = TimerHeapEntry::TimerHeapEntryInHeap;
+ didDecreaseKey(timer);
+}
+
+void TimerHeap::insert(TimerBase* timer)
+{
+ TimerHeapEntry& entry = timer->heapEntry();
+ ASSERT(this == entry.m_owner);
+ ASSERT(!entry.inHeap());
+
+ entry.m_heapInsertionOrder = m_currentHeapInsertionOrder++;
+ entry.m_heapIndex = m_heap.size();
+ entry.m_state = TimerHeapEntry::TimerHeapEntryInHeap;
+
+ m_heap.append(timer);
+
+ HashSet<TimerBase*>::AddResult result = m_entries.add(timer);
+ ASSERT(result.isNewEntry);
+
+ didDecreaseKey(timer);
+
+ check(entry);
+}
+
+bool TimerHeap::hasValidHeapPosition(const TimerHeapEntry& entry) const
+{
+ ASSERT(currentThread() == m_thread);
+ ASSERT(this == entry.m_owner);
+ if (!entry.inHeap())
+ return false;
+#if ENABLE(ASSERT)
+ TimerBase* timer = m_heap[entry.m_heapIndex];
+#endif
+ ASSERT(&timer->heapEntry() == &entry);
+ ASSERT(m_entries.find(timer) != m_entries.end());
+ if (!parentHeapPropertyHolds(entry))
+ return false;
+ unsigned childIndex1 = 2 * entry.m_heapIndex + 1;
+ unsigned childIndex2 = childIndex1 + 1;
+ return childHeapPropertyHolds(entry, childIndex1) && childHeapPropertyHolds(entry, childIndex2);
+}
+
+bool TimerHeap::parentHeapPropertyHolds(const TimerHeapEntry& current) const
+{
+ ASSERT(current.inHeap());
+ if (current.m_heapIndex == 0)
+ return true; // This is the root of the heap.
+ unsigned parentIndex = (current.m_heapIndex - 1) / 2;
+ TimerHeapLessThanFunction compareHeapPosition;
+ return compareHeapPosition(m_heap[current.m_heapIndex], m_heap[parentIndex]);
+}
+
+bool TimerHeap::childHeapPropertyHolds(const TimerHeapEntry& current, size_t childIndex) const
+{
+ ASSERT(current.inHeap());
+ if (childIndex >= m_heap.size())
+ return true; // This is a leaf.
+ TimerHeapLessThanFunction compareHeapPosition;
+ return compareHeapPosition(m_heap[childIndex], m_heap[current.m_heapIndex]);
+}
+
+void TimerHeap::check() const
+{
+ ASSERT_WITH_SECURITY_IMPLICATION(m_entries.size() == m_heap.size());
+ for (HashSet<TimerBase*>::const_iterator it = m_entries.begin(); it != m_entries.end(); ++it) {
+ check((*it)->heapEntry());
+ }
+}
+
+void TimerHeap::check(const TimerHeapEntry& entry) const
+{
+ // Is the entry associated with the right heap.
+ ASSERT_WITH_SECURITY_IMPLICATION(this == entry.m_owner);
+
+ // The "forced minimum for popping" state should only exist transiently.
+ ASSERT_WITH_SECURITY_IMPLICATION(entry.m_state != TimerHeapEntry::TimerHeapEntryInHeap_ForcedMinimumForPopping);
+
+ // Sanity-check the heap index.
+ ASSERT_WITH_SECURITY_IMPLICATION(!entry.inHeap() || entry.m_heapIndex < m_heap.size());
+
+ // If in the heap, is the heap index accurate?
+ ASSERT_WITH_SECURITY_IMPLICATION(!entry.inHeap() || &m_heap[entry.m_heapIndex]->heapEntry() == &entry);
+
+ // Timers should be in the heap if and only if they have a
+ // non-zero next fire time.
+ ASSERT_WITH_SECURITY_IMPLICATION(!entry.inHeap() || (entry.value() != 0));
+
+ ASSERT_WITH_SECURITY_IMPLICATION(!entry.inHeap() || hasValidHeapPosition(entry));
+}
+
+void TimerHeap::didChangeValue(TimerBase& timer, double oldValue)
+{
+ ASSERT(currentThread() == m_thread);
+
+ TimerHeapEntry& entry = timer.heapEntry();
+ if (entry.value() && hasValidHeapPosition(entry))
+ return;
+ bool wasInHeap = entry.inHeap();
+ size_t oldHeapIndex = entry.m_heapIndex;
+ bool wasFirstInHeap = wasInHeap && oldHeapIndex == 0;
+ if (!oldValue)
+ insert(&timer);
+ else if (!entry.value())
+ remove(&timer);
+ else if (entry.value() < oldValue)
+ didDecreaseKey(&timer);
+ else
+ didIncreaseKey(&timer);
+ ASSERT(wasInHeap != entry.inHeap() || entry.m_heapIndex != oldHeapIndex);
+ check(entry);
+
+ bool isFirstInHeap = entry.inHeap() && entry.m_heapIndex == 0;
+ if (m_observer && (wasFirstInHeap || isFirstInHeap))
+ m_observer->nextFiringTimerChanged();
+}
+
+TimerHeap::TimerHeap(TimerQueueObserver* observer)
+ : m_observer(observer)
+ , m_thread(currentThread())
+ , m_currentHeapInsertionOrder(0)
+{
+}
+
+TimerHeap::~TimerHeap()
+{
+ ASSERT(currentThread() == m_thread);
+}
+
+} // namespace blink
« 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