Bug 171900 - REGRESSION (216471): Infinite repaint-drawing loop when asynchronously decoding incomplete image frames
Summary: REGRESSION (216471): Infinite repaint-drawing loop when asynchronously decodi...
Status: RESOLVED FIXED
Alias: None
Product: WebKit
Classification: Unclassified
Component: Images (show other bugs)
Version: WebKit Nightly Build
Hardware: Unspecified Unspecified
: P2 Normal
Assignee: Said Abou-Hallawa
URL:
Keywords: InRadar
Depends on:
Blocks: 172009
  Show dependency treegraph
 
Reported: 2017-05-09 17:37 PDT by Said Abou-Hallawa
Modified: 2017-05-15 19:43 PDT (History)
7 users (show)

See Also:


Attachments
Patch (17.63 KB, patch)
2017-05-09 18:01 PDT, Said Abou-Hallawa
no flags Details | Formatted Diff | Diff
Patch (17.41 KB, patch)
2017-05-10 09:24 PDT, Said Abou-Hallawa
no flags Details | Formatted Diff | Diff
Patch (18.51 KB, patch)
2017-05-11 10:42 PDT, Said Abou-Hallawa
buildbot: commit-queue-
Details | Formatted Diff | Diff
Archive of layout-test-results from ews103 for mac-elcapitan (984.83 KB, application/zip)
2017-05-11 11:48 PDT, Build Bot
no flags Details
Archive of layout-test-results from ews113 for mac-elcapitan (1.78 MB, application/zip)
2017-05-11 12:04 PDT, Build Bot
no flags Details
Patch (19.31 KB, patch)
2017-05-11 13:07 PDT, Said Abou-Hallawa
no flags Details | Formatted Diff | Diff
Patch (19.25 KB, patch)
2017-05-11 13:13 PDT, Said Abou-Hallawa
no flags Details | Formatted Diff | Diff
Patch (26.54 KB, patch)
2017-05-11 17:26 PDT, Said Abou-Hallawa
no flags Details | Formatted Diff | Diff
Patch (34.94 KB, patch)
2017-05-11 18:22 PDT, Said Abou-Hallawa
no flags Details | Formatted Diff | Diff
Patch (35.58 KB, patch)
2017-05-12 08:48 PDT, Said Abou-Hallawa
no flags Details | Formatted Diff | Diff
Patch (40.59 KB, patch)
2017-05-12 10:50 PDT, Said Abou-Hallawa
no flags Details | Formatted Diff | Diff
Patch (40.54 KB, patch)
2017-05-15 14:46 PDT, Said Abou-Hallawa
no flags Details | Formatted Diff | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Said Abou-Hallawa 2017-05-09 17:37:15 PDT
The change <http://trac.webkit.org/changeset/216471> can cause infinite repaint-drawing loop when asynchronously decoding incomplete image frames. It has two flaws:

1. In BitmapImage::draw() we check frameIsCompleteAtIndex() and if it is false, we request decoding the image frame. When the image frame finishes decoding, the image element is repainted and hence the BitmapImage::draw is called back. We check again if frameIsCompleteAtIndex() and we find it false, so we request decoding the image frame although we may not received new data. This can cause an infinite repaint-draw loop. With small number of images, the loop can be broken if the main thread gets a chance to set new data in the ImageDecoder and eventually it answesr frameIsCompleteAtIndex() with true. But if the page has many large images, the main will be busy repainting and drawing the incomplete large images. And it may not have a chance to complete setting the images' data.

2. The decoding thread caches the ImageFrame metadata on the main thread in ImageFrameCache::cacheFrameMetadataAtIndex(). It calls ImageDecoder::frameIsCompleteAtIndex() to check whether this ImageFrame has a partial decoded ImageFrame or a complete one. This actually might return the wrong answer. If while decoding the image frame in the decoding thread or while waiting the callOnMainThread() to be dispatched, the rest of data are set in the ImageDecoder. The ImageDecoder::frameIsCompleteAtIndex() will return true although the decoded image frame is actually incomplete.
Comment 1 Said Abou-Hallawa 2017-05-09 18:01:38 PDT
Created attachment 309565 [details]
Patch
Comment 2 Said Abou-Hallawa 2017-05-10 09:22:00 PDT
I could not write an HTTP test for this patch. I could only reproduce the infinite loop using a page with many large images. The main thread was busy doing repaint-draw for the same set of incomplete images. With one image or two the infinite loop can't happen.
Comment 3 Said Abou-Hallawa 2017-05-10 09:24:34 PDT
Created attachment 309614 [details]
Patch
Comment 4 Radar WebKit Bug Importer 2017-05-10 09:25:39 PDT
<rdar://problem/32109397>
Comment 5 Said Abou-Hallawa 2017-05-11 10:42:14 PDT
Created attachment 309721 [details]
Patch
Comment 6 Build Bot 2017-05-11 11:48:46 PDT
Comment on attachment 309721 [details]
Patch

Attachment 309721 [details] did not pass mac-ews (mac):
Output: http://webkit-queues.webkit.org/results/3719194

New failing tests:
workers/bomb.html
Comment 7 Build Bot 2017-05-11 11:48:47 PDT
Created attachment 309738 [details]
Archive of layout-test-results from ews103 for mac-elcapitan

The attached test failures were seen while running run-webkit-tests on the mac-ews.
Bot: ews103  Port: mac-elcapitan  Platform: Mac OS X 10.11.6
Comment 8 Build Bot 2017-05-11 12:04:34 PDT
Comment on attachment 309721 [details]
Patch

Attachment 309721 [details] did not pass mac-debug-ews (mac):
Output: http://webkit-queues.webkit.org/results/3719212

New failing tests:
http/tests/misc/slow-loading-animated-image.html
Comment 9 Build Bot 2017-05-11 12:04:36 PDT
Created attachment 309745 [details]
Archive of layout-test-results from ews113 for mac-elcapitan

The attached test failures were seen while running run-webkit-tests on the mac-debug-ews.
Bot: ews113  Port: mac-elcapitan  Platform: Mac OS X 10.11.6
Comment 10 Said Abou-Hallawa 2017-05-11 13:07:55 PDT
Created attachment 309759 [details]
Patch
Comment 11 Said Abou-Hallawa 2017-05-11 13:13:23 PDT
Created attachment 309761 [details]
Patch
Comment 12 Simon Fraser (smfr) 2017-05-11 13:22:08 PDT
Comment on attachment 309761 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=309761&action=review

> Source/WebCore/platform/graphics/BitmapImage.h:210
> +    enum class ImageDecodingStatus { Invalid, Decoding, Ready };

Can we avoid having both ImageDecodingStatus and the ImageFrame::Decoding enum? They are so similar, and I really want ImageFrame::Decoding to be ImageFrame::DecodingStatus.
Comment 13 Said Abou-Hallawa 2017-05-11 17:26:05 PDT
Created attachment 309831 [details]
Patch
Comment 14 Said Abou-Hallawa 2017-05-11 17:27:43 PDT
Comment on attachment 309761 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=309761&action=review

>> Source/WebCore/platform/graphics/BitmapImage.h:210
>> +    enum class ImageDecodingStatus { Invalid, Decoding, Ready };
> 
> Can we avoid having both ImageDecodingStatus and the ImageFrame::Decoding enum? They are so similar, and I really want ImageFrame::Decoding to be ImageFrame::DecodingStatus.

Done. ImageFrame::DecodingStatus has the following values:  { Invalid, Partial, Complete, Decoding }.
Comment 15 Said Abou-Hallawa 2017-05-11 18:22:19 PDT
Created attachment 309841 [details]
Patch
Comment 16 Said Abou-Hallawa 2017-05-12 08:48:09 PDT
Created attachment 309899 [details]
Patch
Comment 17 Said Abou-Hallawa 2017-05-12 10:50:01 PDT
Created attachment 309914 [details]
Patch
Comment 18 Tim Horton 2017-05-15 13:20:58 PDT
Comment on attachment 309914 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=309914&action=review

> Source/WebCore/ChangeLog:15
> +        'Decoding'. This new value will not never be cached in the ImageFrame::
> +        m_decodingStatus. Add a member m_currentFrameDecodingStatus to BitmapImage.

not never? Can we de-double-negativeify this?

> Source/WebCore/ChangeLog:34
> +        which is: does the ImageFrame of the nextFrame has a native image with 
> +        decoded with the native size or not?

Grammar here goes downhill as the sentence goes on.

> Source/WebCore/ChangeLog:66
> +        decides whether another request for the same image frame is allowed or not.

s/decides/decide/

> Source/WebCore/platform/graphics/ImageFrameCache.h:137
> +    void cacheAsyncFrameNativeImageAtIndex(NativeImagePtr&&, size_t, SubsamplingLevel, const DecodingOptions&, ImageFrame::DecodingStatus);

I think the Async should have come at the end of this method name (cacheFrameNativeImageAtIndex{Async}), but that's not relevant to this patch. Also could probably drop the "frame" because this is the "ImageFrameCache" so it's implied, but w/e.

> Source/WebCore/platform/graphics/ImageSource.cpp:-116
> -    m_frameCache->destroyIncompleteDecodedData();

Is BitmapImage the only way to get here? Or are there other paths to get here which won't destroyIncompleteDecodedData now?
Comment 19 Said Abou-Hallawa 2017-05-15 14:46:41 PDT
Created attachment 310169 [details]
Patch
Comment 20 Said Abou-Hallawa 2017-05-15 14:53:40 PDT
Comment on attachment 309914 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=309914&action=review

>> Source/WebCore/ChangeLog:15
>> +        m_decodingStatus. Add a member m_currentFrameDecodingStatus to BitmapImage.
> 
> not never? Can we de-double-negativeify this?

The ChangeLog statement was changed. I added assertions in ImageFrame::setDecodingStatus() and ImageFrame::decodingStatus() to ensure the current or the new value isn't equals to ImageFrame::DecodingStatus::Decoding.

>> Source/WebCore/ChangeLog:34
>> +        decoded with the native size or not?
> 
> Grammar here goes downhill as the sentence goes on.

Fixed.

>> Source/WebCore/ChangeLog:66
>> +        decides whether another request for the same image frame is allowed or not.
> 
> s/decides/decide/

Fixed.

>> Source/WebCore/platform/graphics/ImageFrameCache.h:137
>> +    void cacheAsyncFrameNativeImageAtIndex(NativeImagePtr&&, size_t, SubsamplingLevel, const DecodingOptions&, ImageFrame::DecodingStatus);
> 
> I think the Async should have come at the end of this method name (cacheFrameNativeImageAtIndex{Async}), but that's not relevant to this patch. Also could probably drop the "frame" because this is the "ImageFrameCache" so it's implied, but w/e.

The three functions were renamed to the following:

cacheMetadataAtIndex()
cacheNativeImageAtIndex()
cacheNativeImageAtIndexAsync()

>> Source/WebCore/platform/graphics/ImageSource.cpp:-116
>> -    m_frameCache->destroyIncompleteDecodedData();
> 
> Is BitmapImage the only way to get here? Or are there other paths to get here which won't destroyIncompleteDecodedData now?

BitmapImage::dataChanged() is the only way to change the ImageDecoder data and it is the only caller to ImageSource::dataChanged().
Comment 21 WebKit Commit Bot 2017-05-15 15:10:41 PDT
Comment on attachment 310169 [details]
Patch

Clearing flags on attachment: 310169

Committed r216882: <http://trac.webkit.org/changeset/216882>
Comment 22 WebKit Commit Bot 2017-05-15 15:10:43 PDT
All reviewed patches have been landed.  Closing bug.