⚠ Archived content — this site is no longer maintained.   Current WebKit documentation is at docs.webkit.org.

Changeset 204057 in webkit


Ignore:
Timestamp:
Aug 2, 2016, 6:22:25 PM (10 years ago)
Author:
timothy_horton@apple.com
Message:

REGRESSION (r203385): Frequent RELEASE_ASSERT in WebKit::RemoteLayerTreeDrawingArea::flushLayers()
https://bugs.webkit.org/show_bug.cgi?id=160481
<rdar://problem/27534205>

Reviewed by Simon Fraser.

  • UIProcess/mac/RemoteLayerTreeDrawingAreaProxy.h:
  • UIProcess/mac/RemoteLayerTreeDrawingAreaProxy.mm:

(WebKit::RemoteLayerTreeDrawingAreaProxy::commitLayerTree):
(WebKit::RemoteLayerTreeDrawingAreaProxy::didRefreshDisplay):
(WebKit::RemoteLayerTreeDrawingAreaProxy::waitForDidUpdateViewState):
If the UI process sends a didUpdate message while the Web process is in
the middle of flushing on a background thread, the drawing area will
allow another commit to start on the main thread, which then (rightfully)
causes the RELEASE_ASSERT.

This is normally not a problem, because didRefreshDisplay (which sends the didUpdate)
bails if m_didUpdateMessageState is anything other than NotSent, and m_didUpdateMessageState
is only NotSent if the Web process has sent a commit (and thus will not commit again until
it gets a didUpdate). This is the fundamental mechanism that avoids multiple commits being
in flight at once.

In r203385, I added a path where didRefreshDisplay could be called
before the first commit arrived (by way of
_applicationWillEnterForeground -> viewStateDidChange -> waitForDidUpdateViewState).

This caused trouble because m_didUpdateMessageState is initialized to NotSent,
which means that we could end up sending a didUpdate immediately, before the first
commit arrives - even worse, while the first commit is being flushed on a background thread,
leading the aforementioned RELEASE_ASSERT to fire.

Instead, initialize it to Sent (which I've renamed to DoesNotNeedDidUpdate), so that
we won't send a didUpdate until after the first commit arrives (at which point
the two processes are in agreement about the order of things).

It's not currently possible to API test this for multiple reasons, though it is fairly
easy to write a test app that reproduces reliably (by simulating suspend/resume notifications
inside the didFinishNavigation: callback).

Location:
trunk/Source/WebKit2
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebKit2/ChangeLog

    r204053 r204057  
     12016-08-02  Tim Horton  <timothy_horton@apple.com>
     2
     3        REGRESSION (r203385): Frequent RELEASE_ASSERT in WebKit::RemoteLayerTreeDrawingArea::flushLayers()
     4        https://bugs.webkit.org/show_bug.cgi?id=160481
     5        <rdar://problem/27534205>
     6
     7        Reviewed by Simon Fraser.
     8
     9        * UIProcess/mac/RemoteLayerTreeDrawingAreaProxy.h:
     10        * UIProcess/mac/RemoteLayerTreeDrawingAreaProxy.mm:
     11        (WebKit::RemoteLayerTreeDrawingAreaProxy::commitLayerTree):
     12        (WebKit::RemoteLayerTreeDrawingAreaProxy::didRefreshDisplay):
     13        (WebKit::RemoteLayerTreeDrawingAreaProxy::waitForDidUpdateViewState):
     14        If the UI process sends a didUpdate message while the Web process is in
     15        the middle of flushing on a background thread, the drawing area will
     16        allow another commit to start on the main thread, which then (rightfully)
     17        causes the RELEASE_ASSERT.
     18
     19        This is normally not a problem, because didRefreshDisplay (which sends the didUpdate)
     20        bails if m_didUpdateMessageState is anything other than NotSent, and m_didUpdateMessageState
     21        is only NotSent if the Web process has sent a commit (and thus will not commit again until
     22        it gets a didUpdate). This is the fundamental mechanism that avoids multiple commits being
     23        in flight at once.
     24
     25        In r203385, I added a path where didRefreshDisplay could be called
     26        before the first commit arrived (by way of
     27        _applicationWillEnterForeground -> viewStateDidChange -> waitForDidUpdateViewState).
     28
     29        This caused trouble because m_didUpdateMessageState is initialized to NotSent,
     30        which means that we could end up sending a didUpdate immediately, before the first
     31        commit arrives - even worse, while the first commit is being flushed on a background thread,
     32        leading the aforementioned RELEASE_ASSERT to fire.
     33
     34        Instead, initialize it to Sent (which I've renamed to DoesNotNeedDidUpdate), so that
     35        we won't send a didUpdate until after the first commit arrives (at which point
     36        the two processes are in agreement about the order of things).
     37
     38        It's not currently possible to API test this for multiple reasons, though it is fairly
     39        easy to write a test app that reproduces reliably (by simulating suspend/resume notifications
     40        inside the didFinishNavigation: callback).
     41
    1422016-08-02  Enrica Casucci  <enrica@apple.com>
    243
  • trunk/Source/WebKit2/UIProcess/mac/RemoteLayerTreeDrawingAreaProxy.h

    r202291 r204057  
    9292    RemoteLayerTreeHost m_remoteLayerTreeHost;
    9393    bool m_isWaitingForDidUpdateGeometry { false };
    94     enum DidUpdateMessageState { NotSent, Sent, MissedCommit };
    95     DidUpdateMessageState m_didUpdateMessageState { NotSent };
     94    enum DidUpdateMessageState { DoesNotNeedDidUpdate, NeedsDidUpdate, MissedCommit };
     95    DidUpdateMessageState m_didUpdateMessageState { DoesNotNeedDidUpdate };
    9696
    9797    WebCore::IntSize m_lastSentSize;
  • trunk/Source/WebKit2/UIProcess/mac/RemoteLayerTreeDrawingAreaProxy.mm

    r203385 r204057  
    228228
    229229#if PLATFORM(IOS)
    230     if (std::exchange(m_didUpdateMessageState, NotSent) == MissedCommit)
     230    if (std::exchange(m_didUpdateMessageState, NeedsDidUpdate) == MissedCommit)
    231231        didRefreshDisplay(monotonicallyIncreasingTime());
    232232    [m_displayLinkHandler schedule];
    233233#else
    234     m_didUpdateMessageState = NotSent;
     234    m_didUpdateMessageState = NeedsDidUpdate;
    235235    didRefreshDisplay(monotonicallyIncreasingTime());
    236236#endif
     
    400400        return;
    401401
    402     if (m_didUpdateMessageState != NotSent) {
     402    if (m_didUpdateMessageState != NeedsDidUpdate) {
    403403        m_didUpdateMessageState = MissedCommit;
    404404#if PLATFORM(IOS)
     
    408408    }
    409409   
    410     m_didUpdateMessageState = Sent;
     410    m_didUpdateMessageState = DoesNotNeedDidUpdate;
    411411
    412412    TraceScope tracingScope(RAFDidRefreshDisplayStart, RAFDidRefreshDisplayEnd);
     
    426426    // We must send the didUpdate message before blocking on the next commit, otherwise
    427427    // we can be guaranteed that the next commit won't come until after the waitForAndDispatchImmediately times out.
    428     if (m_didUpdateMessageState != Sent)
     428    if (m_didUpdateMessageState != DoesNotNeedDidUpdate)
    429429        didRefreshDisplay(monotonicallyIncreasingTime());
    430430
Note: See TracChangeset for help on using the changeset viewer.