Fix lifetime management issue in ResourceLoadStatisticsPersistentStorage::scheduleOrW...
authorcdumez@apple.com <cdumez@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 24 Jul 2017 22:50:32 +0000 (22:50 +0000)
committercdumez@apple.com <cdumez@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 24 Jul 2017 22:50:32 +0000 (22:50 +0000)
https://bugs.webkit.org/show_bug.cgi?id=174790

Reviewed by Brady Eidson.

Fix lifetime management issue in ResourceLoadStatisticsPersistentStorage::scheduleOrWriteMemoryStore().
WorkQueue::dispatchAfter() keeps the WorkQueue alive because its implementation keeps a strong ref to
|this|. As a result, the lambda passed to dispatchAfter(), which calls writeMemoryStoreToDisk(), can
get executed after the store is gone.

To address the issue, we now use a RunLoop::Timer to schedule the write, instead of a
WorkQueue::dispatchAfter() call. This way, we are guaranteed that the callback will not get called
after the store has been destroyed.

* UIProcess/Storage/ResourceLoadStatisticsPersistentStorage.cpp:
(WebKit::ResourceLoadStatisticsPersistentStorage::ResourceLoadStatisticsPersistentStorage):
(WebKit::ResourceLoadStatisticsPersistentStorage::asyncWriteTimerFired):
(WebKit::ResourceLoadStatisticsPersistentStorage::scheduleOrWriteMemoryStore):
(WebKit::ResourceLoadStatisticsPersistentStorage::finishAllPendingWorkSynchronously):
(WebKit::ResourceLoadStatisticsPersistentStorage::ref):
(WebKit::ResourceLoadStatisticsPersistentStorage::deref):
* UIProcess/Storage/ResourceLoadStatisticsPersistentStorage.h:

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

Source/WebKit/ChangeLog
Source/WebKit/UIProcess/Storage/ResourceLoadStatisticsPersistentStorage.cpp
Source/WebKit/UIProcess/Storage/ResourceLoadStatisticsPersistentStorage.h

index 8978090..a228f54 100644 (file)
@@ -1,5 +1,30 @@
 2017-07-24  Chris Dumez  <cdumez@apple.com>
 
+        Fix lifetime management issue in ResourceLoadStatisticsPersistentStorage::scheduleOrWriteMemoryStore()
+        https://bugs.webkit.org/show_bug.cgi?id=174790
+
+        Reviewed by Brady Eidson.
+
+        Fix lifetime management issue in ResourceLoadStatisticsPersistentStorage::scheduleOrWriteMemoryStore().
+        WorkQueue::dispatchAfter() keeps the WorkQueue alive because its implementation keeps a strong ref to
+        |this|. As a result, the lambda passed to dispatchAfter(), which calls writeMemoryStoreToDisk(), can
+        get executed after the store is gone.
+
+        To address the issue, we now use a RunLoop::Timer to schedule the write, instead of a
+        WorkQueue::dispatchAfter() call. This way, we are guaranteed that the callback will not get called
+        after the store has been destroyed.
+
+        * UIProcess/Storage/ResourceLoadStatisticsPersistentStorage.cpp:
+        (WebKit::ResourceLoadStatisticsPersistentStorage::ResourceLoadStatisticsPersistentStorage):
+        (WebKit::ResourceLoadStatisticsPersistentStorage::asyncWriteTimerFired):
+        (WebKit::ResourceLoadStatisticsPersistentStorage::scheduleOrWriteMemoryStore):
+        (WebKit::ResourceLoadStatisticsPersistentStorage::finishAllPendingWorkSynchronously):
+        (WebKit::ResourceLoadStatisticsPersistentStorage::ref):
+        (WebKit::ResourceLoadStatisticsPersistentStorage::deref):
+        * UIProcess/Storage/ResourceLoadStatisticsPersistentStorage.h:
+
+2017-07-24  Chris Dumez  <cdumez@apple.com>
+
         Unreviewed, rolling out r219828.
 
         Causes debug assertions to be hit on iOS
index 58ea1aa..b4d92be 100644 (file)
@@ -85,6 +85,7 @@ static std::unique_ptr<KeyedDecoder> createDecoderForFile(const String& path)
 ResourceLoadStatisticsPersistentStorage::ResourceLoadStatisticsPersistentStorage(WebResourceLoadStatisticsStore& store, const String& storageDirectoryPath)
     : m_memoryStore(store)
     , m_storageDirectoryPath(storageDirectoryPath)
+    , m_asyncWriteTimer(RunLoop::main(), this, &ResourceLoadStatisticsPersistentStorage::asyncWriteTimerFired)
 {
 }
 
@@ -237,6 +238,14 @@ void ResourceLoadStatisticsPersistentStorage::populateMemoryStoreFromDisk()
         m_memoryStore.grandfatherExistingWebsiteData();
 }
 
+void ResourceLoadStatisticsPersistentStorage::asyncWriteTimerFired()
+{
+    ASSERT(RunLoop::isMain());
+    m_memoryStore.statisticsQueue().dispatch([this] () mutable {
+        writeMemoryStoreToDisk();
+    });
+}
+
 void ResourceLoadStatisticsPersistentStorage::writeMemoryStoreToDisk()
 {
     ASSERT(!RunLoop::isMain());
@@ -280,8 +289,8 @@ void ResourceLoadStatisticsPersistentStorage::scheduleOrWriteMemoryStore()
         if (!m_hasPendingWrite) {
             m_hasPendingWrite = true;
             Seconds delay = minimumWriteInterval - timeSinceLastWrite + 1_s;
-            m_memoryStore.statisticsQueue().dispatchAfter(delay, [this] () mutable {
-                writeMemoryStoreToDisk();
+            RunLoop::main().dispatch([this, protectedThis = makeRef(*this), delay] {
+                m_asyncWriteTimer.startOneShot(delay);
             });
         }
         return;
@@ -305,6 +314,8 @@ void ResourceLoadStatisticsPersistentStorage::clear()
 
 void ResourceLoadStatisticsPersistentStorage::finishAllPendingWorkSynchronously()
 {
+    m_asyncWriteTimer.stop();
+
     BinarySemaphore semaphore;
     // Make sure any pending work in our queue is finished before we terminate.
     m_memoryStore.statisticsQueue().dispatch([&semaphore, this] {
@@ -316,6 +327,16 @@ void ResourceLoadStatisticsPersistentStorage::finishAllPendingWorkSynchronously(
     semaphore.wait(WallTime::infinity());
 }
 
+void ResourceLoadStatisticsPersistentStorage::ref()
+{
+    m_memoryStore.ref();
+}
+
+void ResourceLoadStatisticsPersistentStorage::deref()
+{
+    m_memoryStore.deref();
+}
+
 #if !PLATFORM(IOS)
 void ResourceLoadStatisticsPersistentStorage::excludeFromBackup() const
 {
index e3544f5..81f7ccd 100644 (file)
@@ -27,6 +27,7 @@
 
 #include <wtf/Forward.h>
 #include <wtf/MonotonicTime.h>
+#include <wtf/RunLoop.h>
 #include <wtf/WallTime.h>
 #include <wtf/text/WTFString.h>
 
@@ -49,6 +50,9 @@ public:
 
     void finishAllPendingWorkSynchronously();
 
+    void ref();
+    void deref();
+
 private:
     String storageDirectoryPath() const;
     String resourceLogFilePath() const;
@@ -61,9 +65,11 @@ private:
     void populateMemoryStoreFromDisk();
     void excludeFromBackup() const;
     void refreshMemoryStoreFromDisk();
+    void asyncWriteTimerFired();
 
     WebResourceLoadStatisticsStore& m_memoryStore;
     const String m_storageDirectoryPath;
+    RunLoop::Timer<ResourceLoadStatisticsPersistentStorage> m_asyncWriteTimer;
     std::unique_ptr<WebCore::FileMonitor> m_fileMonitor;
     WallTime m_lastStatisticsFileSyncTime;
     MonotonicTime m_lastStatisticsWriteTime;