| 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
|
|
|