The JSC shell should listen for memory pressure events and respond to them
authorsbarati@apple.com <sbarati@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 14 Dec 2018 04:05:41 +0000 (04:05 +0000)
committersbarati@apple.com <sbarati@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 14 Dec 2018 04:05:41 +0000 (04:05 +0000)
https://bugs.webkit.org/show_bug.cgi?id=192647

Reviewed by Keith Miller.

Source/JavaScriptCore:

We want the JSC shell to behave more like the WebContent process when
it comes to running performance tests. One way to make the shell
more like this is to have it respond to memory pressure events in
a similar way as the WebContent process. This makes it easier to run
benchmarks like JetStream2 on the CLI on iOS.

* jsc.cpp:
(jscmain):
* runtime/VM.cpp:
(JSC::VM::drainMicrotasks):
* runtime/VM.h:
(JSC::VM::setOnEachMicrotaskTick):

Source/WTF:

* wtf/MemoryPressureHandler.cpp:
(WTF::MemoryPressureHandler::MemoryPressureHandler):
(WTF::MemoryPressureHandler::setDispatchQueue):
Make it so that we can customize which dispatch queue memory pressure
events get handled on.

* wtf/MemoryPressureHandler.h:
(WTF::MemoryPressureHandler::setShouldLogMemoryMemoryPressureEvents):
Make it so that we can disable logging that happens on each memory
pressure event.

* wtf/cocoa/MemoryPressureHandlerCocoa.mm:
(WTF::MemoryPressureHandler::install):
(WTF::MemoryPressureHandler::uninstall):
(WTF::MemoryPressureHandler::holdOff):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@239195 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/jsc.cpp
Source/JavaScriptCore/runtime/VM.cpp
Source/JavaScriptCore/runtime/VM.h
Source/WTF/ChangeLog
Source/WTF/wtf/MemoryPressureHandler.cpp
Source/WTF/wtf/MemoryPressureHandler.h
Source/WTF/wtf/cocoa/MemoryPressureHandlerCocoa.mm

index fd1204f..cf999f4 100644 (file)
@@ -1,3 +1,23 @@
+2018-12-13  Saam Barati  <sbarati@apple.com>
+
+        The JSC shell should listen for memory pressure events and respond to them
+        https://bugs.webkit.org/show_bug.cgi?id=192647
+
+        Reviewed by Keith Miller.
+
+        We want the JSC shell to behave more like the WebContent process when
+        it comes to running performance tests. One way to make the shell
+        more like this is to have it respond to memory pressure events in
+        a similar way as the WebContent process. This makes it easier to run
+        benchmarks like JetStream2 on the CLI on iOS.
+
+        * jsc.cpp:
+        (jscmain):
+        * runtime/VM.cpp:
+        (JSC::VM::drainMicrotasks):
+        * runtime/VM.h:
+        (JSC::VM::setOnEachMicrotaskTick):
+
 2018-12-13  Mark Lam  <mark.lam@apple.com>
 
         Ensure that StructureFlags initialization always starts with Base::StructureFlags.
index a4a57f8..a84cc23 100644 (file)
 #include <sys/types.h>
 #include <thread>
 #include <type_traits>
+#include <wtf/Box.h>
 #include <wtf/CommaPrinter.h>
 #include <wtf/MainThread.h>
+#include <wtf/MemoryPressureHandler.h>
 #include <wtf/MonotonicTime.h>
 #include <wtf/NeverDestroyed.h>
 #include <wtf/StringPrintStream.h>
@@ -2871,9 +2873,49 @@ int jscmain(int argc, char** argv)
 #endif
     Gigacage::disableDisablingPrimitiveGigacageIfShouldBeEnabled();
 
+#if PLATFORM(COCOA)
+    auto& memoryPressureHandler = MemoryPressureHandler::singleton();
+    {
+        dispatch_queue_t queue = dispatch_queue_create("jsc shell memory pressure handler", DISPATCH_QUEUE_SERIAL);
+        memoryPressureHandler.setDispatchQueue(queue);
+        dispatch_release(queue);
+    }
+    Box<Critical> memoryPressureCriticalState = Box<Critical>::create(Critical::No);
+    Box<Synchronous> memoryPressureSynchronousState = Box<Synchronous>::create(Synchronous::No);
+    memoryPressureHandler.setLowMemoryHandler([=] (Critical critical, Synchronous synchronous) {
+        // We set these racily with respect to reading them from the JS execution thread.
+        *memoryPressureCriticalState = critical;
+        *memoryPressureSynchronousState = synchronous;
+    });
+    memoryPressureHandler.setShouldLogMemoryMemoryPressureEvents(false);
+    memoryPressureHandler.install();
+
+    auto onEachMicrotaskTick = [&] (VM& vm) {
+        if (*memoryPressureCriticalState == Critical::No)
+            return;
+
+        *memoryPressureCriticalState = Critical::No;
+        bool isSynchronous = *memoryPressureSynchronousState == Synchronous::Yes;
+
+        WTF::releaseFastMallocFreeMemory();
+        vm.deleteAllCode(DeleteAllCodeIfNotCollecting);
+
+        if (!vm.heap.isCurrentThreadBusy()) {
+            if (isSynchronous) {
+                vm.heap.collectNow(Sync, CollectionScope::Full);
+                WTF::releaseFastMallocFreeMemory();
+            } else
+                vm.heap.collectNowFullIfNotDoneRecently(Async);
+        }
+    };
+#endif
+
     int result = runJSC(
         options, false,
-        [&] (VM&, GlobalObject* globalObject, bool& success) {
+        [&] (VM& vm, GlobalObject* globalObject, bool& success) {
+#if PLATFORM(COCOA)
+            vm.setOnEachMicrotaskTick(WTFMove(onEachMicrotaskTick));
+#endif
             runWithOptions(globalObject, options, success);
         });
 
index a774fa5..d83269b 100644 (file)
@@ -1128,8 +1128,11 @@ void VM::queueMicrotask(JSGlobalObject& globalObject, Ref<Microtask>&& task)
 
 void VM::drainMicrotasks()
 {
-    while (!m_microtaskQueue.isEmpty())
+    while (!m_microtaskQueue.isEmpty()) {
         m_microtaskQueue.takeFirst()->run();
+        if (m_onEachMicrotaskTick)
+            m_onEachMicrotaskTick(*this);
+    }
 }
 
 void QueuedTask::run()
index 0fb7b82..865e5f7 100644 (file)
@@ -857,6 +857,7 @@ public:
 
     void queueMicrotask(JSGlobalObject&, Ref<Microtask>&&);
     JS_EXPORT_PRIVATE void drainMicrotasks();
+    ALWAYS_INLINE void setOnEachMicrotaskTick(WTF::Function<void(VM&)>&& func) { m_onEachMicrotaskTick = WTFMove(func); }
     void setGlobalConstRedeclarationShouldThrow(bool globalConstRedeclarationThrow) { m_globalConstRedeclarationShouldThrow = globalConstRedeclarationThrow; }
     ALWAYS_INLINE bool globalConstRedeclarationShouldThrow() const { return m_globalConstRedeclarationShouldThrow; }
 
@@ -1016,6 +1017,8 @@ private:
     std::unique_ptr<ShadowChicken> m_shadowChicken;
     std::unique_ptr<BytecodeIntrinsicRegistry> m_bytecodeIntrinsicRegistry;
 
+    WTF::Function<void(VM&)> m_onEachMicrotaskTick;
+
 #if ENABLE(JIT)
 #if !ASSERT_DISABLED
     JS_EXPORT_PRIVATE static bool s_canUseJITIsSet;
index cb88b9c..7a995d7 100644 (file)
@@ -1,3 +1,26 @@
+2018-12-13  Saam Barati  <sbarati@apple.com>
+
+        The JSC shell should listen for memory pressure events and respond to them
+        https://bugs.webkit.org/show_bug.cgi?id=192647
+
+        Reviewed by Keith Miller.
+
+        * wtf/MemoryPressureHandler.cpp:
+        (WTF::MemoryPressureHandler::MemoryPressureHandler):
+        (WTF::MemoryPressureHandler::setDispatchQueue):
+        Make it so that we can customize which dispatch queue memory pressure
+        events get handled on.
+
+        * wtf/MemoryPressureHandler.h:
+        (WTF::MemoryPressureHandler::setShouldLogMemoryMemoryPressureEvents):
+        Make it so that we can disable logging that happens on each memory
+        pressure event.
+
+        * wtf/cocoa/MemoryPressureHandlerCocoa.mm:
+        (WTF::MemoryPressureHandler::install):
+        (WTF::MemoryPressureHandler::uninstall):
+        (WTF::MemoryPressureHandler::holdOff):
+
 2018-12-13  David Kilzer  <ddkilzer@apple.com>
 
         clang-tidy: Fix unnecessary parameter copies in ParallelHelperPool.cpp
index 63d0f02..6d78f9f 100644 (file)
@@ -55,6 +55,9 @@ MemoryPressureHandler::MemoryPressureHandler()
     : m_windowsMeasurementTimer(RunLoop::main(), this, &MemoryPressureHandler::windowsMeasurementTimerFired)
 #endif
 {
+#if PLATFORM(COCOA)
+    setDispatchQueue(dispatch_get_main_queue());
+#endif
 }
 
 void MemoryPressureHandler::setShouldUsePeriodicMemoryMonitor(bool use)
@@ -298,4 +301,15 @@ void MemoryPressureHandler::ReliefLogger::logMemoryUsageChange()
 void MemoryPressureHandler::platformInitialize() { }
 #endif
 
+#if PLATFORM(COCOA)
+void MemoryPressureHandler::setDispatchQueue(dispatch_queue_t queue)
+{
+    RELEASE_ASSERT(!m_installed);
+    dispatch_retain(queue);
+    if (m_dispatchQueue)
+        dispatch_release(m_dispatchQueue);
+    m_dispatchQueue = queue;
+}
+#endif
+
 } // namespace WebCore
index 83ff699..0f8b0f5 100644 (file)
@@ -94,6 +94,10 @@ public:
 
     WTF_EXPORT_PRIVATE static MemoryUsagePolicy currentMemoryUsagePolicy();
 
+#if PLATFORM(COCOA)
+    WTF_EXPORT_PRIVATE void setDispatchQueue(dispatch_queue_t);
+#endif
+
     class ReliefLogger {
     public:
         explicit ReliefLogger(const char *log)
@@ -150,6 +154,8 @@ public:
 
     WTF_EXPORT_PRIVATE static void setPageCount(unsigned);
 
+    void setShouldLogMemoryMemoryPressureEvents(bool shouldLog) { m_shouldLogMemoryMemoryPressureEvents = shouldLog; }
+
 private:
     size_t thresholdForMemoryKill();
     void memoryPressureStatusChanged();
@@ -180,6 +186,7 @@ private:
 
     std::atomic<bool> m_underMemoryPressure;
     bool m_isSimulatingMemoryPressure { false };
+    bool m_shouldLogMemoryMemoryPressureEvents { true };
 
     std::unique_ptr<RunLoop::Timer<MemoryPressureHandler>> m_measurementTimer;
     MemoryUsagePolicy m_memoryUsagePolicy { MemoryUsagePolicy::Unrestricted };
@@ -198,6 +205,10 @@ private:
     RunLoop::Timer<MemoryPressureHandler> m_holdOffTimer;
     void holdOffTimerFired();
 #endif
+
+#if PLATFORM(COCOA)
+    dispatch_queue_t m_dispatchQueue { nullptr };
+#endif
 };
 
 extern WTFLogChannel LogMemoryPressure;
index bd0fe67..9faa36b 100644 (file)
@@ -67,13 +67,13 @@ void MemoryPressureHandler::install()
     if (m_installed || timerEventSource)
         return;
 
-    dispatch_async(dispatch_get_main_queue(), ^{
+    dispatch_async(m_dispatchQueue, ^{
 #if PLATFORM(IOS_FAMILY)
         auto memoryStatusFlags = DISPATCH_MEMORYPRESSURE_NORMAL | DISPATCH_MEMORYPRESSURE_WARN | DISPATCH_MEMORYPRESSURE_CRITICAL | DISPATCH_MEMORYPRESSURE_PROC_LIMIT_WARN | DISPATCH_MEMORYPRESSURE_PROC_LIMIT_CRITICAL;
 #else // PLATFORM(MAC)
         auto memoryStatusFlags = DISPATCH_MEMORYPRESSURE_CRITICAL;
 #endif
-        memoryPressureEventSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_MEMORYPRESSURE, 0, memoryStatusFlags, dispatch_get_main_queue());
+        memoryPressureEventSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_MEMORYPRESSURE, 0, memoryStatusFlags, m_dispatchQueue);
 
         dispatch_source_set_event_handler(memoryPressureEventSource, ^{
             auto status = dispatch_source_get_data(memoryPressureEventSource);
@@ -102,13 +102,14 @@ void MemoryPressureHandler::install()
 #else // PLATFORM(MAC)
             respondToMemoryPressure(Critical::Yes);
 #endif
-            WTFLogAlways("Received memory pressure event %lu vm pressure %d", status, isUnderMemoryPressure());
+            if (m_shouldLogMemoryMemoryPressureEvents)
+                WTFLogAlways("Received memory pressure event %lu vm pressure %d", status, isUnderMemoryPressure());
         });
         dispatch_resume(memoryPressureEventSource);
     });
 
     // Allow simulation of memory pressure with "notifyutil -p org.WebKit.lowMemory"
-    notify_register_dispatch("org.WebKit.lowMemory", &notifyTokens[0], dispatch_get_main_queue(), ^(int) {
+    notify_register_dispatch("org.WebKit.lowMemory", &notifyTokens[0], m_dispatchQueue, ^(int) {
 #if ENABLE(FMW_FOOTPRINT_COMPARISON)
         auto footprintBefore = pagesPerVMTag();
 #endif
@@ -122,15 +123,15 @@ void MemoryPressureHandler::install()
         logFootprintComparison(footprintBefore, footprintAfter);
 #endif
 
-        dispatch_async(dispatch_get_main_queue(), ^{
+        dispatch_async(m_dispatchQueue, ^{
             endSimulatedMemoryPressure();
         });
     });
 
-    notify_register_dispatch("org.WebKit.lowMemory.begin", &notifyTokens[1], dispatch_get_main_queue(), ^(int) {
+    notify_register_dispatch("org.WebKit.lowMemory.begin", &notifyTokens[1], m_dispatchQueue, ^(int) {
         beginSimulatedMemoryPressure();
     });
-    notify_register_dispatch("org.WebKit.lowMemory.end", &notifyTokens[2], dispatch_get_main_queue(), ^(int) {
+    notify_register_dispatch("org.WebKit.lowMemory.end", &notifyTokens[2], m_dispatchQueue, ^(int) {
         endSimulatedMemoryPressure();
     });
 
@@ -142,7 +143,7 @@ void MemoryPressureHandler::uninstall()
     if (!m_installed)
         return;
 
-    dispatch_async(dispatch_get_main_queue(), ^{
+    dispatch_async(m_dispatchQueue, ^{
         if (memoryPressureEventSource) {
             dispatch_source_cancel(memoryPressureEventSource);
             memoryPressureEventSource = nullptr;
@@ -162,8 +163,8 @@ void MemoryPressureHandler::uninstall()
 
 void MemoryPressureHandler::holdOff(Seconds seconds)
 {
-    dispatch_async(dispatch_get_main_queue(), ^{
-        timerEventSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue());
+    dispatch_async(m_dispatchQueue, ^{
+        timerEventSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, m_dispatchQueue);
         if (timerEventSource) {
             dispatch_set_context(timerEventSource, this);
             // FIXME: The final argument `s_minimumHoldOffTime.seconds()` seems wrong.