Bug 137496

Summary: Add a microtask abstraction
Product: WebKit Reporter: Yoav Weiss <yoav>
Component: New BugsAssignee: Nobody <webkit-unassigned>
Status: RESOLVED FIXED    
Severity: Normal CC: commit-queue, dino, esprehn+autocc, gyuyoung.kim, kangil.han, rniwa, sam, simon.fraser
Priority: P2    
Version: 528+ (Nightly build)   
Hardware: Unspecified   
OS: Unspecified   
Bug Depends on: 142204    
Bug Blocks:    
Attachments:
Description Flags
Patch
none
Patch
none
Patch
none
Patch
none
Patch
none
Patch
none
Patch
none
Patch
none
Patch
none
Patch
none
Patch
none
Patch
none
Patch
none
Patch
none
Patch none

Description Yoav Weiss 2014-10-07 12:49:16 PDT
Add a microtask abstraction
Comment 1 Yoav Weiss 2014-10-07 13:00:34 PDT
Created attachment 239429 [details]
Patch
Comment 2 Yoav Weiss 2014-10-07 13:04:40 PDT
I've split out the MicroTask part from https://bugs.webkit.org/show_bug.cgi?id=134488 into this separate bug & patch.

Since the MicroTask interface doesn't have users/implementors yet, this patch is not testable on its own. Tests will be added with the async image loading patch.
Comment 3 Yoav Weiss 2014-10-07 13:24:59 PDT
Created attachment 239430 [details]
Patch
Comment 4 Darin Adler 2014-10-08 09:10:48 PDT
Comment on attachment 239430 [details]
Patch

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

> Source/WebCore/dom/MicroTask.h:28
> +class MicroTask : public RefCounted<MicroTask> {

Why do these need to be reference counted? Can we use std::unique_ptr instead and use single ownership?

> Source/WebCore/dom/MicroTaskQueue.h:31
> +class MicroTaskQueue : public RefCounted<MicroTaskQueue> {

I don’t think we should use a reference-counted object just to hold a Vector. We should simply have the vector of tasks in Document itself. This level of indirection doesn’t help us. No need to have a class for this.

> Source/WebCore/dom/MicroTaskQueue.h:42
> +        for (auto task : m_tasks)
> +            task->run();
> +        m_tasks.clear();

What prevents this from being reentered, running the same task more than once?
Comment 5 Darin Adler 2014-10-08 09:11:41 PDT
Comment on attachment 239430 [details]
Patch

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

> Source/WebCore/dom/ScriptRunner.cpp:121
> +    m_document.runMicroTasks();

What guarantees we aren’t already executing a script here?

> Source/WebCore/html/parser/HTMLScriptRunner.cpp:236
> +    m_document->runMicroTasks();

What guarantees we aren’t already inside a script here?
Comment 6 Yoav Weiss 2014-10-10 00:18:17 PDT
Created attachment 239604 [details]
Patch
Comment 7 Yoav Weiss 2014-10-10 00:27:08 PDT
(In reply to comment #4)
> (From update of attachment 239430 [details])
> View in context: https://bugs.webkit.org/attachment.cgi?id=239430&action=review
> 
> > Source/WebCore/dom/MicroTask.h:28
> > +class MicroTask : public RefCounted<MicroTask> {
> 
> Why do these need to be reference counted? Can we use std::unique_ptr instead and use single ownership?

You're right, they don't need to be. I removed it.

> 
> > Source/WebCore/dom/MicroTaskQueue.h:31
> > +class MicroTaskQueue : public RefCounted<MicroTaskQueue> {
> 
> I don’t think we should use a reference-counted object just to hold a Vector. We should simply have the vector of tasks in Document itself. This level of indirection doesn’t help us. No need to have a class for this.

Removed.

> 
> > Source/WebCore/dom/MicroTaskQueue.h:42
> > +        for (auto task : m_tasks)
> > +            task->run();
> > +        m_tasks.clear();
> 
> What prevents this from being reentered, running the same task more than once?

I assumed the microtask queue would be accessed only from the main thread. I've added ASSERTs to make sure this assumption stands.

(In reply to comment #5)
> (From update of attachment 239430 [details])
> View in context: https://bugs.webkit.org/attachment.cgi?id=239430&action=review
> 
> > Source/WebCore/dom/ScriptRunner.cpp:121
> > +    m_document.runMicroTasks();
> 
> What guarantees we aren’t already executing a script here?

I've interpreted that code as the code running HTMLScriptElement's code one by one in the loop above my code addition. Therefore, I didn't think such protection is necessary.
It's highly probable that I was wrong about that though. If that's the case, I'd appreciate guidance at to how I can add the required protection here.

> 
> > Source/WebCore/html/parser/HTMLScriptRunner.cpp:236
> > +    m_document->runMicroTasks();
> 
> What guarantees we aren’t already inside a script here?

I've now protected against this. Thanks for catching that.
Comment 8 Yoav Weiss 2014-10-20 01:50:18 PDT
ping
Comment 9 Myles C. Maxfield 2014-10-22 13:34:04 PDT
Comment on attachment 239604 [details]
Patch

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

> Source/WebCore/ChangeLog:8
> +        No new tests for this patch. Tests will be added in a followup patch that uses it.

:(
Comment 10 Yoav Weiss 2014-10-22 14:29:14 PDT
Yeah, I'm not thrilled about adding unused and untestable code either, but I was told that it's better to split out the microtask portion from https://bugs.webkit.org/show_bug.cgi?id=134488 that includes usage (and will include tests).
Comment 11 Yoav Weiss 2014-11-28 07:42:12 PST
Ping. This is still waiting for a review. Thanks :)
Comment 12 Sam Weinig 2014-11-28 17:17:41 PST
Microtasks are defined as being per Event Loop (https://html.spec.whatwg.org/multipage/webappapis.html#event-loops), which is not quite the same as per-document.  I believe this would be observable, as microtasks in multiple documents would be run out of phase.

I also think this would be testable by moving Mutation Observers over to the new abstraction as you build it. Or, if you prefer a less invasive change at the same time, you could add an Internal function that queues a microtask, and use that for testing.
Comment 13 Yoav Weiss 2014-11-30 14:16:26 PST
Thanks for the review! :)

> Microtasks are defined as being per Event Loop 
> (https://html.spec.whatwg.org/multipage/webappapis.html#event-loops), which is
> not quite the same as per-document.  I believe this would be observable, as
> microtasks in multiple documents would be run out of phase.

What would be a better place from which to call runMicroTasks so that the microtasks of different documents would be in sync?
Comment 14 Sam Weinig 2014-11-30 14:53:13 PST
(In reply to comment #13)
> Thanks for the review! :)
> 
> > Microtasks are defined as being per Event Loop 
> > (https://html.spec.whatwg.org/multipage/webappapis.html#event-loops), which is
> > not quite the same as per-document.  I believe this would be observable, as
> > microtasks in multiple documents would be run out of phase.
> 
> What would be a better place from which to call runMicroTasks so that the
> microtasks of different documents would be in sync?

It would either need to be global (which I would rather it not be) or on a new object that is shared between Documents that need to be synchronized. As the spec says, the documents that need to be synchronized are the ones that need to share an Event Loop of which, "There must be at least one browsing context event loop per user agent, and at most one per unit of related similar-origin browsing contexts".
Comment 15 Yoav Weiss 2014-12-05 07:02:52 PST
Is there an object that is common to all Documents that need to be synchronized, which can own the MicroTask Queue? I'd like to avoid a global/singleton as well.
Comment 16 Sam Weinig 2014-12-05 09:17:25 PST
(In reply to comment #15)
> Is there an object that is common to all Documents that need to be
> synchronized, which can own the MicroTask Queue? I'd like to avoid a
> global/singleton as well.

There is not one currently.
Comment 17 Yoav Weiss 2015-02-15 14:49:32 PST
Going back to this, I wonder what's the best way to avoid a global here.
If I understand correctly, we have a single event loop per renderer process. We want microtasks running on this event loop to be synchronized (e.g. pages from a similar origin must be on the same event loop, and therefore should have their microtasks running sequentially).

Looking around the code, it seems like the one-to-one relationship with Document goes all the way up to WebPage. IIUC, WebPages are then owned by WebKit2's WebProcess, which makes me think that WebProcess is the right place to maintain such an object that would represent EventLoop synchronization. Is that correct?
If so, all the WebPages owned by a single WebProcess share the same event loop, right?
Comment 18 Yoav Weiss 2015-02-16 14:36:36 PST
After adding the microtask queue on WebProcess, I realized that it'd mean I need to add it on Source/WebKit as well, which seemed messy.
I'm now leaning towards adding it as a singleton. Please let me know if this is not an acceptable way forward.
Comment 19 Sam Weinig 2015-02-17 10:23:34 PST
(In reply to comment #18)
> After adding the microtask queue on WebProcess, I realized that it'd mean I
> need to add it on Source/WebKit as well, which seemed messy.
> I'm now leaning towards adding it as a singleton. Please let me know if this
> is not an acceptable way forward.

After trying to think this through, I think a singleton is probably the best way forward for now.
Comment 20 Yoav Weiss 2015-02-19 01:51:49 PST
Created attachment 246886 [details]
Patch
Comment 21 Yoav Weiss 2015-02-19 01:54:07 PST
I've added a singleton object as well as testing through an internals method.
Comment 22 Yoav Weiss 2015-02-19 02:44:28 PST
Created attachment 246888 [details]
Patch
Comment 23 Yoav Weiss 2015-02-19 03:58:40 PST
Created attachment 246889 [details]
Patch
Comment 24 Yoav Weiss 2015-02-19 14:35:45 PST
Created attachment 246922 [details]
Patch
Comment 25 Yoav Weiss 2015-02-20 16:05:28 PST
Created attachment 247006 [details]
Patch
Comment 26 Yoav Weiss 2015-02-22 08:37:48 PST
Created attachment 247083 [details]
Patch
Comment 27 Yoav Weiss 2015-02-22 09:39:22 PST
And now it's all green! Sam, can you please take another look?
Comment 28 Sam Weinig 2015-03-01 15:21:09 PST
Comment on attachment 247083 [details]
Patch

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

What happens if a microtask for one document is still enqued when that document goes away or into the back/forward cache? A great follow up would be move MutationObservers to be built on this.

> Source/WebCore/testing/MicroTaskTest.h:31
> +class MicroTaskTest: public MicroTask {

Should have a space after MicroTaskTest.
Comment 29 Yoav Weiss 2015-03-02 07:54:34 PST
Created attachment 247667 [details]
Patch
Comment 30 Yoav Weiss 2015-03-02 07:57:13 PST
> What happens if a microtask for one document is still enqued when that document goes away or into the back/forward cache?

Good catch. I've changed the test MicroTask to only hold a weak pointer to the document, and added a test to make sure we handle that case well.

> Should have a space after MicroTaskTest.

Added.
Comment 31 Yoav Weiss 2015-03-02 08:12:22 PST
Created attachment 247669 [details]
Patch
Comment 32 Yoav Weiss 2015-03-02 14:10:29 PST
Created attachment 247697 [details]
Patch
Comment 33 WebKit Commit Bot 2015-03-02 17:05:03 PST
Comment on attachment 247697 [details]
Patch

Clearing flags on attachment: 247697

Committed r180911: <http://trac.webkit.org/changeset/180911>
Comment 34 WebKit Commit Bot 2015-03-02 17:05:15 PST
All reviewed patches have been landed.  Closing bug.
Comment 35 WebKit Commit Bot 2015-03-02 19:19:36 PST
Re-opened since this is blocked by bug 142204
Comment 36 Mark Rowe (bdash) 2015-03-02 19:28:07 PST
class MicroTask : public RefCounted<MicroTask> {
…
   WEBCORE_EXPORT void queueMicroTask(std::unique_ptr<MicroTask>);

Having unique_ptrs to a RefCounted object seems incorrect. It's likely the reason for the assertion failures that led to me rolling this out.
Comment 37 Yoav Weiss 2015-03-03 00:15:28 PST
Created attachment 247752 [details]
Patch
Comment 38 Yoav Weiss 2015-03-03 07:07:24 PST
I've fixed it, ran the tests locally with a debug build and now they're running fine.
Comment 39 Yoav Weiss 2015-03-03 16:34:27 PST
Created attachment 247812 [details]
Patch
Comment 40 Yoav Weiss 2015-03-03 16:41:23 PST
Sam, can you please re-review?
Comment 41 Sam Weinig 2015-03-03 21:09:06 PST
Comment on attachment 247812 [details]
Patch

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

> Source/WebCore/dom/MicroTask.cpp:26
> +#include <wtf/text/StringBuilder.h>

StringBuilder.h is unused in this file.  You may however need <wtf/NeverDestroyed.h>
Comment 42 Yoav Weiss 2015-03-03 23:04:53 PST
Created attachment 247843 [details]
Patch
Comment 43 Yoav Weiss 2015-03-03 23:09:23 PST
> StringBuilder.h is unused in this file.  You may however need <wtf/NeverDestroyed.h>
[reply] [-] Comment 42

Removed StringBuilder. NeverDestroyed is included in the h file, which we include here.
Comment 44 WebKit Commit Bot 2015-03-03 23:55:52 PST
Comment on attachment 247843 [details]
Patch

Clearing flags on attachment: 247843

Committed r180996: <http://trac.webkit.org/changeset/180996>
Comment 45 WebKit Commit Bot 2015-03-03 23:56:02 PST
All reviewed patches have been landed.  Closing bug.