Title: [275375] trunk/Source
Revision
275375
Author
[email protected]
Date
2021-04-01 13:05:48 -0700 (Thu, 01 Apr 2021)

Log Message

Have the ServiceWorker process hold on to a file mapped version of the service worker scripts to save dirty memory
https://bugs.webkit.org/show_bug.cgi?id=224015
<rdar://75637679>

Reviewed by Geoffrey Garen.

Source/WebCore:

Since r275267, the Network process holds on the file mapped (mmap'd) versions of the service worker
scripts instead of heap allocated versions, in order to decrease its dirty memory use. However, the
ServiceWorker process (which is often a regular WebProcess) was still using heap allocated service
worker scripts. This patch is a follow-up to make sure the NetworkProcess sends its file mapped
scripts to the ServiceWorker processes as ShareableResource handles in order to decrease the dirty
memory usage of the ServiceWorker processes as well.

No new tests, no Web-facing behavior change, just a decrease in dirty memory use in the ServiceWorker
processes (which may be WebProcesses). I have done local testing with a very large service worker
that uses a ~100MB main script, which imports another ~100MB sub-script. With my change, dirty
memory usage goes from ~440MB to ~230MB in both the cold and warm cases ("Cold" meaning that the
service worker was just registed and downloaded from the network and "Warm" meaning that the
service worker had been previously registed and was loaded straight from the SWScriptStorage).

* platform/SharedBuffer.cpp:
(WebCore::SharedBuffer::hasOneSegment const):
Add a utility function to SharedBuffer to check if it contains a single data segment.

(WebCore::SharedBuffer::DataSegment::containsMappedFileData const):
Add a utility function to check if a SharedBuffer DataSegment contains a MappedFileData object.

* platform/SharedBuffer.h:

* workers/service/ServiceWorkerContextData.h:
(WebCore::ServiceWorkerContextData::ImportedScript::isolatedCopy const):
Move IPC encoders / decoders for ServiceWorkerContextData and ServiceWorkerContextData::ImportedScript
to the WebKit layer, in WebCoreArgumentCoders. This allows us to encode / decode the scripts as
WebKit::ShareableHandle whenever possible. This way, when the NetworkProcess sends a
ServiceWorkerContextData to the ServiceWorker process to launch a service worker, both the
ServiceWorker process and the Network process share the same mmap'd versions of the scripts and we
save on dirty memory use. This helps reduce dirty memory use in the ServiceWorker process in the
warm case, where the scripts are loaded straight from the disk (via SWScriptStorage).

* workers/service/ServiceWorkerGlobalScope.cpp:
(WebCore::ServiceWorkerGlobalScope::didSaveScriptsToDisk):
* workers/service/ServiceWorkerGlobalScope.h:
* workers/service/context/SWContextManager.cpp:
(WebCore::SWContextManager::didSaveScriptsToDisk):
* workers/service/context/SWContextManager.h:
* workers/service/context/ServiceWorkerThreadProxy.cpp:
(WebCore::ServiceWorkerThreadProxy::didSaveScriptsToDisk):
* workers/service/context/ServiceWorkerThreadProxy.h:
* workers/service/server/SWServerToContextConnection.h:
* workers/service/server/SWServerWorker.cpp:
(WebCore::SWServerWorker::didSaveScriptsToDisk):
In the cold case, once the NetworkProcess is done saving the scripts to disk, it now sends the
file mapped version of the scripts to the ServiceWorker process, so that it can also replace
its heap-allocated copies and save on dirty memory use.

Source/WebKit:

* NetworkProcess/ServiceWorker/WebSWServerToContextConnection.cpp:
(WebKit::WebSWServerToContextConnection::didSaveScriptsToDisk):
* NetworkProcess/ServiceWorker/WebSWServerToContextConnection.h:
* WebProcess/Storage/WebSWContextManagerConnection.cpp:
(WebKit::WebSWContextManagerConnection::didSaveScriptsToDisk):
* WebProcess/Storage/WebSWContextManagerConnection.h:
* WebProcess/Storage/WebSWContextManagerConnection.messages.in:
When the NetworkProcess is done saving the service worker scripts to disk and it gets
a file mapped version, it now sends them to the ServiceWorker process as ShareableResource
handles via IPC. This allows the ServiceWorker process to replace its heap-allocated
versions of the script and save on dirty memory use (in the cold case).

* Shared/ShareableResource.h:
Fix bug where 2 of ShareableResource data members were not properly initialized by the default
constructor.

* Shared/WebCoreArgumentCoders.cpp:
(IPC::encodeServiceWorkerContextDataScript):
(IPC::decodeServiceWorkerContextDataScript):
(IPC::ArgumentCoder<ServiceWorkerContextData::ImportedScript>::encode):
(IPC::ArgumentCoder<ServiceWorkerContextData::ImportedScript>::decode):
(IPC::ArgumentCoder<ServiceWorkerContextData>::encode):
(IPC::ArgumentCoder<ServiceWorkerContextData>::decode):
(IPC::tryConvertToShareableResourceHandle):
* Shared/WebCoreArgumentCoders.h:
Moved IPC encoders / decoders for ServiceWorkerContextData and ServiceWorkerContextData::ImportedScript
to the WebKit layer, in WebCoreArgumentCoders. This allows us to encode / decode the scripts as
WebKit::ShareableHandle whenever possible. This way, when the NetworkProcess sends a
ServiceWorkerContextData to the ServiceWorker process to launch a service worker, both the
ServiceWorker process and the Network process share the same mmap'd versions of the scripts and we
save on dirty memory use. This helps reduce dirty memory use in the ServiceWorker process in the
warm case, where the scripts are loaded straight from the disk (via SWScriptStorage).

Modified Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (275374 => 275375)


--- trunk/Source/WebCore/ChangeLog	2021-04-01 20:02:50 UTC (rev 275374)
+++ trunk/Source/WebCore/ChangeLog	2021-04-01 20:05:48 UTC (rev 275375)
@@ -1,3 +1,60 @@
+2021-04-01  Chris Dumez  <[email protected]>
+
+        Have the ServiceWorker process hold on to a file mapped version of the service worker scripts to save dirty memory
+        https://bugs.webkit.org/show_bug.cgi?id=224015
+        <rdar://75637679>
+
+        Reviewed by Geoffrey Garen.
+
+        Since r275267, the Network process holds on the file mapped (mmap'd) versions of the service worker
+        scripts instead of heap allocated versions, in order to decrease its dirty memory use. However, the
+        ServiceWorker process (which is often a regular WebProcess) was still using heap allocated service
+        worker scripts. This patch is a follow-up to make sure the NetworkProcess sends its file mapped
+        scripts to the ServiceWorker processes as ShareableResource handles in order to decrease the dirty
+        memory usage of the ServiceWorker processes as well.
+
+        No new tests, no Web-facing behavior change, just a decrease in dirty memory use in the ServiceWorker
+        processes (which may be WebProcesses). I have done local testing with a very large service worker
+        that uses a ~100MB main script, which imports another ~100MB sub-script. With my change, dirty
+        memory usage goes from ~440MB to ~230MB in both the cold and warm cases ("Cold" meaning that the
+        service worker was just registed and downloaded from the network and "Warm" meaning that the
+        service worker had been previously registed and was loaded straight from the SWScriptStorage).
+
+        * platform/SharedBuffer.cpp:
+        (WebCore::SharedBuffer::hasOneSegment const):
+        Add a utility function to SharedBuffer to check if it contains a single data segment.
+
+        (WebCore::SharedBuffer::DataSegment::containsMappedFileData const):
+        Add a utility function to check if a SharedBuffer DataSegment contains a MappedFileData object.
+
+        * platform/SharedBuffer.h:
+
+        * workers/service/ServiceWorkerContextData.h:
+        (WebCore::ServiceWorkerContextData::ImportedScript::isolatedCopy const):
+        Move IPC encoders / decoders for ServiceWorkerContextData and ServiceWorkerContextData::ImportedScript
+        to the WebKit layer, in WebCoreArgumentCoders. This allows us to encode / decode the scripts as
+        WebKit::ShareableHandle whenever possible. This way, when the NetworkProcess sends a
+        ServiceWorkerContextData to the ServiceWorker process to launch a service worker, both the
+        ServiceWorker process and the Network process share the same mmap'd versions of the scripts and we
+        save on dirty memory use. This helps reduce dirty memory use in the ServiceWorker process in the
+        warm case, where the scripts are loaded straight from the disk (via SWScriptStorage).
+
+        * workers/service/ServiceWorkerGlobalScope.cpp:
+        (WebCore::ServiceWorkerGlobalScope::didSaveScriptsToDisk):
+        * workers/service/ServiceWorkerGlobalScope.h:
+        * workers/service/context/SWContextManager.cpp:
+        (WebCore::SWContextManager::didSaveScriptsToDisk):
+        * workers/service/context/SWContextManager.h:
+        * workers/service/context/ServiceWorkerThreadProxy.cpp:
+        (WebCore::ServiceWorkerThreadProxy::didSaveScriptsToDisk):
+        * workers/service/context/ServiceWorkerThreadProxy.h:
+        * workers/service/server/SWServerToContextConnection.h:
+        * workers/service/server/SWServerWorker.cpp:
+        (WebCore::SWServerWorker::didSaveScriptsToDisk):
+        In the cold case, once the NetworkProcess is done saving the scripts to disk, it now sends the
+        file mapped version of the scripts to the ServiceWorker process, so that it can also replace
+        its heap-allocated copies and save on dirty memory use.
+
 2021-04-01  Fujii Hironori  <[email protected]>
 
         [WebGL] Use GraphicsContextGLOpenGLManager for ports using TextureMapper

Modified: trunk/Source/WebCore/platform/SharedBuffer.cpp (275374 => 275375)


--- trunk/Source/WebCore/platform/SharedBuffer.cpp	2021-04-01 20:02:50 UTC (rev 275374)
+++ trunk/Source/WebCore/platform/SharedBuffer.cpp	2021-04-01 20:05:48 UTC (rev 275375)
@@ -216,6 +216,12 @@
     return clone;
 }
 
+bool SharedBuffer::hasOneSegment() const
+{
+    auto it = begin();
+    return it != end() && ++it == end();
+}
+
 #if ASSERT_ENABLED
 bool SharedBuffer::internallyConsistent() const
 {
@@ -247,6 +253,11 @@
     return WTF::visit(visitor, m_immutableData);
 }
 
+bool SharedBuffer::DataSegment::containsMappedFileData() const
+{
+    return WTF::holds_alternative<FileSystem::MappedFileData>(m_immutableData);
+}
+
 #if !USE(CF)
 void SharedBuffer::hintMemoryNotNeededSoon() const
 {

Modified: trunk/Source/WebCore/platform/SharedBuffer.h (275374 => 275375)


--- trunk/Source/WebCore/platform/SharedBuffer.h	2021-04-01 20:02:50 UTC (rev 275374)
+++ trunk/Source/WebCore/platform/SharedBuffer.h	2021-04-01 20:05:48 UTC (rev 275375)
@@ -146,6 +146,8 @@
         RetainPtr<NSData> createNSData() const;
 #endif
 
+        bool containsMappedFileData() const;
+
     private:
         DataSegment(Vector<char>&& data)
             : m_immutableData(WTFMove(data)) { }
@@ -185,6 +187,7 @@
     using DataSegmentVector = Vector<DataSegmentVectorEntry, 1>;
     DataSegmentVector::const_iterator begin() const { return m_segments.begin(); }
     DataSegmentVector::const_iterator end() const { return m_segments.end(); }
+    bool hasOneSegment() const;
     
     // begin and end take O(1) time, this takes O(log(N)) time.
     SharedBufferDataView getSomeData(size_t position) const;

Modified: trunk/Source/WebCore/workers/service/ServiceWorkerContextData.h (275374 => 275375)


--- trunk/Source/WebCore/workers/service/ServiceWorkerContextData.h	2021-04-01 20:02:50 UTC (rev 275374)
+++ trunk/Source/WebCore/workers/service/ServiceWorkerContextData.h	2021-04-01 20:05:48 UTC (rev 275375)
@@ -50,35 +50,6 @@
         String mimeType;
 
         ImportedScript isolatedCopy() const { return { script->copy(), responseURL.isolatedCopy(), mimeType.isolatedCopy() }; }
-
-        template<class Encoder> void encode(Encoder& encoder) const
-        {
-            encoder << script << responseURL << mimeType;
-        }
-
-        template<class Decoder> static Optional<ImportedScript> decode(Decoder& decoder)
-        {
-            Optional<RefPtr<SharedBuffer>> script;
-            decoder >> script;
-            if (!script)
-                return WTF::nullopt;
-            
-            Optional<URL> responseURL;
-            decoder >> responseURL;
-            if (!responseURL)
-                return WTF::nullopt;
-            
-            Optional<String> mimeType;
-            decoder >> mimeType;
-            if (!mimeType)
-                return WTF::nullopt;
-            
-            return {{
-                WTFMove(*script),
-                WTFMove(*responseURL),
-                WTFMove(*mimeType)
-            }};
-        }
     };
 
     Optional<ServiceWorkerJobDataIdentifier> jobDataIdentifier;
@@ -93,86 +64,9 @@
     bool loadedFromDisk;
     HashMap<URL, ImportedScript> scriptResourceMap;
 
-    template<class Encoder> void encode(Encoder&) const;
-    template<class Decoder> static Optional<ServiceWorkerContextData> decode(Decoder&);
-
     ServiceWorkerContextData isolatedCopy() const;
 };
 
-template<class Encoder>
-void ServiceWorkerContextData::encode(Encoder& encoder) const
-{
-    encoder << jobDataIdentifier << registration << serviceWorkerIdentifier << script << contentSecurityPolicy << referrerPolicy << scriptURL << workerType << loadedFromDisk;
-    encoder << scriptResourceMap;
-    encoder << certificateInfo;
-}
-
-template<class Decoder>
-Optional<ServiceWorkerContextData> ServiceWorkerContextData::decode(Decoder& decoder)
-{
-    Optional<Optional<ServiceWorkerJobDataIdentifier>> jobDataIdentifier;
-    decoder >> jobDataIdentifier;
-    if (!jobDataIdentifier)
-        return WTF::nullopt;
-
-    Optional<ServiceWorkerRegistrationData> registration;
-    decoder >> registration;
-    if (!registration)
-        return WTF::nullopt;
-
-    auto serviceWorkerIdentifier = ServiceWorkerIdentifier::decode(decoder);
-    if (!serviceWorkerIdentifier)
-        return WTF::nullopt;
-
-    Optional<Ref<SharedBuffer>> script;
-    decoder >> script;
-    if (!script)
-        return WTF::nullopt;
-
-    ContentSecurityPolicyResponseHeaders contentSecurityPolicy;
-    if (!decoder.decode(contentSecurityPolicy))
-        return WTF::nullopt;
-
-    String referrerPolicy;
-    if (!decoder.decode(referrerPolicy))
-        return WTF::nullopt;
-
-    URL scriptURL;
-    if (!decoder.decode(scriptURL))
-        return WTF::nullopt;
-    
-    WorkerType workerType;
-    if (!decoder.decode(workerType))
-        return WTF::nullopt;
-
-    bool loadedFromDisk;
-    if (!decoder.decode(loadedFromDisk))
-        return WTF::nullopt;
-
-    HashMap<URL, ImportedScript> scriptResourceMap;
-    if (!decoder.decode(scriptResourceMap))
-        return WTF::nullopt;
-
-    Optional<CertificateInfo> certificateInfo;
-    decoder >> certificateInfo;
-    if (!certificateInfo)
-        return WTF::nullopt;
-    
-    return {{
-        WTFMove(*jobDataIdentifier),
-        WTFMove(*registration),
-        WTFMove(*serviceWorkerIdentifier),
-        WTFMove(*script),
-        WTFMove(*certificateInfo),
-        WTFMove(contentSecurityPolicy),
-        WTFMove(referrerPolicy),
-        WTFMove(scriptURL),
-        workerType,
-        loadedFromDisk,
-        WTFMove(scriptResourceMap)
-    }};
-}
-
 } // namespace WebCore
 
 #endif // ENABLE(SERVICE_WORKER)

Modified: trunk/Source/WebCore/workers/service/ServiceWorkerGlobalScope.cpp (275374 => 275375)


--- trunk/Source/WebCore/workers/service/ServiceWorkerGlobalScope.cpp	2021-04-01 20:02:50 UTC (rev 275374)
+++ trunk/Source/WebCore/workers/service/ServiceWorkerGlobalScope.cpp	2021-04-01 20:05:48 UTC (rev 275375)
@@ -155,6 +155,22 @@
     m_contextData.scriptResourceMap.set(url, WTFMove(script));
 }
 
+void ServiceWorkerGlobalScope::didSaveScriptsToDisk(RefPtr<SharedBuffer>&& script, HashMap<URL, RefPtr<SharedBuffer>>&& importedScripts)
+{
+    // These scripts should be identical to the ones we have. However, these are mmap'd so using them helps reduce dirty memory usage.
+    if (script) {
+        ASSERT(m_contextData.script.get() == *script);
+        m_contextData.script = script.releaseNonNull();
+    }
+    for (auto& pair : importedScripts) {
+        auto it = m_contextData.scriptResourceMap.find(pair.key);
+        if (it == m_contextData.scriptResourceMap.end())
+            continue;
+        ASSERT(*it->value.script == *pair.value); // Do a memcmp to make sure the scripts are identical.
+        it->value.script = WTFMove(pair.value);
+    }
+}
+
 } // namespace WebCore
 
 #endif // ENABLE(SERVICE_WORKER)

Modified: trunk/Source/WebCore/workers/service/ServiceWorkerGlobalScope.h (275374 => 275375)


--- trunk/Source/WebCore/workers/service/ServiceWorkerGlobalScope.h	2021-04-01 20:02:50 UTC (rev 275374)
+++ trunk/Source/WebCore/workers/service/ServiceWorkerGlobalScope.h	2021-04-01 20:05:48 UTC (rev 275375)
@@ -68,6 +68,8 @@
     const ServiceWorkerContextData::ImportedScript* scriptResource(const URL&) const;
     void setScriptResource(const URL&, ServiceWorkerContextData::ImportedScript&&);
 
+    void didSaveScriptsToDisk(RefPtr<SharedBuffer>&& script, HashMap<URL, RefPtr<SharedBuffer>>&& importedScripts);
+
     const ServiceWorkerContextData& contextData() const { return m_contextData; }
     const CertificateInfo& certificateInfo() const { return m_contextData.certificateInfo; }
 

Modified: trunk/Source/WebCore/workers/service/context/SWContextManager.cpp (275374 => 275375)


--- trunk/Source/WebCore/workers/service/context/SWContextManager.cpp	2021-04-01 20:02:50 UTC (rev 275374)
+++ trunk/Source/WebCore/workers/service/context/SWContextManager.cpp	2021-04-01 20:05:48 UTC (rev 275375)
@@ -120,6 +120,12 @@
     stopWorker(*serviceWorker, timeout, WTFMove(completionHandler));
 }
 
+void SWContextManager::didSaveScriptsToDisk(ServiceWorkerIdentifier identifier, RefPtr<SharedBuffer>&& script, HashMap<URL, RefPtr<SharedBuffer>>&& importedScripts)
+{
+    if (auto serviceWorker = m_workerMap.get(identifier))
+        serviceWorker->didSaveScriptsToDisk(WTFMove(script), WTFMove(importedScripts));
+}
+
 void SWContextManager::stopWorker(ServiceWorkerThreadProxy& serviceWorker, Seconds timeout, Function<void()>&& completionHandler)
 {
     auto identifier = serviceWorker.identifier();

Modified: trunk/Source/WebCore/workers/service/context/SWContextManager.h (275374 => 275375)


--- trunk/Source/WebCore/workers/service/context/SWContextManager.h	2021-04-01 20:02:50 UTC (rev 275374)
+++ trunk/Source/WebCore/workers/service/context/SWContextManager.h	2021-04-01 20:05:48 UTC (rev 275375)
@@ -90,6 +90,7 @@
     WEBCORE_EXPORT void fireInstallEvent(ServiceWorkerIdentifier);
     WEBCORE_EXPORT void fireActivateEvent(ServiceWorkerIdentifier);
     WEBCORE_EXPORT void terminateWorker(ServiceWorkerIdentifier, Seconds timeout, Function<void()>&&);
+    WEBCORE_EXPORT void didSaveScriptsToDisk(ServiceWorkerIdentifier, RefPtr<SharedBuffer>&& script, HashMap<URL, RefPtr<SharedBuffer>>&& importedScripts);
 
     void forEachServiceWorkerThread(const WTF::Function<void(ServiceWorkerThreadProxy&)>&);
 

Modified: trunk/Source/WebCore/workers/service/context/ServiceWorkerThreadProxy.cpp (275374 => 275375)


--- trunk/Source/WebCore/workers/service/context/ServiceWorkerThreadProxy.cpp	2021-04-01 20:02:50 UTC (rev 275374)
+++ trunk/Source/WebCore/workers/service/context/ServiceWorkerThreadProxy.cpp	2021-04-01 20:05:48 UTC (rev 275375)
@@ -281,6 +281,13 @@
     });
 }
 
+void ServiceWorkerThreadProxy::didSaveScriptsToDisk(RefPtr<SharedBuffer>&& script, HashMap<URL, RefPtr<SharedBuffer>>&& importedScripts)
+{
+    thread().runLoop().postTask([script = WTFMove(script), importedScripts = WTFMove(importedScripts)](auto& context) mutable {
+        downcast<ServiceWorkerGlobalScope>(context).didSaveScriptsToDisk(WTFMove(script), WTFMove(importedScripts));
+    });
+}
+
 } // namespace WebCore
 
 #endif // ENABLE(SERVICE_WORKER)

Modified: trunk/Source/WebCore/workers/service/context/ServiceWorkerThreadProxy.h (275374 => 275375)


--- trunk/Source/WebCore/workers/service/context/ServiceWorkerThreadProxy.h	2021-04-01 20:02:50 UTC (rev 275374)
+++ trunk/Source/WebCore/workers/service/context/ServiceWorkerThreadProxy.h	2021-04-01 20:05:48 UTC (rev 275375)
@@ -78,6 +78,7 @@
     void postMessageToServiceWorker(MessageWithMessagePorts&&, ServiceWorkerOrClientData&&);
     void fireInstallEvent();
     void fireActivateEvent();
+    void didSaveScriptsToDisk(RefPtr<SharedBuffer>&& script, HashMap<URL, RefPtr<SharedBuffer>>&& importedScripts);
 
     WEBCORE_EXPORT void setLastNavigationWasAppBound(bool);
 

Modified: trunk/Source/WebCore/workers/service/server/SWServerToContextConnection.h (275374 => 275375)


--- trunk/Source/WebCore/workers/service/server/SWServerToContextConnection.h	2021-04-01 20:02:50 UTC (rev 275374)
+++ trunk/Source/WebCore/workers/service/server/SWServerToContextConnection.h	2021-04-01 20:05:48 UTC (rev 275375)
@@ -54,6 +54,7 @@
     virtual void fireInstallEvent(ServiceWorkerIdentifier) = 0;
     virtual void fireActivateEvent(ServiceWorkerIdentifier) = 0;
     virtual void terminateWorker(ServiceWorkerIdentifier) = 0;
+    virtual void didSaveScriptsToDisk(ServiceWorkerIdentifier, SharedBuffer& script, const HashMap<URL, RefPtr<SharedBuffer>>& importedScripts) = 0;
     virtual void findClientByIdentifierCompleted(uint64_t requestIdentifier, const Optional<ServiceWorkerClientData>&, bool hasSecurityError) = 0;
     virtual void matchAllCompleted(uint64_t requestIdentifier, const Vector<ServiceWorkerClientData>&) = 0;
 

Modified: trunk/Source/WebCore/workers/service/server/SWServerWorker.cpp (275374 => 275375)


--- trunk/Source/WebCore/workers/service/server/SWServerWorker.cpp	2021-04-01 20:02:50 UTC (rev 275374)
+++ trunk/Source/WebCore/workers/service/server/SWServerWorker.cpp	2021-04-01 20:05:48 UTC (rev 275375)
@@ -240,7 +240,11 @@
 
 void SWServerWorker::didSaveScriptsToDisk(Ref<SharedBuffer>&& mainScript, HashMap<URL, RefPtr<SharedBuffer>>&& importedScripts)
 {
-    // The scripts were saved to disk, replace our scripts with the mmap'd version to save memory.
+    // Send mmap'd version of the scripts to the ServiceWorker process so we can save dirty memory.
+    if (auto* contextConnection = this->contextConnection())
+        contextConnection->didSaveScriptsToDisk(identifier(), mainScript, importedScripts);
+
+    // The scripts were saved to disk, replace our scripts with the mmap'd version to save dirty memory.
     ASSERT(mainScript.get() == m_script.get()); // Do a memcmp to make sure the scripts are identical.
     m_script = WTFMove(mainScript);
     for (auto& pair : importedScripts) {

Modified: trunk/Source/WebKit/ChangeLog (275374 => 275375)


--- trunk/Source/WebKit/ChangeLog	2021-04-01 20:02:50 UTC (rev 275374)
+++ trunk/Source/WebKit/ChangeLog	2021-04-01 20:05:48 UTC (rev 275375)
@@ -1,3 +1,44 @@
+2021-04-01  Chris Dumez  <[email protected]>
+
+        Have the ServiceWorker process hold on to a file mapped version of the service worker scripts to save dirty memory
+        https://bugs.webkit.org/show_bug.cgi?id=224015
+        <rdar://75637679>
+
+        Reviewed by Geoffrey Garen.
+
+        * NetworkProcess/ServiceWorker/WebSWServerToContextConnection.cpp:
+        (WebKit::WebSWServerToContextConnection::didSaveScriptsToDisk):
+        * NetworkProcess/ServiceWorker/WebSWServerToContextConnection.h:
+        * WebProcess/Storage/WebSWContextManagerConnection.cpp:
+        (WebKit::WebSWContextManagerConnection::didSaveScriptsToDisk):
+        * WebProcess/Storage/WebSWContextManagerConnection.h:
+        * WebProcess/Storage/WebSWContextManagerConnection.messages.in:
+        When the NetworkProcess is done saving the service worker scripts to disk and it gets
+        a file mapped version, it now sends them to the ServiceWorker process as ShareableResource
+        handles via IPC. This allows the ServiceWorker process to replace its heap-allocated
+        versions of the script and save on dirty memory use (in the cold case).
+
+        * Shared/ShareableResource.h:
+        Fix bug where 2 of ShareableResource data members were not properly initialized by the default
+        constructor.
+
+        * Shared/WebCoreArgumentCoders.cpp:
+        (IPC::encodeServiceWorkerContextDataScript):
+        (IPC::decodeServiceWorkerContextDataScript):
+        (IPC::ArgumentCoder<ServiceWorkerContextData::ImportedScript>::encode):
+        (IPC::ArgumentCoder<ServiceWorkerContextData::ImportedScript>::decode):
+        (IPC::ArgumentCoder<ServiceWorkerContextData>::encode):
+        (IPC::ArgumentCoder<ServiceWorkerContextData>::decode):
+        (IPC::tryConvertToShareableResourceHandle):
+        * Shared/WebCoreArgumentCoders.h:
+        Moved IPC encoders / decoders for ServiceWorkerContextData and ServiceWorkerContextData::ImportedScript
+        to the WebKit layer, in WebCoreArgumentCoders. This allows us to encode / decode the scripts as
+        WebKit::ShareableHandle whenever possible. This way, when the NetworkProcess sends a
+        ServiceWorkerContextData to the ServiceWorker process to launch a service worker, both the
+        ServiceWorker process and the Network process share the same mmap'd versions of the scripts and we
+        save on dirty memory use. This helps reduce dirty memory use in the ServiceWorker process in the
+        warm case, where the scripts are loaded straight from the disk (via SWScriptStorage).
+
 2021-04-01  Myles C. Maxfield  <[email protected]>
 
         [Cocoa] REGRESSION(r272999): User-installed fonts no longer work in Mail

Modified: trunk/Source/WebKit/NetworkProcess/ServiceWorker/WebSWServerToContextConnection.cpp (275374 => 275375)


--- trunk/Source/WebKit/NetworkProcess/ServiceWorker/WebSWServerToContextConnection.cpp	2021-04-01 20:02:50 UTC (rev 275374)
+++ trunk/Source/WebKit/NetworkProcess/ServiceWorker/WebSWServerToContextConnection.cpp	2021-04-01 20:05:48 UTC (rev 275375)
@@ -107,6 +107,25 @@
     send(Messages::WebSWContextManagerConnection::TerminateWorker(serviceWorkerIdentifier));
 }
 
+void WebSWServerToContextConnection::didSaveScriptsToDisk(ServiceWorkerIdentifier serviceWorkerIdentifier, WebCore::SharedBuffer& script, const HashMap<URL, RefPtr<SharedBuffer>>& importedScripts)
+{
+#if ENABLE(SHAREABLE_RESOURCE) && PLATFORM(COCOA)
+    auto scriptHandle = IPC::tryConvertToShareableResourceHandle(script);
+    HashMap<URL, ShareableResource::Handle> importedScriptHandles;
+    for (auto& pair : importedScripts) {
+        auto handle = IPC::tryConvertToShareableResourceHandle(*pair.value);
+        if (handle.isNull())
+            continue;
+        importedScriptHandles.add(pair.key, WTFMove(handle));
+    }
+    if (!scriptHandle.isNull() || !importedScriptHandles.isEmpty())
+        send(Messages::WebSWContextManagerConnection::DidSaveScriptsToDisk { serviceWorkerIdentifier, scriptHandle, importedScriptHandles });
+#else
+    UNUSED_PARAM(script);
+    UNUSED_PARAM(importedScripts);
+#endif
+}
+
 void WebSWServerToContextConnection::terminateDueToUnresponsiveness()
 {
     m_connection.networkProcess().parentProcessConnection()->send(Messages::NetworkProcessProxy::TerminateUnresponsiveServiceWorkerProcesses { webProcessIdentifier() }, 0);

Modified: trunk/Source/WebKit/NetworkProcess/ServiceWorker/WebSWServerToContextConnection.h (275374 => 275375)


--- trunk/Source/WebKit/NetworkProcess/ServiceWorker/WebSWServerToContextConnection.h	2021-04-01 20:02:50 UTC (rev 275374)
+++ trunk/Source/WebKit/NetworkProcess/ServiceWorker/WebSWServerToContextConnection.h	2021-04-01 20:05:48 UTC (rev 275375)
@@ -88,6 +88,7 @@
     void fireInstallEvent(WebCore::ServiceWorkerIdentifier) final;
     void fireActivateEvent(WebCore::ServiceWorkerIdentifier) final;
     void terminateWorker(WebCore::ServiceWorkerIdentifier) final;
+    void didSaveScriptsToDisk(WebCore::ServiceWorkerIdentifier, WebCore::SharedBuffer& script, const HashMap<URL, RefPtr<WebCore::SharedBuffer>>& importedScripts) final;
     void findClientByIdentifierCompleted(uint64_t requestIdentifier, const Optional<WebCore::ServiceWorkerClientData>&, bool hasSecurityError) final;
     void matchAllCompleted(uint64_t requestIdentifier, const Vector<WebCore::ServiceWorkerClientData>&) final;
 

Modified: trunk/Source/WebKit/Shared/ShareableResource.cpp (275374 => 275375)


--- trunk/Source/WebKit/Shared/ShareableResource.cpp	2021-04-01 20:02:50 UTC (rev 275374)
+++ trunk/Source/WebKit/Shared/ShareableResource.cpp	2021-04-01 20:05:48 UTC (rev 275375)
@@ -34,9 +34,7 @@
 namespace WebKit {
 using namespace WebCore;
 
-ShareableResource::Handle::Handle()
-{
-}
+ShareableResource::Handle::Handle() = default;
 
 void ShareableResource::Handle::encode(IPC::Encoder& encoder) const
 {

Modified: trunk/Source/WebKit/Shared/ShareableResource.h (275374 => 275375)


--- trunk/Source/WebKit/Shared/ShareableResource.h	2021-04-01 20:02:50 UTC (rev 275374)
+++ trunk/Source/WebKit/Shared/ShareableResource.h	2021-04-01 20:05:48 UTC (rev 275375)
@@ -60,8 +60,8 @@
         friend class ShareableResource;
 
         mutable SharedMemory::Handle m_handle;
-        unsigned m_offset;
-        unsigned m_size;
+        unsigned m_offset { 0 };
+        unsigned m_size { 0 };
     };
 
     // Create a shareable resource that uses malloced memory.

Modified: trunk/Source/WebKit/Shared/WebCoreArgumentCoders.cpp (275374 => 275375)


--- trunk/Source/WebKit/Shared/WebCoreArgumentCoders.cpp	2021-04-01 20:02:50 UTC (rev 275374)
+++ trunk/Source/WebKit/Shared/WebCoreArgumentCoders.cpp	2021-04-01 20:05:48 UTC (rev 275375)
@@ -2828,8 +2828,145 @@
     }
     return true;
 }
+
+static void encodeServiceWorkerContextDataScript(Encoder& encoder, SharedBuffer& script)
+{
+#if ENABLE(SHAREABLE_RESOURCE) && PLATFORM(COCOA)
+    auto handle = tryConvertToShareableResourceHandle(script);
+    bool isShareableResourceHandle = !handle.isNull();
+    encoder << isShareableResourceHandle;
+    if (isShareableResourceHandle) {
+        encoder << handle;
+        return;
+    }
 #endif
+    encoder << makeRef(script);
+}
 
+static Optional<Ref<SharedBuffer>> decodeServiceWorkerContextDataScript(Decoder& decoder)
+{
+#if ENABLE(SHAREABLE_RESOURCE) && PLATFORM(COCOA)
+    Optional<bool> isShareableResourceHandle;
+    decoder >> isShareableResourceHandle;
+    if (!isShareableResourceHandle)
+        return WTF::nullopt;
+    if (*isShareableResourceHandle) {
+        ShareableResource::Handle handle;
+        if (!decoder.decode(handle) || handle.isNull())
+            return WTF::nullopt;
+        auto buffer = handle.tryWrapInSharedBuffer();
+        if (!buffer)
+            return WTF::nullopt;
+        return buffer.releaseNonNull();
+    }
+#endif
+    Optional<Ref<SharedBuffer>> script;
+    decoder >> script;
+    return script;
+}
+
+void ArgumentCoder<ServiceWorkerContextData::ImportedScript>::encode(Encoder& encoder, const ServiceWorkerContextData::ImportedScript& importedScript)
+{
+    encodeServiceWorkerContextDataScript(encoder, *importedScript.script);
+    encoder << importedScript.responseURL << importedScript.mimeType;
+}
+
+Optional<ServiceWorkerContextData::ImportedScript> ArgumentCoder<ServiceWorkerContextData::ImportedScript>::decode(Decoder& decoder)
+{
+    auto script = decodeServiceWorkerContextDataScript(decoder);
+    if (!script)
+        return WTF::nullopt;
+
+    Optional<URL> responseURL;
+    decoder >> responseURL;
+    if (!responseURL)
+        return WTF::nullopt;
+
+    Optional<String> mimeType;
+    decoder >> mimeType;
+    if (!mimeType)
+        return WTF::nullopt;
+
+    return {{
+        WTFMove(*script),
+        WTFMove(*responseURL),
+        WTFMove(*mimeType)
+    }};
+}
+
+void ArgumentCoder<ServiceWorkerContextData>::encode(Encoder& encoder, const ServiceWorkerContextData& data)
+{
+    encodeServiceWorkerContextDataScript(encoder, data.script);
+    encoder << data.jobDataIdentifier << data.registration << data.serviceWorkerIdentifier << data.contentSecurityPolicy << data.referrerPolicy
+        << data.scriptURL << data.workerType << data.loadedFromDisk << data.scriptResourceMap << data.certificateInfo;
+}
+
+Optional<ServiceWorkerContextData> ArgumentCoder<ServiceWorkerContextData>::decode(Decoder& decoder)
+{
+    auto script = decodeServiceWorkerContextDataScript(decoder);
+    if (!script)
+        return WTF::nullopt;
+
+    Optional<Optional<ServiceWorkerJobDataIdentifier>> jobDataIdentifier;
+    decoder >> jobDataIdentifier;
+    if (!jobDataIdentifier)
+        return WTF::nullopt;
+
+    Optional<ServiceWorkerRegistrationData> registration;
+    decoder >> registration;
+    if (!registration)
+        return WTF::nullopt;
+
+    auto serviceWorkerIdentifier = ServiceWorkerIdentifier::decode(decoder);
+    if (!serviceWorkerIdentifier)
+        return WTF::nullopt;
+
+    ContentSecurityPolicyResponseHeaders contentSecurityPolicy;
+    if (!decoder.decode(contentSecurityPolicy))
+        return WTF::nullopt;
+
+    String referrerPolicy;
+    if (!decoder.decode(referrerPolicy))
+        return WTF::nullopt;
+
+    URL scriptURL;
+    if (!decoder.decode(scriptURL))
+        return WTF::nullopt;
+
+    WorkerType workerType;
+    if (!decoder.decode(workerType))
+        return WTF::nullopt;
+
+    bool loadedFromDisk;
+    if (!decoder.decode(loadedFromDisk))
+        return WTF::nullopt;
+
+    HashMap<URL, ServiceWorkerContextData::ImportedScript> scriptResourceMap;
+    if (!decoder.decode(scriptResourceMap))
+        return WTF::nullopt;
+
+    Optional<CertificateInfo> certificateInfo;
+    decoder >> certificateInfo;
+    if (!certificateInfo)
+        return WTF::nullopt;
+
+    return {{
+        WTFMove(*jobDataIdentifier),
+        WTFMove(*registration),
+        WTFMove(*serviceWorkerIdentifier),
+        WTFMove(*script),
+        WTFMove(*certificateInfo),
+        WTFMove(contentSecurityPolicy),
+        WTFMove(referrerPolicy),
+        WTFMove(scriptURL),
+        workerType,
+        loadedFromDisk,
+        WTFMove(scriptResourceMap)
+    }};
+}
+
+#endif
+
 #if ENABLE(CSS_SCROLL_SNAP)
 
 void ArgumentCoder<ScrollOffsetRange<float>>::encode(Encoder& encoder, const ScrollOffsetRange<float>& range)
@@ -3066,6 +3203,30 @@
     return buffer.releaseNonNull();
 }
 
+#if ENABLE(SHAREABLE_RESOURCE) && PLATFORM(COCOA)
+ShareableResource::Handle tryConvertToShareableResourceHandle(SharedBuffer& buffer)
+{
+    if (!buffer.hasOneSegment())
+        return ShareableResource::Handle { };
+
+    auto& segment = buffer.begin()->segment;
+    if (!segment->containsMappedFileData())
+        return ShareableResource::Handle { };
+
+    auto sharedMemory = SharedMemory::wrapMap(const_cast<char*>(segment->data()), segment->size(), SharedMemory::Protection::ReadOnly);
+    if (!sharedMemory)
+        return ShareableResource::Handle { };
+
+    auto shareableResource = ShareableResource::create(sharedMemory.releaseNonNull(), 0, segment->size());
+    if (!shareableResource)
+        return ShareableResource::Handle { };
+
+    ShareableResource::Handle shareableResourceHandle;
+    shareableResource->createHandle(shareableResourceHandle);
+    return shareableResourceHandle;
+}
+#endif
+
 #if ENABLE(ENCRYPTED_MEDIA)
 void ArgumentCoder<WebCore::CDMInstanceSession::Message>::encode(Encoder& encoder, const WebCore::CDMInstanceSession::Message& message)
 {

Modified: trunk/Source/WebKit/Shared/WebCoreArgumentCoders.h (275374 => 275375)


--- trunk/Source/WebKit/Shared/WebCoreArgumentCoders.h	2021-04-01 20:02:50 UTC (rev 275374)
+++ trunk/Source/WebKit/Shared/WebCoreArgumentCoders.h	2021-04-01 20:05:48 UTC (rev 275375)
@@ -26,6 +26,7 @@
 #pragma once
 
 #include "ArgumentCoders.h"
+#include "ShareableResource.h"
 #include <WebCore/AutoplayEvent.h>
 #include <WebCore/ColorSpace.h>
 #include <WebCore/DiagnosticLoggingClient.h>
@@ -41,6 +42,7 @@
 #include <WebCore/RenderingMode.h>
 #include <WebCore/ScrollSnapOffsetsInfo.h>
 #include <WebCore/SerializedPlatformDataCueValue.h>
+#include <WebCore/ServiceWorkerContextData.h>
 #include <WebCore/ServiceWorkerTypes.h>
 #include <WebCore/StoredCredentialsPolicy.h>
 #include <WebCore/WorkerType.h>
@@ -735,6 +737,16 @@
     static WARN_UNUSED_RETURN bool decode(Decoder&, WebCore::ServiceWorkerOrClientIdentifier&);
 };
 
+template<> struct ArgumentCoder<WebCore::ServiceWorkerContextData> {
+    static void encode(Encoder&, const WebCore::ServiceWorkerContextData&);
+    static Optional<WebCore::ServiceWorkerContextData> decode(Decoder&);
+};
+
+template<> struct ArgumentCoder<WebCore::ServiceWorkerContextData::ImportedScript> {
+    static void encode(Encoder&, const WebCore::ServiceWorkerContextData::ImportedScript&);
+    static Optional<WebCore::ServiceWorkerContextData::ImportedScript> decode(Decoder&);
+};
+
 #endif
 
 #if ENABLE(CSS_SCROLL_SNAP)
@@ -796,6 +808,12 @@
     static Optional<Ref<WebCore::SharedBuffer>> decode(Decoder&);
 };
 
+#if ENABLE(SHAREABLE_RESOURCE) && PLATFORM(COCOA)
+// If the SharedBuffer contains a single segment and the segment is MappedFileData, then convert it into
+// a ShareableResource handle so that it can be sent over IPC and shared with another process.
+WebKit::ShareableResource::Handle tryConvertToShareableResourceHandle(WebCore::SharedBuffer&);
+#endif
+
 #if ENABLE(ENCRYPTED_MEDIA)
 template<> struct ArgumentCoder<WebCore::CDMInstanceSession::Message> {
     static void encode(Encoder&, const WebCore::CDMInstanceSession::Message&);

Modified: trunk/Source/WebKit/WebProcess/Storage/WebSWContextManagerConnection.cpp (275374 => 275375)


--- trunk/Source/WebKit/WebProcess/Storage/WebSWContextManagerConnection.cpp	2021-04-01 20:02:50 UTC (rev 275374)
+++ trunk/Source/WebKit/WebProcess/Storage/WebSWContextManagerConnection.cpp	2021-04-01 20:05:48 UTC (rev 275375)
@@ -262,6 +262,26 @@
     SWContextManager::singleton().terminateWorker(identifier, SWContextManager::workerTerminationTimeout, nullptr);
 }
 
+#if ENABLE(SHAREABLE_RESOURCE) && PLATFORM(COCOA)
+void WebSWContextManagerConnection::didSaveScriptsToDisk(WebCore::ServiceWorkerIdentifier identifier, const ShareableResource::Handle& scriptHandle, const HashMap<URL, ShareableResource::Handle>& importedScriptHandles)
+{
+    auto toSharedBuffer = [](const ShareableResource::Handle& handle) -> RefPtr<SharedBuffer> {
+        if (handle.isNull())
+            return nullptr;
+        auto sharedBuffer = handle.tryWrapInSharedBuffer();
+        if (!sharedBuffer)
+            RELEASE_LOG_ERROR(ServiceWorker, "WebSWContextManagerConnection::didSaveScriptsToDisk: Failed to wrap ShareableResource handle in a SharedBuffer");
+        return sharedBuffer;
+    };
+    HashMap<URL, RefPtr<SharedBuffer>> importedScripts;
+    for (auto& pair : importedScriptHandles) {
+        if (auto sharedBuffer = toSharedBuffer(pair.value))
+            importedScripts.add(pair.key, WTFMove(sharedBuffer));
+    }
+    SWContextManager::singleton().didSaveScriptsToDisk(identifier, toSharedBuffer(scriptHandle), WTFMove(importedScripts));
+}
+#endif
+
 void WebSWContextManagerConnection::postMessageToServiceWorkerClient(const ServiceWorkerClientIdentifier& destinationIdentifier, const MessageWithMessagePorts& message, ServiceWorkerIdentifier sourceIdentifier, const String& sourceOrigin)
 {
     m_connectionToNetworkProcess->send(Messages::WebSWServerToContextConnection::PostMessageToServiceWorkerClient(destinationIdentifier, message, sourceIdentifier, sourceOrigin), 0);

Modified: trunk/Source/WebKit/WebProcess/Storage/WebSWContextManagerConnection.h (275374 => 275375)


--- trunk/Source/WebKit/WebProcess/Storage/WebSWContextManagerConnection.h	2021-04-01 20:02:50 UTC (rev 275374)
+++ trunk/Source/WebKit/WebProcess/Storage/WebSWContextManagerConnection.h	2021-04-01 20:05:48 UTC (rev 275375)
@@ -29,6 +29,7 @@
 
 #include "Connection.h"
 #include "MessageReceiver.h"
+#include "ShareableResource.h"
 #include "UserContentControllerIdentifier.h"
 #include "WebPageProxyIdentifier.h"
 #include "WebSWContextManagerConnectionMessagesReplies.h"
@@ -90,6 +91,9 @@
     void fireInstallEvent(WebCore::ServiceWorkerIdentifier);
     void fireActivateEvent(WebCore::ServiceWorkerIdentifier);
     void terminateWorker(WebCore::ServiceWorkerIdentifier);
+#if ENABLE(SHAREABLE_RESOURCE) && PLATFORM(COCOA)
+    void didSaveScriptsToDisk(WebCore::ServiceWorkerIdentifier, const ShareableResource::Handle& script, const HashMap<URL, ShareableResource::Handle>& importedScripts);
+#endif
     void findClientByIdentifierCompleted(uint64_t requestIdentifier, Optional<WebCore::ServiceWorkerClientData>&&, bool hasSecurityError);
     void matchAllCompleted(uint64_t matchAllRequestIdentifier, Vector<WebCore::ServiceWorkerClientData>&&);
     void setUserAgent(String&& userAgent);

Modified: trunk/Source/WebKit/WebProcess/Storage/WebSWContextManagerConnection.messages.in (275374 => 275375)


--- trunk/Source/WebKit/WebProcess/Storage/WebSWContextManagerConnection.messages.in	2021-04-01 20:02:50 UTC (rev 275374)
+++ trunk/Source/WebKit/WebProcess/Storage/WebSWContextManagerConnection.messages.in	2021-04-01 20:05:48 UTC (rev 275375)
@@ -31,6 +31,9 @@
     FireInstallEvent(WebCore::ServiceWorkerIdentifier identifier)
     FireActivateEvent(WebCore::ServiceWorkerIdentifier identifier)
     TerminateWorker(WebCore::ServiceWorkerIdentifier identifier)
+#if ENABLE(SHAREABLE_RESOURCE) && PLATFORM(COCOA)
+    DidSaveScriptsToDisk(WebCore::ServiceWorkerIdentifier serviceWorkerIdentifier, WebKit::ShareableResource::Handle script, HashMap<URL, WebKit::ShareableResource::Handle> importedScripts);
+#endif
     FindClientByIdentifierCompleted(uint64_t clientIdRequestIdentifier, Optional<WebCore::ServiceWorkerClientData> data, bool hasSecurityError)
     MatchAllCompleted(uint64_t matchAllRequestIdentifier, Vector<WebCore::ServiceWorkerClientData> clientsData)
     SetUserAgent(String userAgent)
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to