diff options
| author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2015-05-20 09:56:07 +0000 |
|---|---|---|
| committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2015-05-20 09:56:07 +0000 |
| commit | 41386e9cb918eed93b3f13648cbef387e371e451 (patch) | |
| tree | a97f9d7bd1d9d091833286085f72da9d83fd0606 /Source/JavaScriptCore/runtime/Watchdog.cpp | |
| parent | e15dd966d523731101f70ccf768bba12435a0208 (diff) | |
| download | WebKitGtk-tarball-41386e9cb918eed93b3f13648cbef387e371e451.tar.gz | |
webkitgtk-2.4.9webkitgtk-2.4.9
Diffstat (limited to 'Source/JavaScriptCore/runtime/Watchdog.cpp')
| -rw-r--r-- | Source/JavaScriptCore/runtime/Watchdog.cpp | 223 |
1 files changed, 117 insertions, 106 deletions
diff --git a/Source/JavaScriptCore/runtime/Watchdog.cpp b/Source/JavaScriptCore/runtime/Watchdog.cpp index 044552cf9..573260b16 100644 --- a/Source/JavaScriptCore/runtime/Watchdog.cpp +++ b/Source/JavaScriptCore/runtime/Watchdog.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013, 2015 Apple Inc. All rights reserved. + * Copyright (C) 2013 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -32,157 +32,168 @@ namespace JSC { -const std::chrono::microseconds Watchdog::noTimeLimit = std::chrono::microseconds::max(); - -static std::chrono::microseconds currentWallClockTime() -{ - auto steadyTimeSinceEpoch = std::chrono::steady_clock::now().time_since_epoch(); - return std::chrono::duration_cast<std::chrono::microseconds>(steadyTimeSinceEpoch); -} +#define NO_LIMIT std::numeric_limits<double>::infinity() Watchdog::Watchdog() : m_timerDidFire(false) - , m_timeLimit(noTimeLimit) - , m_cpuDeadline(noTimeLimit) - , m_wallClockDeadline(noTimeLimit) + , m_didFire(false) + , m_limit(NO_LIMIT) + , m_startTime(0) + , m_elapsedTime(0) + , m_reentryCount(0) + , m_isStopped(true) , m_callback(0) , m_callbackData1(0) , m_callbackData2(0) - , m_timerQueue(WorkQueue::create("jsc.watchdog.queue", WorkQueue::Type::Serial, WorkQueue::QOS::Utility)) { - m_timerHandler = [this] { - { - LockHolder locker(m_lock); - this->m_timerDidFire = true; - } - this->deref(); - }; + initTimer(); } -void Watchdog::setTimeLimit(std::chrono::microseconds limit, +Watchdog::~Watchdog() +{ + ASSERT(!isArmed()); + stopCountdown(); + destroyTimer(); +} + +void Watchdog::setTimeLimit(VM& vm, double limit, ShouldTerminateCallback callback, void* data1, void* data2) { - LockHolder locker(m_lock); + bool wasEnabled = isEnabled(); + + if (!m_isStopped) + stopCountdown(); - m_timeLimit = limit; + m_didFire = false; // Reset the watchdog. + + m_limit = limit; m_callback = callback; m_callbackData1 = data1; m_callbackData2 = data2; - if (m_hasEnteredVM && hasTimeLimit()) - startTimer(locker, m_timeLimit); -} - -JS_EXPORT_PRIVATE void Watchdog::terminateSoon() -{ - LockHolder locker(m_lock); + // If this is the first time that timeout is being enabled, then any + // previously JIT compiled code will not have the needed polling checks. + // Hence, we need to flush all the pre-existing compiled code. + // + // However, if the timeout is already enabled, and we're just changing the + // timeout value, then any existing JITted code will have the appropriate + // polling checks. Hence, there is no need to re-do this flushing. + if (!wasEnabled) { + // And if we've previously compiled any functions, we need to revert + // them because they don't have the needed polling checks yet. + vm.releaseExecutableMemory(); + } - m_timeLimit = std::chrono::microseconds(0); - m_cpuDeadline = std::chrono::microseconds(0); - m_wallClockDeadline = std::chrono::microseconds(0); - m_timerDidFire = true; + startCountdownIfNeeded(); } -bool Watchdog::didFireSlow(ExecState* exec) +bool Watchdog::didFire(ExecState* exec) { - { - LockHolder locker(m_lock); - - ASSERT(m_timerDidFire); - m_timerDidFire = false; - - if (currentWallClockTime() < m_wallClockDeadline) - return false; // Just a stale timer firing. Nothing to do. - - // Set m_wallClockDeadline to noTimeLimit here so that we can reject all future - // spurious wakes. - m_wallClockDeadline = noTimeLimit; + if (m_didFire) + return true; - auto cpuTime = currentCPUTime(); - if (cpuTime < m_cpuDeadline) { - auto remainingCPUTime = m_cpuDeadline - cpuTime; - startTimer(locker, remainingCPUTime); - return false; + if (!m_timerDidFire) + return false; + m_timerDidFire = false; + stopCountdown(); + + double currentTime = currentCPUTime(); + double deltaTime = currentTime - m_startTime; + double totalElapsedTime = m_elapsedTime + deltaTime; + if (totalElapsedTime > m_limit) { + // Case 1: the allowed CPU time has elapsed. + + // If m_callback is not set, then we terminate by default. + // Else, we let m_callback decide if we should terminate or not. + bool needsTermination = !m_callback + || m_callback(exec, m_callbackData1, m_callbackData2); + if (needsTermination) { + m_didFire = true; + return true; } - } - // Note: we should not be holding the lock while calling the callbacks. The callbacks may - // call setTimeLimit() which will try to lock as well. + // The m_callback may have set a new limit. So, we may need to restart + // the countdown. + startCountdownIfNeeded(); - // If m_callback is not set, then we terminate by default. - // Else, we let m_callback decide if we should terminate or not. - bool needsTermination = !m_callback - || m_callback(exec, m_callbackData1, m_callbackData2); - if (needsTermination) - return true; + } else { + // Case 2: the allowed CPU time has NOT elapsed. - { - LockHolder locker(m_lock); - - // If we get here, then the callback above did not want to terminate execution. As a - // result, the callback may have done one of the following: - // 1. cleared the time limit (i.e. watchdog is disabled), - // 2. set a new time limit via Watchdog::setTimeLimit(), or - // 3. did nothing (i.e. allow another cycle of the current time limit). - // - // In the case of 1, we don't have to do anything. - // In the case of 2, Watchdog::setTimeLimit() would already have started the timer. - // In the case of 3, we need to re-start the timer here. - - ASSERT(m_hasEnteredVM); - bool callbackAlreadyStartedTimer = (m_cpuDeadline != noTimeLimit); - if (hasTimeLimit() && !callbackAlreadyStartedTimer) - startTimer(locker, m_timeLimit); + // Tell the timer to alarm us again when it thinks we've reached the + // end of the allowed time. + double remainingTime = m_limit - totalElapsedTime; + m_elapsedTime = totalElapsedTime; + m_startTime = currentTime; + startCountdown(remainingTime); } + return false; } -bool Watchdog::hasTimeLimit() +bool Watchdog::isEnabled() { - return (m_timeLimit != noTimeLimit); + return (m_limit != NO_LIMIT); } -void Watchdog::enteredVM() +void Watchdog::fire() { - m_hasEnteredVM = true; - if (hasTimeLimit()) { - LockHolder locker(m_lock); - startTimer(locker, m_timeLimit); - } + m_didFire = true; +} + +void Watchdog::arm() +{ + m_reentryCount++; + if (m_reentryCount == 1) + startCountdownIfNeeded(); } -void Watchdog::exitedVM() +void Watchdog::disarm() { - ASSERT(m_hasEnteredVM); - LockHolder locker(m_lock); - stopTimer(locker); - m_hasEnteredVM = false; + ASSERT(m_reentryCount > 0); + if (m_reentryCount == 1) + stopCountdown(); + m_reentryCount--; } -void Watchdog::startTimer(LockHolder&, std::chrono::microseconds timeLimit) +void Watchdog::startCountdownIfNeeded() { - ASSERT(m_hasEnteredVM); - ASSERT(hasTimeLimit()); - ASSERT(timeLimit <= m_timeLimit); + if (!m_isStopped) + return; // Already started. - m_cpuDeadline = currentCPUTime() + timeLimit; - auto wallClockTime = currentWallClockTime(); - auto wallClockDeadline = wallClockTime + timeLimit; + if (!isArmed()) + return; // Not executing JS script. No need to start. - if ((wallClockTime < m_wallClockDeadline) - && (m_wallClockDeadline <= wallClockDeadline)) - return; // Wait for the current active timer to expire before starting a new one. + if (isEnabled()) { + m_elapsedTime = 0; + m_startTime = currentCPUTime(); + startCountdown(m_limit); + } +} + +void Watchdog::startCountdown(double limit) +{ + ASSERT(m_isStopped); + m_isStopped = false; + startTimer(limit); +} - // Else, the current active timer won't fire soon enough. So, start a new timer. - this->ref(); // m_timerHandler will deref to match later. - m_wallClockDeadline = wallClockDeadline; +void Watchdog::stopCountdown() +{ + if (m_isStopped) + return; + stopTimer(); + m_isStopped = true; +} - m_timerQueue->dispatchAfter(std::chrono::nanoseconds(timeLimit), m_timerHandler); +Watchdog::Scope::Scope(Watchdog& watchdog) + : m_watchdog(watchdog) +{ + m_watchdog.arm(); } -void Watchdog::stopTimer(LockHolder&) +Watchdog::Scope::~Scope() { - m_cpuDeadline = noTimeLimit; + m_watchdog.disarm(); } } // namespace JSC |
