/* * Copyright (C) 2012 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" #include "HeapTimer.h" #include "APIShims.h" #include "JSObject.h" #include "JSString.h" #include #include #if PLATFORM(QT) #include #include #include #include #endif namespace JSC { #if USE(CF) const CFTimeInterval HeapTimer::s_decade = 60 * 60 * 24 * 365 * 10; HeapTimer::HeapTimer(JSGlobalData* globalData, CFRunLoopRef runLoop) : m_globalData(globalData) , m_runLoop(runLoop) { memset(&m_context, 0, sizeof(CFRunLoopTimerContext)); m_context.info = this; m_timer.adoptCF(CFRunLoopTimerCreate(0, s_decade, s_decade, 0, 0, HeapTimer::timerDidFire, &m_context)); CFRunLoopAddTimer(m_runLoop.get(), m_timer.get(), kCFRunLoopCommonModes); } HeapTimer::~HeapTimer() { CFRunLoopRemoveTimer(m_runLoop.get(), m_timer.get(), kCFRunLoopCommonModes); CFRunLoopTimerInvalidate(m_timer.get()); } void HeapTimer::synchronize() { if (CFRunLoopGetCurrent() == m_runLoop.get()) return; CFRunLoopRemoveTimer(m_runLoop.get(), m_timer.get(), kCFRunLoopCommonModes); m_runLoop = CFRunLoopGetCurrent(); CFRunLoopAddTimer(m_runLoop.get(), m_timer.get(), kCFRunLoopCommonModes); } void HeapTimer::invalidate() { m_globalData = 0; CFRunLoopTimerSetNextFireDate(m_timer.get(), CFAbsoluteTimeGetCurrent() - s_decade); } void HeapTimer::didStartVMShutdown() { if (CFRunLoopGetCurrent() == m_runLoop.get()) { invalidate(); delete this; return; } ASSERT(!m_globalData->apiLock().currentThreadIsHoldingLock()); MutexLocker locker(m_shutdownMutex); invalidate(); } void HeapTimer::timerDidFire(CFRunLoopTimerRef, void* info) { HeapTimer* agent = static_cast(info); agent->m_shutdownMutex.lock(); if (!agent->m_globalData) { agent->m_shutdownMutex.unlock(); delete agent; return; } { // We don't ref here to prevent us from resurrecting the ref count of a "dead" JSGlobalData. APIEntryShim shim(agent->m_globalData, APIEntryShimWithoutLock::DontRefGlobalData); agent->doWork(); } agent->m_shutdownMutex.unlock(); } #elif PLATFORM(BLACKBERRY) HeapTimer::HeapTimer(JSGlobalData* globalData) : m_globalData(globalData) , m_timer(this, &HeapTimer::timerDidFire) { // FIXME: Implement HeapTimer for other threads. if (WTF::isMainThread() && !m_timer.tryCreateClient()) CRASH(); } HeapTimer::~HeapTimer() { } void HeapTimer::timerDidFire() { doWork(); } void HeapTimer::synchronize() { } void HeapTimer::invalidate() { } void HeapTimer::didStartVMShutdown() { delete this; } #elif PLATFORM(QT) HeapTimer::HeapTimer(JSGlobalData* globalData) : m_globalData(globalData) , m_newThread(0) , m_mutex(QMutex::NonRecursive) { // The HeapTimer might be created before the runLoop is started, // but we need to ensure the thread has an eventDispatcher already. QEventLoop fakeLoop(this); } HeapTimer::~HeapTimer() { } void HeapTimer::timerEvent(QTimerEvent*) { QMutexLocker lock(&m_mutex); if (m_newThread) { // We need to wait with processing until we are on the right thread. return; } APIEntryShim shim(m_globalData, APIEntryShimWithoutLock::DontRefGlobalData); doWork(); } void HeapTimer::customEvent(QEvent*) { ASSERT(m_newThread); QMutexLocker lock(&m_mutex); moveToThread(m_newThread); m_newThread = 0; } void HeapTimer::synchronize() { if (thread() != QThread::currentThread()) { // We can only move from the objects own thread to another, so we fire an // event into the owning thread to trigger the move. // This must be processed before any timerEvents so giving it high priority. QMutexLocker lock(&m_mutex); m_newThread = QThread::currentThread(); QCoreApplication::postEvent(this, new QEvent(QEvent::User), Qt::HighEventPriority); } } void HeapTimer::invalidate() { QMutexLocker lock(&m_mutex); m_timer.stop(); } void HeapTimer::didStartVMShutdown() { invalidate(); if (thread() == QThread::currentThread()) delete this; else deleteLater(); } #else HeapTimer::HeapTimer(JSGlobalData* globalData) : m_globalData(globalData) { } HeapTimer::~HeapTimer() { } void HeapTimer::didStartVMShutdown() { delete this; } void HeapTimer::synchronize() { } void HeapTimer::invalidate() { } #endif } // namespace JSC