summaryrefslogtreecommitdiff
path: root/Source/JavaScriptCore/runtime/Watchdog.cpp
diff options
context:
space:
mode:
authorLorry Tar Creator <lorry-tar-importer@lorry>2015-05-20 09:56:07 +0000
committerLorry Tar Creator <lorry-tar-importer@lorry>2015-05-20 09:56:07 +0000
commit41386e9cb918eed93b3f13648cbef387e371e451 (patch)
treea97f9d7bd1d9d091833286085f72da9d83fd0606 /Source/JavaScriptCore/runtime/Watchdog.cpp
parente15dd966d523731101f70ccf768bba12435a0208 (diff)
downloadWebKitGtk-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.cpp223
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