Bug 212461

Summary: AutoTrader crashed while browsing search results
Product: WebKit Reporter: Dean Jackson <dino>
Component: New BugsAssignee: Dean Jackson <dino>
Status: RESOLVED FIXED    
Severity: Normal CC: cdumez, changseok, esprehn+autocc, ews-watchlist, graouts, gyuyoung.kim, kangil.han, kbr, kondapallykalyan, noam, sam, simon.fraser, webkit-bug-importer
Priority: P2 Keywords: InRadar
Version: WebKit Nightly Build   
Hardware: Unspecified   
OS: Unspecified   
Bug Depends on:    
Bug Blocks: 214660, 210153, 212271, 212647, 218177    
Attachments:
Description Flags
Patch
none
Patch
none
Patch sam: review+

Description Dean Jackson 2020-05-28 05:12:56 PDT
AutoTrader crashed while browsing search results
Comment 1 Dean Jackson 2020-05-28 05:16:20 PDT
We've noticed lots of crashes on iOS since swapping to the ANGLE backend. In every case, the stack trace points to the main thread performing some GL operations instead of the web thread. This indicates it is specific to WebKit1, and the fact that CoreAnimation is the one that calls WebGLLayer's display method on a separate thread.
Comment 2 Dean Jackson 2020-05-28 05:16:24 PDT
rdar://60733185
Comment 3 Dean Jackson 2020-05-28 05:17:10 PDT
The fix for this looks like it might address 212251 as well.
Comment 4 Dean Jackson 2020-05-28 06:39:51 PDT
Created attachment 400450 [details]
Patch
Comment 5 Dean Jackson 2020-05-28 06:49:21 PDT
I was seeing some strange failures in WebGL tests locally (test timeouts), but only on WK2. Looking forward to the EWS results.

I wonder if there could be an issue with the timing of the prepare v display calls. If possible, I'd love to know there is a guarantee that display will always be called after prepare, and we'd never get two displays or prepares in a row.
Comment 6 Sam Weinig 2020-05-28 07:27:13 PDT
Comment on attachment 400450 [details]
Patch

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

> Source/WebCore/html/CanvasBase.cpp:55
> +HashSet<CanvasBase*>& CanvasBase::instancesRequiringPreparationForDisplay()
> +{
> +    static NeverDestroyed<HashSet<CanvasBase*>> instances;
> +    return instances;
> +}

I think doing this in CanvasBase is probably a mistake, given it is not safe to use this from multiple threads (and I think OffscreenCanvas works on a non-main thread). I realize this is not a problem currently, as needsPreparationForDisplay() is only true for HTMLCanvasElement, but I think to make this a bit more robust, I would move this set up to HTMLCanvasElement as well. One way to do this would be to replace needsPreparationForDisplay() with a virtual function like addToRequiringPreparationForDisplaySetIfNeeded() (but with a better name) that only HTMLCanvasElement would implement in a meaningful way. 

I am also generally dubious of global state like this, and if possible aim to avoid it. Can the set of HTMLCanvasElements live on the Page or something like that instead?
Comment 7 Dean Jackson 2020-05-28 07:32:03 PDT
Comment on attachment 400450 [details]
Patch

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

>> Source/WebCore/html/CanvasBase.cpp:55
>> +}
> 
> I think doing this in CanvasBase is probably a mistake, given it is not safe to use this from multiple threads (and I think OffscreenCanvas works on a non-main thread). I realize this is not a problem currently, as needsPreparationForDisplay() is only true for HTMLCanvasElement, but I think to make this a bit more robust, I would move this set up to HTMLCanvasElement as well. One way to do this would be to replace needsPreparationForDisplay() with a virtual function like addToRequiringPreparationForDisplaySetIfNeeded() (but with a better name) that only HTMLCanvasElement would implement in a meaningful way. 
> 
> I am also generally dubious of global state like this, and if possible aim to avoid it. Can the set of HTMLCanvasElements live on the Page or something like that instead?

I originally started with Page calling forEachDocument which would then find the HTMLCanvasElements that the Document considered dirty. Maybe that's a better solution - keep the set there. I ended up with the current approach when I realised I could take a shortcut.

I'll fix it to have Page call into each Document, and the set of canvii that need preparation will live there. An HTMLCanvasElement always has a Document.
Comment 8 Kenneth Russell 2020-05-28 12:53:52 PDT
Does this address both Bug 212271 and Bug 210153? May I block those two bugs on this one since it sounds like the first fix will be done here?
Comment 9 Kenneth Russell 2020-05-28 13:05:03 PDT
Comment on attachment 400450 [details]
Patch

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

> Source/WebCore/html/CanvasBase.h:106
> +    static HashSet<CanvasBase*>& instancesRequiringPreparationForDisplay();

In the next iteration of this patch, could you add some documentation indicating that the set of canvases is transient, and cleared out at the end of each rendered frame? That would help address concerns about the lifetime of the entries in the collection.

Also wondering whether this should hold RefPtrs to the target canvases for GC safety.

> Tools/TestWebKitAPI/Tests/WebKitLegacy/ios/WebGLPrepareDisplayOnWebThread.mm:102
> +    [jsContext evaluateScript:@"loadColorIntoTexture(128, 128, 255, 255)"];

Is this color verified by the test harness? It would be ideal if it were.
Comment 10 Dean Jackson 2020-05-28 13:24:46 PDT
Comment on attachment 400450 [details]
Patch

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

>> Source/WebCore/html/CanvasBase.h:106
>> +    static HashSet<CanvasBase*>& instancesRequiringPreparationForDisplay();
> 
> In the next iteration of this patch, could you add some documentation indicating that the set of canvases is transient, and cleared out at the end of each rendered frame? That would help address concerns about the lifetime of the entries in the collection.
> 
> Also wondering whether this should hold RefPtrs to the target canvases for GC safety.

This is a good point. I think it is a mistake in the current patch that the set is cleared by Page. Instead Page should not need to know it must clear the set, and instead call into something that does the preparation and clears itself.

Also agree on RefPtr.
Comment 11 Dean Jackson 2020-05-28 13:27:41 PDT
Comment on attachment 400450 [details]
Patch

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

>> Tools/TestWebKitAPI/Tests/WebKitLegacy/ios/WebGLPrepareDisplayOnWebThread.mm:102
>> +    [jsContext evaluateScript:@"loadColorIntoTexture(128, 128, 255, 255)"];
> 
> Is this color verified by the test harness? It would be ideal if it were.

It isn't. The goal here was to be doing something from outside the UIWebView that would cause a conflict. However, I'm pretty sure this doesn't do that any more than simply calling draw would.

I do want to get to a point where I can have an API test that manipulates the WebGL and gets back the contents of the canvas.
Comment 12 Dean Jackson 2020-05-29 17:24:28 PDT
Created attachment 400638 [details]
Patch
Comment 13 Dean Jackson 2020-05-29 17:27:23 PDT
*** Bug 210153 has been marked as a duplicate of this bug. ***
Comment 14 Sam Weinig 2020-05-29 17:33:16 PDT
Comment on attachment 400638 [details]
Patch

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

> Source/WebCore/ChangeLog:41
> +            need to run code before being composited. Collect
> +            these objects in a static HashSet, so that the Page

You don't have a static set anymore, but a per-document one.

> Source/WebCore/html/HTMLCanvasElement.cpp:1010
> +        document().canvasNeedsPreparationForDisplay(this);

Seems like the HTMLCanvasElement needs to also remove itself from the Document::canvasNeedsPreparationForDisplay set if its destroyed. It may also want to reset any of this state if it moves between documents.

> Source/WebCore/html/HTMLCanvasElement.h:187
> +    bool m_hasInformedNeedForDisplayPreparation { false };

This variable name is a bit awkward.
Comment 15 Dean Jackson 2020-05-29 18:26:57 PDT
Comment on attachment 400638 [details]
Patch

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

>> Source/WebCore/ChangeLog:41
>> +            these objects in a static HashSet, so that the Page
> 
> You don't have a static set anymore, but a per-document one.

:thumbsup:

>> Source/WebCore/html/HTMLCanvasElement.cpp:1010
>> +        document().canvasNeedsPreparationForDisplay(this);
> 
> Seems like the HTMLCanvasElement needs to also remove itself from the Document::canvasNeedsPreparationForDisplay set if its destroyed. It may also want to reset any of this state if it moves between documents.

Will do.

>> Source/WebCore/html/HTMLCanvasElement.h:187
>> +    bool m_hasInformedNeedForDisplayPreparation { false };
> 
> This variable name is a bit awkward.

I'm getting rid of it as I move to making Document a CanvasObserver. I worried about the (slight) cost of the hash lookup each time the canvas draws something (although the document will ask the canvas if it needs preparation before doing that).
Comment 16 Kenneth Russell 2020-05-29 18:50:52 PDT
Comment on attachment 400638 [details]
Patch

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

Looks good overall; a couple of questions.

> Source/WebCore/ChangeLog:27
> +        which had been difficult to diagnose.

This block should be removed now that it's been determined that https://bugs.webkit.org/show_bug.cgi?id=212277 was the root cause.

> Source/WebCore/dom/Document.h:1819
> +    HashSet<HTMLCanvasElement*> m_canvasesNeedingDisplayPreparation;

Is RefPtr<HTMLCanvasElement> not an option because it will introduce a graph cycle? That might address the lifetime consideration.

>> Source/WebCore/html/HTMLCanvasElement.cpp:1010
>> +        document().canvasNeedsPreparationForDisplay(this);
> 
> Seems like the HTMLCanvasElement needs to also remove itself from the Document::canvasNeedsPreparationForDisplay set if its destroyed. It may also want to reset any of this state if it moves between documents.

Is document() typically non-null in HTMLCanvasElement::~HTMLCanvasElement? If not, when would be a good time to unregister it?
Comment 17 Dean Jackson 2020-05-29 19:13:04 PDT
Created attachment 400646 [details]
Patch
Comment 18 Dean Jackson 2020-05-31 16:13:30 PDT
Comment on attachment 400638 [details]
Patch

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

>> Source/WebCore/ChangeLog:27
>> +        which had been difficult to diagnose.
> 
> This block should be removed now that it's been determined that https://bugs.webkit.org/show_bug.cgi?id=212277 was the root cause.

Removed.

>> Source/WebCore/dom/Document.h:1819
>> +    HashSet<HTMLCanvasElement*> m_canvasesNeedingDisplayPreparation;
> 
> Is RefPtr<HTMLCanvasElement> not an option because it will introduce a graph cycle? That might address the lifetime consideration.

It probably would be fine to use a RefPtr but other sets/lists on document don't. As long as the lifetime is guaranteed via CanvasObserver I think we're ok.

>>>> Source/WebCore/html/HTMLCanvasElement.cpp:1010
>>>> +        document().canvasNeedsPreparationForDisplay(this);
>>> 
>>> Seems like the HTMLCanvasElement needs to also remove itself from the Document::canvasNeedsPreparationForDisplay set if its destroyed. It may also want to reset any of this state if it moves between documents.
>> 
>> Will do.
> 
> Is document() typically non-null in HTMLCanvasElement::~HTMLCanvasElement? If not, when would be a good time to unregister it?

document() is always a Ref<>, never null.
Comment 19 Dean Jackson 2020-05-31 16:21:35 PDT
Committed r262366: <https://trac.webkit.org/changeset/262366>
Comment 20 Ryan Haddad 2020-06-01 21:40:54 PDT
(In reply to Dean Jackson from comment #19)
> Committed r262366: <https://trac.webkit.org/changeset/262366>
As win-ews detected, this change broke the Windows build:
C:\cygwin\worker\win10-release\build\Source\WebCore\html/HTMLCanvasElement.cpp(1028,61): error C2027: use of undefined type 'WebCore::WebGLRenderingContextBase' (compiling source file C:\cygwin\worker\win10-release\build\WebKitBuild\Release\DerivedSources\WebCore\unified-sources\UnifiedSource-950a39b6-5.cpp) [C:\cygwin\worker\win10-release\build\WebKitBuild\Release\Source\WebCore\WebCore.vcxproj]

https://build.webkit.org/builders/Apple%20Win%2010%20Release%20%28Build%29/builds/13995/steps/compile-webkit/logs/stdio
Comment 21 Ryan Haddad 2020-06-02 11:51:34 PDT
(In reply to Ryan Haddad from comment #20)
> (In reply to Dean Jackson from comment #19)
> > Committed r262366: <https://trac.webkit.org/changeset/262366>
> As win-ews detected, this change broke the Windows build:
> C:\cygwin\worker\win10-release\build\Source\WebCore\html/HTMLCanvasElement.
> cpp(1028,61): error C2027: use of undefined type
> 'WebCore::WebGLRenderingContextBase' (compiling source file
> C:\cygwin\worker\win10-
> release\build\WebKitBuild\Release\DerivedSources\WebCore\unified-
> sources\UnifiedSource-950a39b6-5.cpp)
> [C:\cygwin\worker\win10-
> release\build\WebKitBuild\Release\Source\WebCore\WebCore.vcxproj]
> 
> https://build.webkit.org/builders/Apple%20Win%2010%20Release%20%28Build%29/
> builds/13995/steps/compile-webkit/logs/stdio
This was fixed with https://trac.webkit.org/changeset/262418/webkit
Comment 22 Simon Fraser (smfr) 2020-06-03 19:47:11 PDT
Comment on attachment 400646 [details]
Patch

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

> Source/WebCore/dom/Document.cpp:8590
> +        auto refCountedCanvas = makeRefPtr(canvas);

Normally we call this protectedFoo,

> Source/WebCore/dom/Document.cpp:8601
> +        auto* canvas = downcast<HTMLCanvasElement>(&canvasBase);
> +        if (canvas->needsPreparationForDisplay())
> +            m_canvasesNeedingDisplayPreparation.add(canvas);

Would be nicer if canvas were a reference.

> Source/WebCore/dom/Document.h:1823
> +    HashSet<HTMLCanvasElement*> m_canvasesNeedingDisplayPreparation;

Should this have been a weak hash set?

> Source/WebCore/platform/graphics/cocoa/WebGLLayer.h:65
> +    BOOL _prepared;

preparedForWhat?

> Tools/TestWebKitAPI/Tests/WebKitLegacy/ios/WebGLPrepareDisplayOnWebThread.mm:39
> +@interface WebGLPrepareDisplayOnWebThreadDelegate : NSObject <UIWebViewDelegate> {
> +}

Are the {} necessary?
Comment 23 Dean Jackson 2020-06-04 18:56:19 PDT
Addressed comments in https://bugs.webkit.org/show_bug.cgi?id=212647