1/* 2 * Copyright (C) 2006, 2008 Apple Inc. All rights reserved. 3 * Copyright (C) 2009 Google Inc. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY 15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR 18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 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. 25 */ 26 27#include "config.h" 28#include "ThreadTimers.h" 29 30#include "SharedTimer.h" 31#include "ThreadGlobalData.h" 32#include "Timer.h" 33#include <wtf/CurrentTime.h> 34#include <wtf/MainThread.h> 35 36#if PLATFORM(IOS) 37#include "WebCoreThread.h" 38#endif 39 40namespace WebCore { 41 42// Fire timers for this length of time, and then quit to let the run loop process user input events. 43// 100ms is about a perceptable delay in UI, so use a half of that as a threshold. 44// This is to prevent UI freeze when there are too many timers or machine performance is low. 45static const double maxDurationOfFiringTimers = 0.050; 46 47// Timers are created, started and fired on the same thread, and each thread has its own ThreadTimers 48// copy to keep the heap and a set of currently firing timers. 49 50static MainThreadSharedTimer* mainThreadSharedTimer() 51{ 52 static MainThreadSharedTimer* timer = new MainThreadSharedTimer; 53 return timer; 54} 55 56ThreadTimers::ThreadTimers() 57 : m_sharedTimer(0) 58 , m_firingTimers(false) 59 , m_pendingSharedTimerFireTime(0) 60{ 61 if (isUIThread()) 62 setSharedTimer(mainThreadSharedTimer()); 63} 64 65// A worker thread may initialize SharedTimer after some timers are created. 66// Also, SharedTimer can be replaced with 0 before all timers are destroyed. 67void ThreadTimers::setSharedTimer(SharedTimer* sharedTimer) 68{ 69 if (m_sharedTimer) { 70 m_sharedTimer->setFiredFunction(0); 71 m_sharedTimer->stop(); 72 m_pendingSharedTimerFireTime = 0; 73 } 74 75 m_sharedTimer = sharedTimer; 76 77 if (sharedTimer) { 78 m_sharedTimer->setFiredFunction(ThreadTimers::sharedTimerFired); 79 updateSharedTimer(); 80 } 81} 82 83void ThreadTimers::updateSharedTimer() 84{ 85 if (!m_sharedTimer) 86 return; 87 88 if (m_firingTimers || m_timerHeap.isEmpty()) { 89 m_pendingSharedTimerFireTime = 0; 90 m_sharedTimer->stop(); 91 } else { 92 double nextFireTime = m_timerHeap.first()->m_nextFireTime; 93 double currentMonotonicTime = monotonicallyIncreasingTime(); 94 if (m_pendingSharedTimerFireTime) { 95 // No need to restart the timer if both the pending fire time and the new fire time are in the past. 96 if (m_pendingSharedTimerFireTime <= currentMonotonicTime && nextFireTime <= currentMonotonicTime) 97 return; 98 } 99 m_pendingSharedTimerFireTime = nextFireTime; 100 m_sharedTimer->setFireInterval(std::max(nextFireTime - currentMonotonicTime, 0.0)); 101 } 102} 103 104void ThreadTimers::sharedTimerFired() 105{ 106 // Redirect to non-static method. 107 threadGlobalData().threadTimers().sharedTimerFiredInternal(); 108} 109 110void ThreadTimers::sharedTimerFiredInternal() 111{ 112 ASSERT(isMainThread() || (!isWebThread() && !isUIThread())); 113 // Do a re-entrancy check. 114 if (m_firingTimers) 115 return; 116 m_firingTimers = true; 117 m_pendingSharedTimerFireTime = 0; 118 119 double fireTime = monotonicallyIncreasingTime(); 120 double timeToQuit = fireTime + maxDurationOfFiringTimers; 121 122 while (!m_timerHeap.isEmpty() && m_timerHeap.first()->m_nextFireTime <= fireTime) { 123 TimerBase* timer = m_timerHeap.first(); 124 timer->m_nextFireTime = 0; 125 timer->m_unalignedNextFireTime = 0; 126 timer->heapDeleteMin(); 127 128 double interval = timer->repeatInterval(); 129 timer->setNextFireTime(interval ? fireTime + interval : 0); 130 131 // Once the timer has been fired, it may be deleted, so do nothing else with it after this point. 132 timer->fired(); 133 134 // Catch the case where the timer asked timers to fire in a nested event loop, or we are over time limit. 135 if (!m_firingTimers || timeToQuit < monotonicallyIncreasingTime()) 136 break; 137 } 138 139 m_firingTimers = false; 140 141 updateSharedTimer(); 142} 143 144void ThreadTimers::fireTimersInNestedEventLoop() 145{ 146 // Reset the reentrancy guard so the timers can fire again. 147 m_firingTimers = false; 148 updateSharedTimer(); 149} 150 151} // namespace WebCore 152 153