Diff
Modified: trunk/Source/_javascript_Core/API/JSContextRef.cpp (188328 => 188329)
--- trunk/Source/_javascript_Core/API/JSContextRef.cpp 2015-08-12 05:54:14 UTC (rev 188328)
+++ trunk/Source/_javascript_Core/API/JSContextRef.cpp 2015-08-12 05:56:20 UTC (rev 188329)
@@ -37,6 +37,7 @@
#include "JSCInlines.h"
#include "SourceProvider.h"
#include "StackVisitor.h"
+#include "Watchdog.h"
#include <wtf/text/StringBuilder.h>
#include <wtf/text/StringHash.h>
@@ -94,7 +95,7 @@
static void createWatchdogIfNeeded(VM& vm)
{
if (!vm.watchdog) {
- vm.watchdog = std::make_unique<Watchdog>();
+ vm.watchdog = adoptRef(new Watchdog());
// The LLINT peeks into the Watchdog object directly. In order to do that,
// the LLINT assumes that the internal shape of a std::unique_ptr is the
@@ -120,9 +121,8 @@
{
VM& vm = *toJS(group);
JSLockHolder locker(&vm);
- createWatchdogIfNeeded(vm);
- Watchdog& watchdog = *vm.watchdog;
- watchdog.setTimeLimit(vm, std::chrono::microseconds::max());
+ if (vm.watchdog)
+ vm.watchdog->setTimeLimit(vm, Watchdog::noTimeLimit);
}
// From the API's perspective, a global context remains alive iff it has been JSGlobalContextRetained.
Modified: trunk/Source/_javascript_Core/API/tests/ExecutionTimeLimitTest.cpp (188328 => 188329)
--- trunk/Source/_javascript_Core/API/tests/ExecutionTimeLimitTest.cpp 2015-08-12 05:54:14 UTC (rev 188328)
+++ trunk/Source/_javascript_Core/API/tests/ExecutionTimeLimitTest.cpp 2015-08-12 05:56:20 UTC (rev 188329)
@@ -26,34 +26,15 @@
#include "config.h"
#include "ExecutionTimeLimitTest.h"
-#if OS(DARWIN)
-
#include "JSContextRefPrivate.h"
#include "_javascript_Core.h"
+#include <chrono>
+#include <wtf/CurrentTime.h>
-#include <mach/mach.h>
-#include <mach/mach_time.h>
-#include <stdio.h>
-#include <sys/time.h>
+using namespace std::chrono;
static JSGlobalContextRef context = nullptr;
-static double currentCPUTime()
-{
- mach_msg_type_number_t infoCount = THREAD_BASIC_INFO_COUNT;
- thread_basic_info_data_t info;
-
- /* Get thread information */
- mach_port_t threadPort = mach_thread_self();
- thread_info(threadPort, THREAD_BASIC_INFO, (thread_info_t)(&info), &infoCount);
- mach_port_deallocate(mach_task_self(), threadPort);
-
- double time = info.user_time.seconds + info.user_time.microseconds / 1000000.;
- time += info.system_time.seconds + info.system_time.microseconds / 1000000.;
-
- return time;
-}
-
static JSValueRef currentCPUTimeAsJSFunctionCallback(JSContextRef ctx, JSObjectRef functionObject, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
{
UNUSED_PARAM(functionObject);
@@ -63,7 +44,7 @@
UNUSED_PARAM(exception);
ASSERT(JSContextGetGlobalContext(ctx) == context);
- return JSValueMakeNumber(ctx, currentCPUTime());
+ return JSValueMakeNumber(ctx, currentCPUTime().count() / 1000000.);
}
bool shouldTerminateCallbackWasCalled = false;
@@ -120,18 +101,16 @@
{
const char* loopForeverScript = "var startTime = currentCPUTime(); while (true) { if (currentCPUTime() - startTime > .150) break; } ";
JSStringRef script = JSStringCreateWithUTF8CString(loopForeverScript);
- double startTime;
- double endTime;
exception = nullptr;
shouldTerminateCallbackWasCalled = false;
- startTime = currentCPUTime();
+ auto startTime = currentCPUTime();
v = JSEvaluateScript(context, script, nullptr, nullptr, 1, &exception);
- endTime = currentCPUTime();
+ auto endTime = currentCPUTime();
- if (((endTime - startTime) < .150f) && shouldTerminateCallbackWasCalled)
+ if (((endTime - startTime) < milliseconds(150)) && shouldTerminateCallbackWasCalled)
printf("PASS: script timed out as expected.\n");
else {
- if (!((endTime - startTime) < .150f))
+ if (!((endTime - startTime) < milliseconds(150)))
printf("FAIL: script did not time out as expected.\n");
if (!shouldTerminateCallbackWasCalled)
printf("FAIL: script timeout callback was not called.\n");
@@ -149,16 +128,14 @@
{
const char* loopForeverScript = "var startTime = currentCPUTime(); try { while (true) { if (currentCPUTime() - startTime > .150) break; } } catch(e) { }";
JSStringRef script = JSStringCreateWithUTF8CString(loopForeverScript);
- double startTime;
- double endTime;
exception = nullptr;
shouldTerminateCallbackWasCalled = false;
- startTime = currentCPUTime();
+ auto startTime = currentCPUTime();
v = JSEvaluateScript(context, script, nullptr, nullptr, 1, &exception);
- endTime = currentCPUTime();
+ auto endTime = currentCPUTime();
- if (((endTime - startTime) >= .150f) || !shouldTerminateCallbackWasCalled) {
- if (!((endTime - startTime) < .150f))
+ if (((endTime - startTime) >= milliseconds(150)) || !shouldTerminateCallbackWasCalled) {
+ if (!((endTime - startTime) < milliseconds(150)))
printf("FAIL: script did not time out as expected.\n");
if (!shouldTerminateCallbackWasCalled)
printf("FAIL: script timeout callback was not called.\n");
@@ -178,17 +155,15 @@
{
const char* loopForeverScript = "var startTime = currentCPUTime(); while (true) { if (currentCPUTime() - startTime > .150) break; } ";
JSStringRef script = JSStringCreateWithUTF8CString(loopForeverScript);
- double startTime;
- double endTime;
exception = nullptr;
- startTime = currentCPUTime();
+ auto startTime = currentCPUTime();
v = JSEvaluateScript(context, script, nullptr, nullptr, 1, &exception);
- endTime = currentCPUTime();
+ auto endTime = currentCPUTime();
- if (((endTime - startTime) < .150f) && shouldTerminateCallbackWasCalled)
+ if (((endTime - startTime) < milliseconds(150)) && shouldTerminateCallbackWasCalled)
printf("PASS: script timed out as expected when no callback is specified.\n");
else {
- if (!((endTime - startTime) < .150f))
+ if (!((endTime - startTime) < milliseconds(150)))
printf("FAIL: script did not time out as expected when no callback is specified.\n");
failed = true;
}
@@ -204,17 +179,15 @@
{
const char* loopForeverScript = "var startTime = currentCPUTime(); while (true) { if (currentCPUTime() - startTime > .150) break; } ";
JSStringRef script = JSStringCreateWithUTF8CString(loopForeverScript);
- double startTime;
- double endTime;
exception = nullptr;
- startTime = currentCPUTime();
+ auto startTime = currentCPUTime();
v = JSEvaluateScript(context, script, nullptr, nullptr, 1, &exception);
- endTime = currentCPUTime();
+ auto endTime = currentCPUTime();
- if (((endTime - startTime) >= .150f) && cancelTerminateCallbackWasCalled && !exception)
+ if (((endTime - startTime) >= milliseconds(150)) && cancelTerminateCallbackWasCalled && !exception)
printf("PASS: script timeout was cancelled as expected.\n");
else {
- if (((endTime - startTime) < .150) || exception)
+ if (((endTime - startTime) < milliseconds(150)) || exception)
printf("FAIL: script timeout was not cancelled.\n");
if (!cancelTerminateCallbackWasCalled)
printf("FAIL: script timeout callback was not called.\n");
@@ -232,21 +205,18 @@
{
const char* loopForeverScript = "var startTime = currentCPUTime(); while (true) { if (currentCPUTime() - startTime > .500) break; } ";
JSStringRef script = JSStringCreateWithUTF8CString(loopForeverScript);
- double startTime;
- double endTime;
- double deltaTime;
exception = nullptr;
- startTime = currentCPUTime();
+ auto startTime = currentCPUTime();
v = JSEvaluateScript(context, script, nullptr, nullptr, 1, &exception);
- endTime = currentCPUTime();
- deltaTime = endTime - startTime;
+ auto endTime = currentCPUTime();
+ auto deltaTime = endTime - startTime;
- if ((deltaTime >= .300f) && (deltaTime < .500f) && (extendTerminateCallbackCalled == 2) && exception)
+ if ((deltaTime >= milliseconds(300)) && (deltaTime < milliseconds(500)) && (extendTerminateCallbackCalled == 2) && exception)
printf("PASS: script timeout was extended as expected.\n");
else {
- if (deltaTime < .200f)
+ if (deltaTime < milliseconds(200))
printf("FAIL: script timeout was not extended as expected.\n");
- else if (deltaTime >= .500f)
+ else if (deltaTime >= milliseconds(500))
printf("FAIL: script did not timeout.\n");
if (extendTerminateCallbackCalled < 1)
@@ -264,5 +234,3 @@
JSGlobalContextRelease(context);
return failed;
}
-
-#endif // OS(DARWIN)
Modified: trunk/Source/_javascript_Core/API/tests/testapi.c (188328 => 188329)
--- trunk/Source/_javascript_Core/API/tests/testapi.c 2015-08-12 05:54:14 UTC (rev 188328)
+++ trunk/Source/_javascript_Core/API/tests/testapi.c 2015-08-12 05:56:20 UTC (rev 188329)
@@ -43,9 +43,7 @@
#include "CustomGlobalObjectClassTest.h"
#include "GlobalContextWithFinalizerTest.h"
-#if OS(DARWIN)
#include "ExecutionTimeLimitTest.h"
-#endif
#if JSC_OBJC_API_ENABLED
void testObjectiveCAPI(void);
@@ -1893,9 +1891,7 @@
JSGlobalContextRelease(context);
}
-#if OS(DARWIN)
failed = testExecutionTimeLimit() || failed;
-#endif /* OS(DARWIN) */
failed = testGlobalContextWithFinalizer() || failed;
// Clear out local variables pointing at JSObjectRefs to allow their values to be collected
Modified: trunk/Source/_javascript_Core/CMakeLists.txt (188328 => 188329)
--- trunk/Source/_javascript_Core/CMakeLists.txt 2015-08-12 05:54:14 UTC (rev 188328)
+++ trunk/Source/_javascript_Core/CMakeLists.txt 2015-08-12 05:56:20 UTC (rev 188329)
@@ -617,7 +617,6 @@
runtime/VMEntryScope.cpp
runtime/VarOffset.cpp
runtime/Watchdog.cpp
- runtime/WatchdogNone.cpp
runtime/WeakMapConstructor.cpp
runtime/WeakMapData.cpp
runtime/WeakMapPrototype.cpp
Modified: trunk/Source/_javascript_Core/ChangeLog (188328 => 188329)
--- trunk/Source/_javascript_Core/ChangeLog 2015-08-12 05:54:14 UTC (rev 188328)
+++ trunk/Source/_javascript_Core/ChangeLog 2015-08-12 05:56:20 UTC (rev 188329)
@@ -1,3 +1,197 @@
+2015-08-11 Mark Lam <[email protected]>
+
+ Implementation _javascript_ watchdog using WTF::WorkQueue.
+ https://bugs.webkit.org/show_bug.cgi?id=147107
+
+ Reviewed by Geoffrey Garen.
+
+ How the Watchdog works?
+ ======================
+
+ 1. When do we start the Watchdog?
+ =============================
+ The watchdog should only be started if both the following conditions are true:
+ 1. A time limit has been set.
+ 2. We have entered the VM.
+
+ 2. CPU time vs Wall Clock time
+ ===========================
+ Why do we need 2 time deadlines: m_cpuDeadline and m_wallClockDeadline?
+
+ The watchdog uses WorkQueue dispatchAfter() to queue a timer to measure the watchdog time
+ limit. WorkQueue timers measure time in monotonic wall clock time. m_wallClockDeadline
+ indicates the wall clock time point when the WorkQueue timer is expected to fire.
+
+ The time limit for which we allow JS code to run should be measured in CPU time, which can
+ differ from wall clock time. m_cpuDeadline indicates the CPU time point when the watchdog
+ should fire.
+
+ Note: the timer firing is not the same thing as the watchdog firing. When the timer fires,
+ we need to check if m_cpuDeadline has been reached.
+
+ If m_cpuDeadline has been reached, the watchdog is considered to have fired.
+
+ If not, then we have a remaining amount of CPU time, Tremainder, that we should allow JS
+ code to continue to run for. Hence, we need to start a new timer to fire again after
+ Tremainder microseconds.
+
+ See Watchdog::didFireSlow().
+
+ 3. Spurious wake ups
+ =================
+ Because the WorkQueue timer cannot be cancelled, the watchdog needs to ignore stale timers.
+ It does this by checking the m_wallClockDeadline. A wakeup that occurs right after
+ m_wallClockDeadline expires is considered to be the wakeup for the active timer. All other
+ wake ups are considered to be spurious and will be ignored.
+
+ See Watchdog::didFireSlow().
+
+ 4. Minimizing Timer creation cost
+ ==============================
+ Conceptually, we could start a new timer every time we start the watchdog. But we can do better
+ than this.
+
+ In practice, the time limit of a watchdog tends to be long, and the amount of time a watchdog
+ stays active tends to be short for well-behaved JS code. The user also tends to re-use the same
+ time limit. Consider the following example:
+
+ |---|-----|---|----------------|---------|
+ t0 t1 t2 t3 t0 + L t2 + L
+
+ |<--- T1 --------------------->|
+ |<--- T2 --------------------->|
+ |<-- Td ->| |<-- Td ->|
+
+ 1. The user initializes the watchdog with time limit L.
+ 2. At t0, we enter the VM to execute JS code, and starts the watchdog timer, T1.
+ The timer is set to expire at t0 + L.
+ 3. At t1, we exit the VM.
+ 4. At t2, we enter the VM again, and would like to start a new watchdog timer, T2.
+
+ However, we can note that the expiration time for T2 would be after the expiration time
+ of T1. Specifically, T2 would have expired at Td after T1 expires.
+
+ Hence, we can just wait for T1 to expire, and then start a new timer T2' at time t0 + L
+ for a period or Td instead.
+
+ Note that didFireSlow() already compensates for time differences between wall clock and CPU time,
+ as well as handle spurious wake ups (see note 2 and 3 above). As a result, didFireSlow() will
+ automatically take care of starting a new timer for the difference Td in the example above.
+ Instead of starting the new timer T2 and time t2, we just verify that if the active timer, T1's
+ expiration is less than T2s, then we are already covered by T1 and there's no need to start T2.
+
+ The benefit:
+
+ 1. we minimize the number of timer instances we have queued in the workqueue at the same time
+ (ideally only 1 or 0), and use less peak memory usage.
+
+ 2. we minimize the frequency of instantiating timer instances. By waiting for the current
+ active timer to expire first, on average, we get to start one timer per time limit
+ (which is infrequent because time limits tend to be long) instead of one timer per
+ VM entry (which tends to be frequent).
+
+ See Watchdog::startTimer().
+
+ * API/JSContextRef.cpp:
+ (createWatchdogIfNeeded):
+ (JSContextGroupClearExecutionTimeLimit):
+ - No need to create the watchdog (if not already created) just to clear it.
+ If the watchdog is not created yet, then it is effectively cleared.
+
+ * API/tests/ExecutionTimeLimitTest.cpp:
+ (currentCPUTimeAsJSFunctionCallback):
+ (testExecutionTimeLimit):
+ (currentCPUTime): Deleted.
+ * API/tests/testapi.c:
+ (main):
+ * _javascript_Core.vcxproj/testapi/testapi.vcxproj:
+ * _javascript_Core.vcxproj/testapi/testapi.vcxproj.filters:
+ - Enable watchdog tests for all platforms.
+
+ * CMakeLists.txt:
+ * _javascript_Core.vcxproj/_javascript_Core.vcxproj:
+ * _javascript_Core.vcxproj/_javascript_Core.vcxproj.filters:
+ * _javascript_Core.xcodeproj/project.pbxproj:
+ - Remove now unneeded WatchdogMac.cpp and WatchdogNone.cpp.
+
+ * PlatformEfl.cmake:
+
+ * dfg/DFGByteCodeParser.cpp:
+ (JSC::DFG::ByteCodeParser::parseBlock):
+ * dfg/DFGSpeculativeJIT32_64.cpp:
+ * dfg/DFGSpeculativeJIT64.cpp:
+ * interpreter/Interpreter.cpp:
+ (JSC::Interpreter::execute):
+ (JSC::Interpreter::executeCall):
+ (JSC::Interpreter::executeConstruct):
+ * jit/JITOpcodes.cpp:
+ (JSC::JIT::emit_op_loop_hint):
+ (JSC::JIT::emitSlow_op_loop_hint):
+ * jit/JITOperations.cpp:
+ * llint/LLIntOffsetsExtractor.cpp:
+ * llint/LLIntSlowPaths.cpp:
+ * runtime/VM.cpp:
+ - #include Watchdog.h in these files directly instead of doing it via VM.h.
+ These saves us from having to recompile the world when we change Watchdog.h.
+
+ * runtime/VM.h:
+ - See comment in Watchdog::startTimer() below for why the Watchdog needs to be
+ thread-safe ref counted.
+
+ * runtime/VMEntryScope.cpp:
+ (JSC::VMEntryScope::VMEntryScope):
+ (JSC::VMEntryScope::~VMEntryScope):
+ - We have done away with the WatchdogScope and arming/disarming of the watchdog.
+ Instead, the VMEntryScope will inform the watchdog of when we have entered and
+ exited the VM.
+
+ * runtime/Watchdog.cpp:
+ (JSC::currentWallClockTime):
+ (JSC::Watchdog::Watchdog):
+ (JSC::Watchdog::hasStartedTimer):
+ (JSC::Watchdog::setTimeLimit):
+ (JSC::Watchdog::didFireSlow):
+ (JSC::Watchdog::hasTimeLimit):
+ (JSC::Watchdog::fire):
+ (JSC::Watchdog::enteredVM):
+ (JSC::Watchdog::exitedVM):
+
+ (JSC::Watchdog::startTimer):
+ - The Watchdog is now thread-safe ref counted because the WorkQueue may access it
+ (from a different thread) even after the VM shuts down. We need to keep it
+ alive until the WorkQueue callback completes.
+
+ In Watchdog::startTimer(), we'll ref the Watchdog to keep it alive for each
+ WorkQueue callback we dispatch. The callback will deref the Watchdog after it
+ is done with it. This ensures that the Watchdog is kept alive until all
+ WorkQueue callbacks are done.
+
+ (JSC::Watchdog::stopTimer):
+ (JSC::Watchdog::~Watchdog): Deleted.
+ (JSC::Watchdog::didFire): Deleted.
+ (JSC::Watchdog::isEnabled): Deleted.
+ (JSC::Watchdog::arm): Deleted.
+ (JSC::Watchdog::disarm): Deleted.
+ (JSC::Watchdog::startCountdownIfNeeded): Deleted.
+ (JSC::Watchdog::startCountdown): Deleted.
+ (JSC::Watchdog::stopCountdown): Deleted.
+ * runtime/Watchdog.h:
+ (JSC::Watchdog::didFire):
+ (JSC::Watchdog::timerDidFireAddress):
+ (JSC::Watchdog::isArmed): Deleted.
+ (JSC::Watchdog::Scope::Scope): Deleted.
+ (JSC::Watchdog::Scope::~Scope): Deleted.
+ * runtime/WatchdogMac.cpp:
+ (JSC::Watchdog::initTimer): Deleted.
+ (JSC::Watchdog::destroyTimer): Deleted.
+ (JSC::Watchdog::startTimer): Deleted.
+ (JSC::Watchdog::stopTimer): Deleted.
+ * runtime/WatchdogNone.cpp:
+ (JSC::Watchdog::initTimer): Deleted.
+ (JSC::Watchdog::destroyTimer): Deleted.
+ (JSC::Watchdog::startTimer): Deleted.
+ (JSC::Watchdog::stopTimer): Deleted.
+
2015-08-11 Filip Pizlo <[email protected]>
Always use a byte-sized lock implementation
Modified: trunk/Source/_javascript_Core/_javascript_Core.vcxproj/_javascript_Core.vcxproj (188328 => 188329)
--- trunk/Source/_javascript_Core/_javascript_Core.vcxproj/_javascript_Core.vcxproj 2015-08-12 05:54:14 UTC (rev 188328)
+++ trunk/Source/_javascript_Core/_javascript_Core.vcxproj/_javascript_Core.vcxproj 2015-08-12 05:56:20 UTC (rev 188329)
@@ -870,7 +870,6 @@
<ClCompile Include="..\runtime\VMEntryScope.cpp" />
<ClCompile Include="..\runtime\VarOffset.cpp" />
<ClCompile Include="..\runtime\Watchdog.cpp" />
- <ClCompile Include="..\runtime\WatchdogNone.cpp" />
<ClCompile Include="..\runtime\WeakMapConstructor.cpp" />
<ClCompile Include="..\runtime\WeakMapData.cpp" />
<ClCompile Include="..\runtime\WeakMapPrototype.cpp" />
Modified: trunk/Source/_javascript_Core/_javascript_Core.vcxproj/_javascript_Core.vcxproj.filters (188328 => 188329)
--- trunk/Source/_javascript_Core/_javascript_Core.vcxproj/_javascript_Core.vcxproj.filters 2015-08-12 05:54:14 UTC (rev 188328)
+++ trunk/Source/_javascript_Core/_javascript_Core.vcxproj/_javascript_Core.vcxproj.filters 2015-08-12 05:56:20 UTC (rev 188329)
@@ -936,9 +936,6 @@
<ClCompile Include="..\runtime\Watchdog.cpp">
<Filter>runtime</Filter>
</ClCompile>
- <ClCompile Include="..\runtime\WatchdogNone.cpp">
- <Filter>runtime</Filter>
- </ClCompile>
<ClCompile Include="..\tools\CodeProfile.cpp">
<Filter>tools</Filter>
</ClCompile>
Modified: trunk/Source/_javascript_Core/_javascript_Core.vcxproj/testapi/testapi.vcxproj (188328 => 188329)
--- trunk/Source/_javascript_Core/_javascript_Core.vcxproj/testapi/testapi.vcxproj 2015-08-12 05:54:14 UTC (rev 188328)
+++ trunk/Source/_javascript_Core/_javascript_Core.vcxproj/testapi/testapi.vcxproj 2015-08-12 05:56:20 UTC (rev 188329)
@@ -297,6 +297,8 @@
<ClInclude Include="..\..\API\tests\CompareAndSwapTest.h" />
<ClCompile Include="..\..\API\tests\CustomGlobalObjectClassTest.c" />
<ClInclude Include="..\..\API\tests\CustomGlobalObjectClassTest.h" />
+ <ClCompile Include="..\..\API\tests\ExecutionTimeLimitTest.cpp" />
+ <ClInclude Include="..\..\API\tests\ExecutionTimeLimitTest.h" />
<ClCompile Include="..\..\API\tests\GlobalContextWithFinalizerTest.cpp" />
<ClInclude Include="..\..\API\tests\GlobalContextWithFinalizerTest.h" />
</ItemGroup>
Modified: trunk/Source/_javascript_Core/_javascript_Core.vcxproj/testapi/testapi.vcxproj.filters (188328 => 188329)
--- trunk/Source/_javascript_Core/_javascript_Core.vcxproj/testapi/testapi.vcxproj.filters 2015-08-12 05:54:14 UTC (rev 188328)
+++ trunk/Source/_javascript_Core/_javascript_Core.vcxproj/testapi/testapi.vcxproj.filters 2015-08-12 05:56:20 UTC (rev 188329)
@@ -12,6 +12,8 @@
<ClInclude Include="..\..\API\tests\CompareAndSwapTest.h" />
<ClCompile Include="..\..\API\tests\CustomGlobalObjectClassTest.c" />
<ClInclude Include="..\..\API\tests\CustomGlobalObjectClassTest.h" />
+ <ClCompile Include="..\..\API\tests\ExecutionTimeLimitTest.cpp" />
+ <ClInclude Include="..\..\API\tests\ExecutionTimeLimitTest.h" />
<ClCompile Include="..\..\API\tests\GlobalContextWithFinalizerTest.cpp" />
<ClInclude Include="..\..\API\tests\GlobalContextWithFinalizerTest.h" />
</ItemGroup>
Modified: trunk/Source/_javascript_Core/_javascript_Core.xcodeproj/project.pbxproj (188328 => 188329)
--- trunk/Source/_javascript_Core/_javascript_Core.xcodeproj/project.pbxproj 2015-08-12 05:54:14 UTC (rev 188328)
+++ trunk/Source/_javascript_Core/_javascript_Core.xcodeproj/project.pbxproj 2015-08-12 05:56:20 UTC (rev 188329)
@@ -1728,7 +1728,6 @@
FED287B215EC9A5700DA8161 /* LLIntOpcode.h in Headers */ = {isa = PBXBuildFile; fileRef = FED287B115EC9A5700DA8161 /* LLIntOpcode.h */; settings = {ATTRIBUTES = (Private, ); }; };
FED94F2E171E3E2300BE77A4 /* Watchdog.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FED94F2B171E3E2300BE77A4 /* Watchdog.cpp */; };
FED94F2F171E3E2300BE77A4 /* Watchdog.h in Headers */ = {isa = PBXBuildFile; fileRef = FED94F2C171E3E2300BE77A4 /* Watchdog.h */; settings = {ATTRIBUTES = (Private, ); }; };
- FED94F30171E3E2300BE77A4 /* WatchdogMac.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FED94F2D171E3E2300BE77A4 /* WatchdogMac.cpp */; };
FEF040511AAE662D00BD28B0 /* CompareAndSwapTest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FEF040501AAE662D00BD28B0 /* CompareAndSwapTest.cpp */; };
FEF6835E174343CC00A32E25 /* JITStubsARM.h in Headers */ = {isa = PBXBuildFile; fileRef = FEF6835A174343CC00A32E25 /* JITStubsARM.h */; settings = {ATTRIBUTES = (Private, ); }; };
FEF6835F174343CC00A32E25 /* JITStubsARMv7.h in Headers */ = {isa = PBXBuildFile; fileRef = FEF6835B174343CC00A32E25 /* JITStubsARMv7.h */; settings = {ATTRIBUTES = (Private, ); }; };
@@ -3587,7 +3586,6 @@
FED287B115EC9A5700DA8161 /* LLIntOpcode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LLIntOpcode.h; path = llint/LLIntOpcode.h; sourceTree = "<group>"; };
FED94F2B171E3E2300BE77A4 /* Watchdog.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Watchdog.cpp; sourceTree = "<group>"; };
FED94F2C171E3E2300BE77A4 /* Watchdog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Watchdog.h; sourceTree = "<group>"; };
- FED94F2D171E3E2300BE77A4 /* WatchdogMac.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WatchdogMac.cpp; sourceTree = "<group>"; };
FEF040501AAE662D00BD28B0 /* CompareAndSwapTest.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CompareAndSwapTest.cpp; path = API/tests/CompareAndSwapTest.cpp; sourceTree = "<group>"; };
FEF040521AAEC4ED00BD28B0 /* CompareAndSwapTest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CompareAndSwapTest.h; path = API/tests/CompareAndSwapTest.h; sourceTree = "<group>"; };
FEF6835A174343CC00A32E25 /* JITStubsARM.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JITStubsARM.h; sourceTree = "<group>"; };
@@ -4920,7 +4918,6 @@
FE5932A6183C5A2600A1ECCC /* VMEntryScope.h */,
FED94F2B171E3E2300BE77A4 /* Watchdog.cpp */,
FED94F2C171E3E2300BE77A4 /* Watchdog.h */,
- FED94F2D171E3E2300BE77A4 /* WatchdogMac.cpp */,
14BFCE6810CDB1FC00364CCE /* WeakGCMap.h */,
AD86A93D1AA4D87C002FE77F /* WeakGCMapInlines.h */,
A7CA3ADD17DA41AE006538AF /* WeakMapConstructor.cpp */,
@@ -7833,7 +7830,6 @@
E18E3A590DF9278C00D90B34 /* VM.cpp in Sources */,
FE5932A7183C5A2600A1ECCC /* VMEntryScope.cpp in Sources */,
FED94F2E171E3E2300BE77A4 /* Watchdog.cpp in Sources */,
- FED94F30171E3E2300BE77A4 /* WatchdogMac.cpp in Sources */,
7B98D1361B60CD5F0023B1A4 /* JSWASMModule.cpp in Sources */,
0F919D2515853CE0004A4E7D /* Watchpoint.cpp in Sources */,
1ACF7377171CA6FB00C9BB1E /* Weak.cpp in Sources */,
Modified: trunk/Source/_javascript_Core/PlatformEfl.cmake (188328 => 188329)
--- trunk/Source/_javascript_Core/PlatformEfl.cmake 2015-08-12 05:54:14 UTC (rev 188328)
+++ trunk/Source/_javascript_Core/PlatformEfl.cmake 2015-08-12 05:56:20 UTC (rev 188329)
@@ -2,6 +2,7 @@
${ECORE_INCLUDE_DIRS}
${EINA_INCLUDE_DIRS}
${EO_INCLUDE_DIRS}
+ "${WTF_DIR}/wtf/efl"
)
add_definitions(-DSTATICALLY_LINKED_WITH_WTF)
Modified: trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp (188328 => 188329)
--- trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp 2015-08-12 05:54:14 UTC (rev 188328)
+++ trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp 2015-08-12 05:56:20 UTC (rev 188329)
@@ -45,6 +45,7 @@
#include "PutByIdStatus.h"
#include "StackAlignment.h"
#include "StringConstructor.h"
+#include "Watchdog.h"
#include <wtf/CommaPrinter.h>
#include <wtf/HashMap.h>
#include <wtf/MathExtras.h>
@@ -4030,7 +4031,7 @@
addToGraph(LoopHint);
- if (m_vm->watchdog && m_vm->watchdog->isEnabled())
+ if (m_vm->watchdog && m_vm->watchdog->hasTimeLimit())
addToGraph(CheckWatchdogTimer);
NEXT_OPCODE(op_loop_hint);
Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp (188328 => 188329)
--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp 2015-08-12 05:54:14 UTC (rev 188328)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp 2015-08-12 05:56:20 UTC (rev 188329)
@@ -44,6 +44,7 @@
#include "JSCInlines.h"
#include "SetupVarargsFrame.h"
#include "TypeProfilerLog.h"
+#include "Watchdog.h"
namespace JSC { namespace DFG {
Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp (188328 => 188329)
--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp 2015-08-12 05:54:14 UTC (rev 188328)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp 2015-08-12 05:56:20 UTC (rev 188329)
@@ -44,6 +44,7 @@
#include "SetupVarargsFrame.h"
#include "SpillRegistersMode.h"
#include "TypeProfilerLog.h"
+#include "Watchdog.h"
namespace JSC { namespace DFG {
Modified: trunk/Source/_javascript_Core/interpreter/Interpreter.cpp (188328 => 188329)
--- trunk/Source/_javascript_Core/interpreter/Interpreter.cpp 2015-08-12 05:54:14 UTC (rev 188328)
+++ trunk/Source/_javascript_Core/interpreter/Interpreter.cpp 2015-08-12 05:56:20 UTC (rev 188329)
@@ -71,6 +71,7 @@
#include "Symbol.h"
#include "VMEntryScope.h"
#include "VirtualRegister.h"
+#include "Watchdog.h"
#include <limits.h>
#include <stdio.h>
@@ -880,8 +881,6 @@
JSValue result;
{
SamplingTool::CallRecord callRecord(m_sampler.get());
- Watchdog::Scope watchdogScope(vm.watchdog.get());
-
result = program->generatedJITCode()->execute(&vm, &protoCallFrame);
}
@@ -942,7 +941,6 @@
JSValue result;
{
SamplingTool::CallRecord callRecord(m_sampler.get(), !isJSCall);
- Watchdog::Scope watchdogScope(vm.watchdog.get());
// Execute the code:
if (isJSCall)
@@ -1013,7 +1011,6 @@
JSValue result;
{
SamplingTool::CallRecord callRecord(m_sampler.get(), !isJSConstruct);
- Watchdog::Scope watchdogScope(vm.watchdog.get());
// Execute the code.
if (isJSConstruct)
@@ -1082,8 +1079,6 @@
JSValue result;
{
SamplingTool::CallRecord callRecord(m_sampler.get());
- Watchdog::Scope watchdogScope(vm.watchdog.get());
-
result = closure.functionExecutable->generatedJITCodeForCall()->execute(&vm, closure.protoCallFrame);
}
@@ -1173,8 +1168,6 @@
JSValue result;
{
SamplingTool::CallRecord callRecord(m_sampler.get());
- Watchdog::Scope watchdogScope(vm.watchdog.get());
-
result = eval->generatedJITCode()->execute(&vm, &protoCallFrame);
}
Modified: trunk/Source/_javascript_Core/jit/JITOpcodes.cpp (188328 => 188329)
--- trunk/Source/_javascript_Core/jit/JITOpcodes.cpp 2015-08-12 05:54:14 UTC (rev 188328)
+++ trunk/Source/_javascript_Core/jit/JITOpcodes.cpp 2015-08-12 05:56:20 UTC (rev 188329)
@@ -45,6 +45,7 @@
#include "TypeLocation.h"
#include "TypeProfilerLog.h"
#include "VirtualRegister.h"
+#include "Watchdog.h"
namespace JSC {
@@ -905,7 +906,7 @@
}
// Emit the watchdog timer check:
- if (m_vm->watchdog && m_vm->watchdog->isEnabled())
+ if (m_vm->watchdog && m_vm->watchdog->hasTimeLimit())
addSlowCase(branchTest8(NonZero, AbsoluteAddress(m_vm->watchdog->timerDidFireAddress())));
}
@@ -931,7 +932,7 @@
#endif
// Emit the slow path of the watchdog timer check:
- if (m_vm->watchdog && m_vm->watchdog->isEnabled()) {
+ if (m_vm->watchdog && m_vm->watchdog->hasTimeLimit()) {
linkSlowCase(iter);
callOperation(operationHandleWatchdogTimer);
Modified: trunk/Source/_javascript_Core/jit/JITOperations.cpp (188328 => 188329)
--- trunk/Source/_javascript_Core/jit/JITOperations.cpp 2015-08-12 05:54:14 UTC (rev 188328)
+++ trunk/Source/_javascript_Core/jit/JITOperations.cpp 2015-08-12 05:56:20 UTC (rev 188329)
@@ -57,6 +57,7 @@
#include "ScopedArguments.h"
#include "TestRunnerUtils.h"
#include "TypeProfilerLog.h"
+#include "Watchdog.h"
#include <wtf/InlineASM.h>
namespace JSC {
Modified: trunk/Source/_javascript_Core/llint/LLIntOffsetsExtractor.cpp (188328 => 188329)
--- trunk/Source/_javascript_Core/llint/LLIntOffsetsExtractor.cpp 2015-08-12 05:54:14 UTC (rev 188328)
+++ trunk/Source/_javascript_Core/llint/LLIntOffsetsExtractor.cpp 2015-08-12 05:56:20 UTC (rev 188329)
@@ -55,6 +55,7 @@
#include "TypeProfilerLog.h"
#include "VMEntryRecord.h"
#include "ValueProfile.h"
+#include "Watchdog.h"
#include <wtf/text/StringImpl.h>
Modified: trunk/Source/_javascript_Core/llint/LLIntSlowPaths.cpp (188328 => 188329)
--- trunk/Source/_javascript_Core/llint/LLIntSlowPaths.cpp 2015-08-12 05:54:14 UTC (rev 188328)
+++ trunk/Source/_javascript_Core/llint/LLIntSlowPaths.cpp 2015-08-12 05:56:20 UTC (rev 188329)
@@ -53,6 +53,7 @@
#include "ObjectConstructor.h"
#include "ProtoCallFrame.h"
#include "StructureRareDataInlines.h"
+#include "Watchdog.h"
#include <wtf/StringPrintStream.h>
namespace JSC { namespace LLInt {
Modified: trunk/Source/_javascript_Core/runtime/VM.cpp (188328 => 188329)
--- trunk/Source/_javascript_Core/runtime/VM.cpp 2015-08-12 05:54:14 UTC (rev 188328)
+++ trunk/Source/_javascript_Core/runtime/VM.cpp 2015-08-12 05:56:20 UTC (rev 188329)
@@ -85,6 +85,7 @@
#include "TypeProfiler.h"
#include "TypeProfilerLog.h"
#include "UnlinkedCodeBlock.h"
+#include "Watchdog.h"
#include "WeakGCMapInlines.h"
#include "WeakMapData.h"
#include <wtf/CurrentTime.h>
Modified: trunk/Source/_javascript_Core/runtime/VM.h (188328 => 188329)
--- trunk/Source/_javascript_Core/runtime/VM.h 2015-08-12 05:54:14 UTC (rev 188328)
+++ trunk/Source/_javascript_Core/runtime/VM.h 2015-08-12 05:56:20 UTC (rev 188329)
@@ -50,7 +50,6 @@
#include "ThunkGenerators.h"
#include "TypedArrayController.h"
#include "VMEntryRecord.h"
-#include "Watchdog.h"
#include "Watchpoint.h"
#include "WeakRandom.h"
#include <wtf/Bag.h>
@@ -107,6 +106,7 @@
class UnlinkedProgramCodeBlock;
class VirtualRegister;
class VMEntryScope;
+class Watchdog;
class Watchpoint;
class WatchpointSet;
@@ -259,7 +259,7 @@
ClientData* clientData;
VMEntryFrame* topVMEntryFrame;
ExecState* topCallFrame;
- std::unique_ptr<Watchdog> watchdog;
+ RefPtr<Watchdog> watchdog;
Strong<Structure> structureStructure;
Strong<Structure> structureRareDataStructure;
Modified: trunk/Source/_javascript_Core/runtime/VMEntryScope.cpp (188328 => 188329)
--- trunk/Source/_javascript_Core/runtime/VMEntryScope.cpp 2015-08-12 05:54:14 UTC (rev 188328)
+++ trunk/Source/_javascript_Core/runtime/VMEntryScope.cpp 2015-08-12 05:56:20 UTC (rev 188329)
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013, 2014 Apple Inc. All rights reserved.
+ * Copyright (C) 2013-2015 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -29,6 +29,7 @@
#include "Debugger.h"
#include "Options.h"
#include "VM.h"
+#include "Watchdog.h"
#include <wtf/StackBounds.h>
namespace JSC {
@@ -48,6 +49,9 @@
// Reset the date cache between JS invocations to force the VM to
// observe time xone changes.
vm.resetDateCache();
+
+ if (vm.watchdog)
+ vm.watchdog->enteredVM();
}
vm.clearLastException();
@@ -63,6 +67,9 @@
if (m_vm.entryScope != this)
return;
+ if (m_vm.watchdog)
+ m_vm.watchdog->exitedVM();
+
m_vm.entryScope = nullptr;
for (auto& listener : m_allEntryScopeDidPopListeners.values())
Modified: trunk/Source/_javascript_Core/runtime/Watchdog.cpp (188328 => 188329)
--- trunk/Source/_javascript_Core/runtime/Watchdog.cpp 2015-08-12 05:54:14 UTC (rev 188328)
+++ trunk/Source/_javascript_Core/runtime/Watchdog.cpp 2015-08-12 05:56:20 UTC (rev 188329)
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2013, 2015 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,75 +32,136 @@
namespace JSC {
-#define NO_LIMIT std::chrono::microseconds::max()
+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);
+}
+
Watchdog::Watchdog()
: m_timerDidFire(false)
, m_didFire(false)
- , m_timeoutPeriod(NO_LIMIT)
- , m_startCPUTime(0)
- , m_elapsedCPUTime(0)
- , m_reentryCount(0)
- , m_isStopped(true)
+ , m_timeLimit(noTimeLimit)
+ , m_cpuDeadline(noTimeLimit)
+ , m_wallClockDeadline(noTimeLimit)
, m_callback(0)
, m_callbackData1(0)
, m_callbackData2(0)
+ , m_timerQueue(WorkQueue::create("jsc.watchdog.queue", WorkQueue::Type::Serial, WorkQueue::QOS::Utility))
{
- initTimer();
+ m_timerHandler = [this] {
+ this->m_timerDidFire = true;
+ this->deref();
+ };
}
-Watchdog::~Watchdog()
+inline bool Watchdog::hasStartedTimer()
{
- ASSERT(!isArmed());
- stopCountdown();
- destroyTimer();
+ return m_cpuDeadline != noTimeLimit;
}
void Watchdog::setTimeLimit(VM& vm, std::chrono::microseconds limit,
ShouldTerminateCallback callback, void* data1, void* data2)
{
- bool wasEnabled = isEnabled();
+ bool hadTimeLimit = hasTimeLimit();
- if (!m_isStopped)
- stopCountdown();
-
m_didFire = false; // Reset the watchdog.
- m_timeoutPeriod = limit;
+ m_timeLimit = limit;
m_callback = callback;
m_callbackData1 = data1;
m_callbackData2 = data2;
- // If this is the first time that timeout is being enabled, then any
+ // If this is the first time that time limit 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
+ // However, if the time limit is already enabled, and we're just changing the
+ // time limit value, then any existing JITted code will have the appropriate
// polling checks. Hence, there is no need to re-do this flushing.
- if (!wasEnabled) {
+ if (!hadTimeLimit) {
// 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();
}
- startCountdownIfNeeded();
+ if (m_hasEnteredVM && hasTimeLimit())
+ startTimer(m_timeLimit);
}
-bool Watchdog::didFire(ExecState* exec)
+bool Watchdog::didFireSlow(ExecState* exec)
{
- if (m_didFire)
- return true;
-
- if (!m_timerDidFire)
- return false;
+ ASSERT(m_timerDidFire);
m_timerDidFire = false;
- stopCountdown();
- auto currentTime = currentCPUTime();
- auto deltaCPUTime = currentTime - m_startCPUTime;
- auto totalElapsedCPUTime = m_elapsedCPUTime + deltaCPUTime;
- if (totalElapsedCPUTime > m_timeoutPeriod) {
+ // A legitimate timer is the timer which woke up watchdog and can caused didFireSlow() to be
+ // called after m_wallClockDeadline has expired. All other timers are considered to be stale,
+ // and their wake ups are considered to be spurious and should be ignored.
+ //
+ // didFireSlow() will race against m_timerHandler to clear and set m_timerDidFire respectively.
+ // We need to deal with a variety of scenarios where we can have stale timers and legitimate
+ // timers firing in close proximity to each other.
+ //
+ // Consider the following scenarios:
+ //
+ // 1. A stale timer fires long before m_wallClockDeadline expires.
+ //
+ // In this case, the m_wallClockDeadline check below will fail and the stale timer will be
+ // ignored as spurious. We just need to make sure that we clear m_timerDidFire before we
+ // check m_wallClockDeadline and return below.
+ //
+ // 2. A stale timer fires just before m_wallClockDeadline expires.
+ // Before the watchdog can gets to the clearing m_timerDidFire above, the legit timer also fires.
+ //
+ // In this case, m_timerDidFire was set twice by the 2 timers, but we only clear need to clear
+ // it once. The m_wallClockDeadline below will pass and this invocation of didFireSlow() will
+ // be treated as the response to the legit timer. The spurious timer is effectively ignored.
+ //
+ // 3. A state timer fires just before m_wallClockDeadline expires.
+ // Right after the watchdog clears m_timerDidFire above, the legit timer also fires.
+ //
+ // The fact that the legit timer fails means that the m_wallClockDeadline check will pass.
+ // As long as we clear m_timerDidFire before we do the check, we are safe. This is the same
+ // scenario as 2 above.
+ //
+ // 4. A stale timer fires just before m_wallClockDeadline expires.
+ // Right after we do the m_wallClockDeadline check below, the legit timer fires.
+ //
+ // The fact that the legit timer fires after the m_wallClockDeadline check means that
+ // the m_wallClockDeadline check will have failed. This is the same scenario as 1 above.
+ //
+ // 5. A legit timer fires.
+ // A stale timer also fires before we clear m_timerDidFire above.
+ //
+ // This is the same scenario as 2 above.
+ //
+ // 6. A legit timer fires.
+ // A stale timer fires right after we clear m_timerDidFire above.
+ //
+ // In this case, the m_wallClockDeadline check will pass, and we fire the watchdog
+ // though m_timerDidFire remains set. This just means that didFireSlow() will be called again due
+ // to m_timerDidFire being set. The subsequent invocation of didFireSlow() will end up handling
+ // the stale timer and ignoring it. This is the same scenario as 1 above.
+ //
+ // 7. A legit timer fires.
+ // A stale timer fires right after we do the m_wallClockDeadline check.
+ //
+ // This is the same as 6, which means it's the same as 1 above.
+ //
+ // In all these cases, we just need to ensure that we clear m_timerDidFire before we do the
+ // m_wallClockDeadline check below. Hence, a storeLoad fence is needed to ensure that these 2
+ // operations do not get re-ordered.
+
+ WTF::storeLoadFence();
+
+ if (currentWallClockTime() < m_wallClockDeadline)
+ return false; // Just a stale timer firing. Nothing to do.
+
+ m_wallClockDeadline = noTimeLimit;
+
+ if (currentCPUTime() >= m_cpuDeadline) {
// Case 1: the allowed CPU time has elapsed.
// If m_callback is not set, then we terminate by default.
@@ -112,27 +173,32 @@
return true;
}
- // The m_callback may have set a new limit. So, we may need to restart
- // the countdown.
- startCountdownIfNeeded();
+ // 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);
+ if (hasTimeLimit() && !hasStartedTimer())
+ startTimer(m_timeLimit);
+
} else {
// Case 2: the allowed CPU time has NOT elapsed.
-
- // Tell the timer to alarm us again when it thinks we've reached the
- // end of the allowed time.
- auto remainingCPUTime = m_timeoutPeriod - totalElapsedCPUTime;
- m_elapsedCPUTime = totalElapsedCPUTime;
- m_startCPUTime = currentTime;
- startCountdown(remainingCPUTime);
+ auto remainingCPUTime = m_cpuDeadline - currentCPUTime();
+ startTimer(remainingCPUTime);
}
return false;
}
-bool Watchdog::isEnabled()
+bool Watchdog::hasTimeLimit()
{
- return (m_timeoutPeriod != NO_LIMIT);
+ return (m_timeLimit != noTimeLimit);
}
void Watchdog::fire()
@@ -140,49 +206,51 @@
m_didFire = true;
}
-void Watchdog::arm()
+void Watchdog::enteredVM()
{
- m_reentryCount++;
- if (m_reentryCount == 1)
- startCountdownIfNeeded();
+ m_hasEnteredVM = true;
+ if (hasTimeLimit())
+ startTimer(m_timeLimit);
}
-void Watchdog::disarm()
+void Watchdog::exitedVM()
{
- ASSERT(m_reentryCount > 0);
- if (m_reentryCount == 1)
- stopCountdown();
- m_reentryCount--;
+ ASSERT(m_hasEnteredVM);
+ stopTimer();
+ m_hasEnteredVM = false;
}
-void Watchdog::startCountdownIfNeeded()
+void Watchdog::startTimer(std::chrono::microseconds timeLimit)
{
- if (!m_isStopped)
- return; // Already started.
+ ASSERT(m_hasEnteredVM);
+ ASSERT(hasTimeLimit());
- if (!isArmed())
- return; // Not executing JS script. No need to start.
+ m_cpuDeadline = currentCPUTime() + timeLimit;
+ auto wallClockTime = currentWallClockTime();
+ auto wallClockDeadline = wallClockTime + timeLimit;
- if (isEnabled()) {
- m_elapsedCPUTime = std::chrono::microseconds::zero();
- m_startCPUTime = currentCPUTime();
- startCountdown(m_timeoutPeriod);
+ if ((wallClockTime < m_wallClockDeadline)
+ && (m_wallClockDeadline <= wallClockDeadline)) {
+ return; // Wait for the current active timer to expire before starting a new one.
}
-}
-void Watchdog::startCountdown(std::chrono::microseconds 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;
+ m_timerDidFire = false;
+
+ // We clear m_timerDidFire because we're starting a new timer. However, we need to make sure
+ // that the clearing occurs before the timer thread is started. Thereafter, only didFireSlow()
+ // should clear m_timerDidFire (unless we start yet another timer). Hence, we need a storeStore
+ // fence here to ensure these operations do not get re-ordered.
+ WTF::storeStoreFence();
+
+ m_timerQueue->dispatchAfter(std::chrono::nanoseconds(timeLimit), m_timerHandler);
}
-void Watchdog::stopCountdown()
+void Watchdog::stopTimer()
{
- if (m_isStopped)
- return;
- stopTimer();
- m_isStopped = true;
+ m_cpuDeadline = noTimeLimit;
}
} // namespace JSC
Modified: trunk/Source/_javascript_Core/runtime/Watchdog.h (188328 => 188329)
--- trunk/Source/_javascript_Core/runtime/Watchdog.h 2015-08-12 05:54:14 UTC (rev 188328)
+++ trunk/Source/_javascript_Core/runtime/Watchdog.h 2015-08-12 05:56:20 UTC (rev 188329)
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2013, 2015 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -26,31 +26,40 @@
#ifndef Watchdog_h
#define Watchdog_h
-#if OS(DARWIN)
-#include <dispatch/dispatch.h>
-#endif
+#include <mutex>
+#include <wtf/Ref.h>
+#include <wtf/ThreadSafeRefCounted.h>
+#include <wtf/WorkQueue.h>
namespace JSC {
class ExecState;
class VM;
-class Watchdog {
+class Watchdog : public WTF::ThreadSafeRefCounted<Watchdog> {
WTF_MAKE_FAST_ALLOCATED;
public:
class Scope;
Watchdog();
- ~Watchdog();
typedef bool (*ShouldTerminateCallback)(ExecState*, void* data1, void* data2);
void setTimeLimit(VM&, std::chrono::microseconds limit, ShouldTerminateCallback = 0, void* data1 = 0, void* data2 = 0);
// This version of didFire() will check the elapsed CPU time and call the
// callback (if needed) to determine if the watchdog should fire.
- bool didFire(ExecState*);
+ bool didFire(ExecState* exec)
+ {
+ if (m_didFire)
+ return true;
+ if (!m_timerDidFire)
+ return false;
+ return didFireSlow(exec);
+ }
- bool isEnabled();
+ bool hasTimeLimit();
+ void enteredVM();
+ void exitedVM();
// This version of didFire() is a more efficient version for when we want
// to know if the watchdog has fired in the past, and not whether it should
@@ -60,21 +69,17 @@
void* timerDidFireAddress() { return &m_timerDidFire; }
-private:
- void arm();
- void disarm();
- void startCountdownIfNeeded();
- void startCountdown(std::chrono::microseconds limit);
- void stopCountdown();
- bool isArmed() { return !!m_reentryCount; }
+ static const std::chrono::microseconds noTimeLimit;
- // Platform specific timer implementation:
- void initTimer();
- void destroyTimer();
- void startTimer(std::chrono::microseconds limit);
+private:
+ void startTimer(std::chrono::microseconds timeLimit);
void stopTimer();
- // m_timerDidFire (above) indicates whether the timer fired. The Watchdog
+ inline bool hasStartedTimer();
+
+ bool didFireSlow(ExecState*);
+
+ // m_timerDidFire indicates whether the timer fired. The Watchdog
// still needs to check if the allowed CPU time has elapsed. If so, then
// the Watchdog fires and m_didFire will be set.
// NOTE: m_timerDidFire is only set by the platform specific timer
@@ -82,47 +87,23 @@
bool m_timerDidFire;
bool m_didFire;
- std::chrono::microseconds m_timeoutPeriod;
- std::chrono::microseconds m_startCPUTime;
- std::chrono::microseconds m_elapsedCPUTime;
+ std::chrono::microseconds m_timeLimit;
- int m_reentryCount;
- bool m_isStopped;
+ std::chrono::microseconds m_cpuDeadline;
+ std::chrono::microseconds m_wallClockDeadline;
+ bool m_hasEnteredVM { false };
+
ShouldTerminateCallback m_callback;
void* m_callbackData1;
void* m_callbackData2;
-#if OS(DARWIN) && !PLATFORM(EFL) && !PLATFORM(GTK)
- dispatch_queue_t m_queue;
- dispatch_source_t m_timer;
-#endif
+ Ref<WorkQueue> m_timerQueue;
+ std::function<void ()> m_timerHandler;
- friend class Watchdog::Scope;
friend class LLIntOffsetsExtractor;
};
-class Watchdog::Scope {
-public:
- Scope(Watchdog* watchdog)
- : m_watchdog(watchdog)
- {
- if (!watchdog)
- return;
- m_watchdog->arm();
- }
-
- ~Scope()
- {
- if (!m_watchdog)
- return;
- m_watchdog->disarm();
- }
-
-private:
- Watchdog* m_watchdog;
-};
-
} // namespace JSC
#endif // Watchdog_h
Modified: trunk/Source/_javascript_Core/runtime/WatchdogMac.cpp (188328 => 188329)
--- trunk/Source/_javascript_Core/runtime/WatchdogMac.cpp 2015-08-12 05:54:14 UTC (rev 188328)
+++ trunk/Source/_javascript_Core/runtime/WatchdogMac.cpp 2015-08-12 05:56:20 UTC (rev 188329)
@@ -1,72 +0,0 @@
-/*
- * 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
- * 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 "Watchdog.h"
-
-namespace JSC {
-
-void Watchdog::initTimer()
-{
- m_queue = 0;
- m_timer = 0;
-}
-
-void Watchdog::destroyTimer()
-{
- ASSERT(!m_timer);
- if (m_queue)
- dispatch_release(m_queue);
-}
-
-void Watchdog::startTimer(std::chrono::microseconds limit)
-{
- ASSERT(!m_timer);
- if (!m_queue)
- m_queue = dispatch_queue_create("jsc.watchdog.queue", 0);
- m_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, m_queue);
-
- dispatch_source_set_timer(m_timer,
- dispatch_time(DISPATCH_TIME_NOW, std::chrono::nanoseconds(limit).count()),
- DISPATCH_TIME_FOREVER, 0);
-
- dispatch_source_set_event_handler(m_timer, ^{
- m_timerDidFire = true;
- });
-
- dispatch_resume(m_timer);
-}
-
-void Watchdog::stopTimer()
-{
- ASSERT(m_queue);
- dispatch_sync(m_queue, ^{
- dispatch_source_cancel(m_timer);
- });
- dispatch_release(m_timer);
- m_timer = 0;
-}
-
-} // namespace JSC
Modified: trunk/Source/_javascript_Core/runtime/WatchdogNone.cpp (188328 => 188329)
--- trunk/Source/_javascript_Core/runtime/WatchdogNone.cpp 2015-08-12 05:54:14 UTC (rev 188328)
+++ trunk/Source/_javascript_Core/runtime/WatchdogNone.cpp 2015-08-12 05:56:20 UTC (rev 188329)
@@ -1,50 +0,0 @@
-/*
- * 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
- * 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 "Watchdog.h"
-
-namespace JSC {
-
-// This is a stub for platforms that have not implemented this functionality.
-// In this case, the platform timer here never fires.
-
-void Watchdog::initTimer()
-{
-}
-
-void Watchdog::destroyTimer()
-{
-}
-
-void Watchdog::startTimer(std::chrono::microseconds)
-{
-}
-
-void Watchdog::stopTimer()
-{
-}
-
-} // namespace JSC
Modified: trunk/Source/WebCore/ChangeLog (188328 => 188329)
--- trunk/Source/WebCore/ChangeLog 2015-08-12 05:54:14 UTC (rev 188328)
+++ trunk/Source/WebCore/ChangeLog 2015-08-12 05:56:20 UTC (rev 188329)
@@ -1,3 +1,20 @@
+2015-08-11 Mark Lam <[email protected]>
+
+ Implementation _javascript_ watchdog using WTF::WorkQueue.
+ https://bugs.webkit.org/show_bug.cgi?id=147107
+
+ Reviewed by Geoffrey Garen.
+
+ No new tests because we're not introducing any behavior change to WebCore here.
+ We're only #include'ing Watchdog.h directly instead of going through VM.h.
+
+ * ForwardingHeaders/runtime/Watchdog.h: Added.
+ * PlatformEfl.cmake:
+ * WebCore.vcxproj/WebCore.vcxproj:
+ * WebCore.vcxproj/WebCore.vcxproj.filters:
+ * bindings/js/JSEventListener.cpp:
+ * bindings/js/WorkerScriptController.cpp:
+
2015-08-11 Simon Fraser <[email protected]>
[iOS WK2] ASSERT(!m_properties.backingStore || owner()) sometimes on zooming
Added: trunk/Source/WebCore/ForwardingHeaders/runtime/Watchdog.h (0 => 188329)
--- trunk/Source/WebCore/ForwardingHeaders/runtime/Watchdog.h (rev 0)
+++ trunk/Source/WebCore/ForwardingHeaders/runtime/Watchdog.h 2015-08-12 05:56:20 UTC (rev 188329)
@@ -0,0 +1,4 @@
+#ifndef WebCore_FWD_Watchdog_h
+#define WebCore_FWD_Watchdog_h
+#include <_javascript_Core/Watchdog.h>
+#endif
Modified: trunk/Source/WebCore/PlatformEfl.cmake (188328 => 188329)
--- trunk/Source/WebCore/PlatformEfl.cmake 2015-08-12 05:54:14 UTC (rev 188328)
+++ trunk/Source/WebCore/PlatformEfl.cmake 2015-08-12 05:56:20 UTC (rev 188329)
@@ -24,6 +24,7 @@
"${WEBCORE_DIR}/platform/network/soup"
"${WEBCORE_DIR}/platform/text/efl"
"${WEBCORE_DIR}/plugins/efl"
+ "${WTF_DIR}/wtf/efl"
)
list(APPEND WebCore_SOURCES
Modified: trunk/Source/WebCore/WebCore.vcxproj/WebCore.vcxproj (188328 => 188329)
--- trunk/Source/WebCore/WebCore.vcxproj/WebCore.vcxproj 2015-08-12 05:54:14 UTC (rev 188328)
+++ trunk/Source/WebCore/WebCore.vcxproj/WebCore.vcxproj 2015-08-12 05:56:20 UTC (rev 188329)
@@ -20436,6 +20436,7 @@
<ClInclude Include="..\ForwardingHeaders\runtime\Structure.h" />
<ClInclude Include="..\ForwardingHeaders\runtime\StructureChain.h" />
<ClInclude Include="..\ForwardingHeaders\runtime\SymbolTable.h" />
+ <ClInclude Include="..\ForwardingHeaders\runtime\Watchdog.h" />
<ClInclude Include="..\ForwardingHeaders\runtime\WeakGCMap.h" />
<ClInclude Include="..\ForwardingHeaders\runtime\WriteBarrier.h" />
<ClInclude Include="..\ForwardingHeaders\yarr\YarrJIT.h" />
Modified: trunk/Source/WebCore/WebCore.vcxproj/WebCore.vcxproj.filters (188328 => 188329)
--- trunk/Source/WebCore/WebCore.vcxproj/WebCore.vcxproj.filters 2015-08-12 05:54:14 UTC (rev 188328)
+++ trunk/Source/WebCore/WebCore.vcxproj/WebCore.vcxproj.filters 2015-08-12 05:56:20 UTC (rev 188329)
@@ -12198,6 +12198,9 @@
<ClInclude Include="..\ForwardingHeaders\runtime\SymbolTable.h">
<Filter>ForwardingHeaders\runtime</Filter>
</ClInclude>
+ <ClInclude Include="..\ForwardingHeaders\runtime\Watchdog.h">
+ <Filter>ForwardingHeaders\runtime</Filter>
+ </ClInclude>
<ClInclude Include="..\ForwardingHeaders\runtime\WeakGCMap.h">
<Filter>ForwardingHeaders\runtime</Filter>
</ClInclude>
Modified: trunk/Source/WebCore/bindings/js/JSEventListener.cpp (188328 => 188329)
--- trunk/Source/WebCore/bindings/js/JSEventListener.cpp 2015-08-12 05:54:14 UTC (rev 188328)
+++ trunk/Source/WebCore/bindings/js/JSEventListener.cpp 2015-08-12 05:56:20 UTC (rev 188329)
@@ -34,6 +34,7 @@
#include <runtime/ExceptionHelpers.h>
#include <runtime/JSLock.h>
#include <runtime/VMEntryScope.h>
+#include <runtime/Watchdog.h>
#include <wtf/Ref.h>
#include <wtf/RefCountedLeakCounter.h>
Modified: trunk/Source/WebCore/bindings/js/WorkerScriptController.cpp (188328 => 188329)
--- trunk/Source/WebCore/bindings/js/WorkerScriptController.cpp 2015-08-12 05:54:14 UTC (rev 188328)
+++ trunk/Source/WebCore/bindings/js/WorkerScriptController.cpp 2015-08-12 05:56:20 UTC (rev 188329)
@@ -44,6 +44,7 @@
#include <runtime/ExceptionHelpers.h>
#include <runtime/Error.h>
#include <runtime/JSLock.h>
+#include <runtime/Watchdog.h>
using namespace JSC;