Title: [286690] branches/safari-612-branch/Source
Revision
286690
Author
alanc...@apple.com
Date
2021-12-08 12:23:29 -0800 (Wed, 08 Dec 2021)

Log Message

Cherry-pick r286481. rdar://problem/85928816

    A Safari tab can rarely get stuck in a state where rendering updates stop happening
    https://bugs.webkit.org/show_bug.cgi?id=233784
    rdar://85445072

    Reviewed by Chris Dumez.

    Sometimes a Safari tab can get into a state where rendering updates cease to happen,
    which manifests as partially broken scrolling, blank tiles revealed when scrolling,
    and somewhat broken page updates. I was able to sometimes reproduce this by clicking
    on links in eBay emails from Mail on a system with two displays.

    From the one time I reproduce with logging, the output indicated that DisplayRefreshMonitor::displayLinkFired()
    would early return because isPreviousFrameDone() was false. The only way for that to occur,
    barring memory corruption, is if DisplayRefreshMonitorMac::dispatchDisplayDidRefresh() returned early,
    which it does if the callback comes twice in a single event loop; this may explain the rarity.

    So fix DisplayRefreshMonitorMac::dispatchDisplayDidRefresh() call setIsPreviousFrameDone(true)
    so the next callback can make progress

    Also add some locking annotations and fix one missing lock, and some release logging.

    Source/WebCore:

    * platform/graphics/DisplayRefreshMonitor.cpp:
    (WebCore::DisplayRefreshMonitor::stop):
    (WebCore::DisplayRefreshMonitor::firedAndReachedMaxUnscheduledFireCount):
    (WebCore::DisplayRefreshMonitor::displayLinkFired):
    * platform/graphics/DisplayRefreshMonitor.h:
    (WebCore::DisplayRefreshMonitor::WTF_REQUIRES_LOCK):
    (WebCore::DisplayRefreshMonitor::WTF_GUARDED_BY_LOCK):
    (WebCore::DisplayRefreshMonitor::setMaxUnscheduledFireCount): Deleted.
    (WebCore::DisplayRefreshMonitor::isScheduled const): Deleted.
    (WebCore::DisplayRefreshMonitor::setIsScheduled): Deleted.
    (WebCore::DisplayRefreshMonitor::isPreviousFrameDone const): Deleted.
    (WebCore::DisplayRefreshMonitor::setIsPreviousFrameDone): Deleted.

    Source/WebKit:

    * WebProcess/WebPage/mac/DisplayRefreshMonitorMac.cpp:
    (WebKit::DisplayRefreshMonitorMac::dispatchDisplayDidRefresh):

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

Modified Paths

Diff

Modified: branches/safari-612-branch/Source/WebCore/ChangeLog (286689 => 286690)


--- branches/safari-612-branch/Source/WebCore/ChangeLog	2021-12-08 20:23:25 UTC (rev 286689)
+++ branches/safari-612-branch/Source/WebCore/ChangeLog	2021-12-08 20:23:29 UTC (rev 286690)
@@ -1,3 +1,87 @@
+2021-12-03  Russell Epstein  <repst...@apple.com>
+
+        Cherry-pick r286481. rdar://problem/85928816
+
+    A Safari tab can rarely get stuck in a state where rendering updates stop happening
+    https://bugs.webkit.org/show_bug.cgi?id=233784
+    rdar://85445072
+    
+    Reviewed by Chris Dumez.
+    
+    Sometimes a Safari tab can get into a state where rendering updates cease to happen,
+    which manifests as partially broken scrolling, blank tiles revealed when scrolling,
+    and somewhat broken page updates. I was able to sometimes reproduce this by clicking
+    on links in eBay emails from Mail on a system with two displays.
+    
+    From the one time I reproduce with logging, the output indicated that DisplayRefreshMonitor::displayLinkFired()
+    would early return because isPreviousFrameDone() was false. The only way for that to occur,
+    barring memory corruption, is if DisplayRefreshMonitorMac::dispatchDisplayDidRefresh() returned early,
+    which it does if the callback comes twice in a single event loop; this may explain the rarity.
+    
+    So fix DisplayRefreshMonitorMac::dispatchDisplayDidRefresh() call setIsPreviousFrameDone(true)
+    so the next callback can make progress
+    
+    Also add some locking annotations and fix one missing lock, and some release logging.
+    
+    Source/WebCore:
+    
+    * platform/graphics/DisplayRefreshMonitor.cpp:
+    (WebCore::DisplayRefreshMonitor::stop):
+    (WebCore::DisplayRefreshMonitor::firedAndReachedMaxUnscheduledFireCount):
+    (WebCore::DisplayRefreshMonitor::displayLinkFired):
+    * platform/graphics/DisplayRefreshMonitor.h:
+    (WebCore::DisplayRefreshMonitor::WTF_REQUIRES_LOCK):
+    (WebCore::DisplayRefreshMonitor::WTF_GUARDED_BY_LOCK):
+    (WebCore::DisplayRefreshMonitor::setMaxUnscheduledFireCount): Deleted.
+    (WebCore::DisplayRefreshMonitor::isScheduled const): Deleted.
+    (WebCore::DisplayRefreshMonitor::setIsScheduled): Deleted.
+    (WebCore::DisplayRefreshMonitor::isPreviousFrameDone const): Deleted.
+    (WebCore::DisplayRefreshMonitor::setIsPreviousFrameDone): Deleted.
+    
+    Source/WebKit:
+    
+    * WebProcess/WebPage/mac/DisplayRefreshMonitorMac.cpp:
+    (WebKit::DisplayRefreshMonitorMac::dispatchDisplayDidRefresh):
+    
+    
+    git-svn-id: https://svn.webkit.org/repository/webkit/trunk@286481 268f45cc-cd09-0410-ab3c-d52691b4dbfc
+
+    2021-12-02  Simon Fraser  <simon.fra...@apple.com>
+
+            A Safari tab can rarely get stuck in a state where rendering updates stop happening
+            https://bugs.webkit.org/show_bug.cgi?id=233784
+            rdar://85445072
+
+            Reviewed by Chris Dumez.
+
+            Sometimes a Safari tab can get into a state where rendering updates cease to happen,
+            which manifests as partially broken scrolling, blank tiles revealed when scrolling,
+            and somewhat broken page updates. I was able to sometimes reproduce this by clicking
+            on links in eBay emails from Mail on a system with two displays.
+
+            From the one time I reproduce with logging, the output indicated that DisplayRefreshMonitor::displayLinkFired()
+            would early return because isPreviousFrameDone() was false. The only way for that to occur,
+            barring memory corruption, is if DisplayRefreshMonitorMac::dispatchDisplayDidRefresh() returned early,
+            which it does if the callback comes twice in a single event loop; this may explain the rarity.
+
+            So fix DisplayRefreshMonitorMac::dispatchDisplayDidRefresh() call setIsPreviousFrameDone(true)
+            so the next callback can make progress
+
+            Also add some locking annotations and fix one missing lock, and some release logging.
+
+            * platform/graphics/DisplayRefreshMonitor.cpp:
+            (WebCore::DisplayRefreshMonitor::stop):
+            (WebCore::DisplayRefreshMonitor::firedAndReachedMaxUnscheduledFireCount):
+            (WebCore::DisplayRefreshMonitor::displayLinkFired):
+            * platform/graphics/DisplayRefreshMonitor.h:
+            (WebCore::DisplayRefreshMonitor::WTF_REQUIRES_LOCK):
+            (WebCore::DisplayRefreshMonitor::WTF_GUARDED_BY_LOCK):
+            (WebCore::DisplayRefreshMonitor::setMaxUnscheduledFireCount): Deleted.
+            (WebCore::DisplayRefreshMonitor::isScheduled const): Deleted.
+            (WebCore::DisplayRefreshMonitor::setIsScheduled): Deleted.
+            (WebCore::DisplayRefreshMonitor::isPreviousFrameDone const): Deleted.
+            (WebCore::DisplayRefreshMonitor::setIsPreviousFrameDone): Deleted.
+
 2021-12-02  Russell Epstein  <repst...@apple.com>
 
         Cherry-pick r286410. rdar://problem/85928816

Modified: branches/safari-612-branch/Source/WebCore/platform/graphics/DisplayRefreshMonitor.cpp (286689 => 286690)


--- branches/safari-612-branch/Source/WebCore/platform/graphics/DisplayRefreshMonitor.cpp	2021-12-08 20:23:25 UTC (rev 286689)
+++ branches/safari-612-branch/Source/WebCore/platform/graphics/DisplayRefreshMonitor.cpp	2021-12-08 20:23:29 UTC (rev 286690)
@@ -85,6 +85,8 @@
 void DisplayRefreshMonitor::stop()
 {
     stopNotificationMechanism();
+
+    Locker locker { m_lock };
     setIsScheduled(false);
 }
 
@@ -152,8 +154,6 @@
 
 bool DisplayRefreshMonitor::firedAndReachedMaxUnscheduledFireCount()
 {
-    ASSERT(m_lock.isLocked());
-
     if (isScheduled()) {
         m_unscheduledFireCount = 0;
         return false;
@@ -169,8 +169,10 @@
         Locker locker { m_lock };
 
         // This may be off the main thread.
-        if (!isPreviousFrameDone())
+        if (!isPreviousFrameDone()) {
+            RELEASE_LOG(DisplayLink, "[Web] DisplayRefreshMonitor::displayLinkFired for display %u - previous frame is not complete", displayID());
             return;
+        }
 
         LOG_WITH_STREAM(DisplayLink, stream << "[Web] DisplayRefreshMonitor::displayLinkFired for display " << displayID() << " - scheduled " << isScheduled() << " unscheduledFireCount " << m_unscheduledFireCount << " of " << m_maxUnscheduledFireCount);
         if (firedAndReachedMaxUnscheduledFireCount()) {

Modified: branches/safari-612-branch/Source/WebCore/platform/graphics/DisplayRefreshMonitor.h (286689 => 286690)


--- branches/safari-612-branch/Source/WebCore/platform/graphics/DisplayRefreshMonitor.h	2021-12-08 20:23:25 UTC (rev 286689)
+++ branches/safari-612-branch/Source/WebCore/platform/graphics/DisplayRefreshMonitor.h	2021-12-08 20:23:29 UTC (rev 286690)
@@ -72,23 +72,23 @@
 
     WEBCORE_EXPORT virtual void dispatchDisplayDidRefresh(const DisplayUpdate&);
 
-    Lock& lock() { return m_lock; }
-    void setMaxUnscheduledFireCount(unsigned count) { m_maxUnscheduledFireCount = count; }
+    Lock& lock() WTF_RETURNS_LOCK(m_lock) { return m_lock; }
+    void setMaxUnscheduledFireCount(unsigned count) WTF_REQUIRES_LOCK(m_lock) { m_maxUnscheduledFireCount = count; }
 
     // Returns true if the start was successful.
     WEBCORE_EXPORT virtual bool startNotificationMechanism() = 0;
     WEBCORE_EXPORT virtual void stopNotificationMechanism() = 0;
 
-    bool isScheduled() const { return m_scheduled; }
-    void setIsScheduled(bool scheduled) { m_scheduled = scheduled; }
+    bool isScheduled() const WTF_REQUIRES_LOCK(m_lock) { return m_scheduled; }
+    void setIsScheduled(bool scheduled) WTF_REQUIRES_LOCK(m_lock) { m_scheduled = scheduled; }
 
-    bool isPreviousFrameDone() const { return m_previousFrameDone; }
-    void setIsPreviousFrameDone(bool done) { m_previousFrameDone = done; }
+    bool isPreviousFrameDone() const WTF_REQUIRES_LOCK(m_lock) { return m_previousFrameDone; }
+    void setIsPreviousFrameDone(bool done) WTF_REQUIRES_LOCK(m_lock) { m_previousFrameDone = done; }
 
     WEBCORE_EXPORT void displayDidRefresh(const DisplayUpdate&);
 
 private:
-    bool firedAndReachedMaxUnscheduledFireCount();
+    bool firedAndReachedMaxUnscheduledFireCount() WTF_REQUIRES_LOCK(m_lock);
 
     virtual void adjustPreferredFramesPerSecond(FramesPerSecond) { }
 
@@ -102,11 +102,11 @@
     std::optional<FramesPerSecond> m_maxClientPreferredFramesPerSecond;
 
     Lock m_lock;
-    bool m_scheduled { false };
-    bool m_previousFrameDone { true };
+    bool m_scheduled WTF_GUARDED_BY_LOCK(m_lock) { false };
+    bool m_previousFrameDone WTF_GUARDED_BY_LOCK(m_lock) { true };
     
-    unsigned m_unscheduledFireCount { 0 };
-    unsigned m_maxUnscheduledFireCount { 0 };
+    unsigned m_unscheduledFireCount WTF_GUARDED_BY_LOCK(m_lock) { 0 };
+    unsigned m_maxUnscheduledFireCount WTF_GUARDED_BY_LOCK(m_lock) { 0 };
 };
 
 }

Modified: branches/safari-612-branch/Source/WebKit/ChangeLog (286689 => 286690)


--- branches/safari-612-branch/Source/WebKit/ChangeLog	2021-12-08 20:23:25 UTC (rev 286689)
+++ branches/safari-612-branch/Source/WebKit/ChangeLog	2021-12-08 20:23:29 UTC (rev 286690)
@@ -1,5 +1,79 @@
 2021-12-03  Russell Epstein  <repst...@apple.com>
 
+        Cherry-pick r286481. rdar://problem/85928816
+
+    A Safari tab can rarely get stuck in a state where rendering updates stop happening
+    https://bugs.webkit.org/show_bug.cgi?id=233784
+    rdar://85445072
+    
+    Reviewed by Chris Dumez.
+    
+    Sometimes a Safari tab can get into a state where rendering updates cease to happen,
+    which manifests as partially broken scrolling, blank tiles revealed when scrolling,
+    and somewhat broken page updates. I was able to sometimes reproduce this by clicking
+    on links in eBay emails from Mail on a system with two displays.
+    
+    From the one time I reproduce with logging, the output indicated that DisplayRefreshMonitor::displayLinkFired()
+    would early return because isPreviousFrameDone() was false. The only way for that to occur,
+    barring memory corruption, is if DisplayRefreshMonitorMac::dispatchDisplayDidRefresh() returned early,
+    which it does if the callback comes twice in a single event loop; this may explain the rarity.
+    
+    So fix DisplayRefreshMonitorMac::dispatchDisplayDidRefresh() call setIsPreviousFrameDone(true)
+    so the next callback can make progress
+    
+    Also add some locking annotations and fix one missing lock, and some release logging.
+    
+    Source/WebCore:
+    
+    * platform/graphics/DisplayRefreshMonitor.cpp:
+    (WebCore::DisplayRefreshMonitor::stop):
+    (WebCore::DisplayRefreshMonitor::firedAndReachedMaxUnscheduledFireCount):
+    (WebCore::DisplayRefreshMonitor::displayLinkFired):
+    * platform/graphics/DisplayRefreshMonitor.h:
+    (WebCore::DisplayRefreshMonitor::WTF_REQUIRES_LOCK):
+    (WebCore::DisplayRefreshMonitor::WTF_GUARDED_BY_LOCK):
+    (WebCore::DisplayRefreshMonitor::setMaxUnscheduledFireCount): Deleted.
+    (WebCore::DisplayRefreshMonitor::isScheduled const): Deleted.
+    (WebCore::DisplayRefreshMonitor::setIsScheduled): Deleted.
+    (WebCore::DisplayRefreshMonitor::isPreviousFrameDone const): Deleted.
+    (WebCore::DisplayRefreshMonitor::setIsPreviousFrameDone): Deleted.
+    
+    Source/WebKit:
+    
+    * WebProcess/WebPage/mac/DisplayRefreshMonitorMac.cpp:
+    (WebKit::DisplayRefreshMonitorMac::dispatchDisplayDidRefresh):
+    
+    
+    git-svn-id: https://svn.webkit.org/repository/webkit/trunk@286481 268f45cc-cd09-0410-ab3c-d52691b4dbfc
+
+    2021-12-02  Simon Fraser  <simon.fra...@apple.com>
+
+            A Safari tab can rarely get stuck in a state where rendering updates stop happening
+            https://bugs.webkit.org/show_bug.cgi?id=233784
+            rdar://85445072
+
+            Reviewed by Chris Dumez.
+
+            Sometimes a Safari tab can get into a state where rendering updates cease to happen,
+            which manifests as partially broken scrolling, blank tiles revealed when scrolling,
+            and somewhat broken page updates. I was able to sometimes reproduce this by clicking
+            on links in eBay emails from Mail on a system with two displays.
+
+            From the one time I reproduce with logging, the output indicated that DisplayRefreshMonitor::displayLinkFired()
+            would early return because isPreviousFrameDone() was false. The only way for that to occur,
+            barring memory corruption, is if DisplayRefreshMonitorMac::dispatchDisplayDidRefresh() returned early,
+            which it does if the callback comes twice in a single event loop; this may explain the rarity.
+
+            So fix DisplayRefreshMonitorMac::dispatchDisplayDidRefresh() call setIsPreviousFrameDone(true)
+            so the next callback can make progress
+
+            Also add some locking annotations and fix one missing lock, and some release logging.
+
+            * WebProcess/WebPage/mac/DisplayRefreshMonitorMac.cpp:
+            (WebKit::DisplayRefreshMonitorMac::dispatchDisplayDidRefresh):
+
+2021-12-03  Russell Epstein  <repst...@apple.com>
+
         Cherry-pick r286380. rdar://problem/85928816
 
     Unreviewed build fixes after r286346.

Modified: branches/safari-612-branch/Source/WebKit/WebProcess/WebPage/RemoteLayerTree/RemoteLayerTreeDisplayRefreshMonitor.mm (286689 => 286690)


--- branches/safari-612-branch/Source/WebKit/WebProcess/WebPage/RemoteLayerTree/RemoteLayerTreeDisplayRefreshMonitor.mm	2021-12-08 20:23:25 UTC (rev 286689)
+++ branches/safari-612-branch/Source/WebKit/WebProcess/WebPage/RemoteLayerTree/RemoteLayerTreeDisplayRefreshMonitor.mm	2021-12-08 20:23:29 UTC (rev 286690)
@@ -67,11 +67,14 @@
     if (!m_drawingArea)
         return false;
 
-    if (!isScheduled()) {
-        LOG_WITH_STREAM(DisplayLink, stream << "RemoteLayerTreeDisplayRefreshMonitor::requestRefreshCallback - triggering update");
-        static_cast<DrawingArea&>(*m_drawingArea.get()).triggerRenderingUpdate();
-    }
+    Locker locker { lock() };
 
+    if (isScheduled())
+        return true;
+
+    LOG_WITH_STREAM(DisplayLink, stream << "RemoteLayerTreeDisplayRefreshMonitor::requestRefreshCallback - triggering update");
+    static_cast<DrawingArea&>(*m_drawingArea.get()).triggerRenderingUpdate();
+
     setIsScheduled(true);
     return true;
 }
@@ -78,12 +81,15 @@
 
 void RemoteLayerTreeDisplayRefreshMonitor::didUpdateLayers()
 {
-    setIsScheduled(false);
+    {
+        Locker locker { lock() };
+        setIsScheduled(false);
 
-    if (!isPreviousFrameDone())
-        return;
+        if (!isPreviousFrameDone())
+            return;
 
-    setIsPreviousFrameDone(false);
+        setIsPreviousFrameDone(false);
+    }
     displayDidRefresh(m_currentUpdate);
     m_currentUpdate = m_currentUpdate.nextUpdate();
 }

Modified: branches/safari-612-branch/Source/WebKit/WebProcess/WebPage/mac/DisplayRefreshMonitorMac.cpp (286689 => 286690)


--- branches/safari-612-branch/Source/WebKit/WebProcess/WebPage/mac/DisplayRefreshMonitorMac.cpp	2021-12-08 20:23:25 UTC (rev 286689)
+++ branches/safari-612-branch/Source/WebKit/WebProcess/WebPage/mac/DisplayRefreshMonitorMac.cpp	2021-12-08 20:23:29 UTC (rev 286690)
@@ -60,8 +60,12 @@
 void DisplayRefreshMonitorMac::dispatchDisplayDidRefresh(const DisplayUpdate& displayUpdate)
 {
     // FIXME: This will perturb displayUpdate.
-    if (!m_firstCallbackInCurrentRunloop)
+    if (!m_firstCallbackInCurrentRunloop) {
+        RELEASE_LOG(DisplayLink, "[Web] DisplayRefreshMonitorMac::dispatchDisplayDidRefresh() for display %u - m_firstCallbackInCurrentRunloop is false", displayID());
+        Locker locker { lock() };
+        setIsPreviousFrameDone(true);
         return;
+    }
 
     DisplayRefreshMonitor::dispatchDisplayDidRefresh(displayUpdate);
 }
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to