Bug 147933

Summary: Promise callbacks should be called at microtask checkpoints
Product: WebKit Reporter: Jake Archibald <jaffathecake>
Component: JavaScriptCoreAssignee: Sam Weinig <sam>
Status: RESOLVED FIXED    
Severity: Normal CC: ap, buildbot, calvaris, cdumez, dbates, jonlee, nekr.fabula, rniwa, ryanhaddad, sam, webkit-bug-importer, yoav, ysuzuki
Priority: P2 Keywords: InRadar
Version: 528+ (Nightly build)   
Hardware: Unspecified   
OS: Unspecified   
Bug Depends on:    
Bug Blocks: 140043, 149032    
Attachments:
Description Flags
Patch
none
WIP
none
Archive of layout-test-results from ews100 for mac-mavericks
none
Archive of layout-test-results from ews104 for mac-mavericks-wk2
none
WIP
none
Archive of layout-test-results from ews103 for mac-mavericks
none
Archive of layout-test-results from ews104 for mac-mavericks-wk2
none
Patch
none
Patch cdumez: review+, cdumez: commit-queue-

Jake Archibald
Reported 2015-08-12 04:49:47 PDT
Here is a demo of the issue: http://jsbin.com/kaqume/1/edit?js,console,output Click the blue square. The `promise` logs should appear before the `mutate` logs. The correct ordering is: "click" "promise" "mutate" "click" "promise" "mutate" "timeout" "timeout" The microtask queue should be emptied once a listener callback has finished, before returning to spec prose. Promise callbacks seem to run on a different queue in WebKit.
Attachments
Patch (68.66 KB, patch)
2015-08-28 08:59 PDT, Sam Weinig
no flags
WIP (68.86 KB, patch)
2015-08-30 03:47 PDT, Sam Weinig
no flags
Archive of layout-test-results from ews100 for mac-mavericks (689.66 KB, application/zip)
2015-08-30 04:31 PDT, Build Bot
no flags
Archive of layout-test-results from ews104 for mac-mavericks-wk2 (690.60 KB, application/zip)
2015-08-30 04:36 PDT, Build Bot
no flags
WIP (69.39 KB, patch)
2015-08-30 11:18 PDT, Sam Weinig
no flags
Archive of layout-test-results from ews103 for mac-mavericks (549.62 KB, application/zip)
2015-08-30 12:01 PDT, Build Bot
no flags
Archive of layout-test-results from ews104 for mac-mavericks-wk2 (714.68 KB, application/zip)
2015-08-30 12:07 PDT, Build Bot
no flags
Patch (68.84 KB, patch)
2015-10-10 15:36 PDT, Sam Weinig
no flags
Patch (69.18 KB, patch)
2015-10-11 10:16 PDT, Sam Weinig
cdumez: review+
cdumez: commit-queue-
Yusuke Suzuki
Comment 1 2015-08-12 17:26:27 PDT
JSC Promises now just uses postTask under the WebCore environment. It should use micro task. Now quickly see the code base, MicroTask code in WebCore is found, but it seems that nobody except for the test uses it yet. Hm.
Yusuke Suzuki
Comment 2 2015-08-12 18:07:01 PDT
Currently, MutationObserver does not use MicroTask. So to fix this, 1. When enqueuing MutationRecord at first (in this loop), we need to enqueue the micro task that later drains the mutation records[1]. 2. Promises executes the asynchronous job by enqueueing the micro task. [1]: https://dom.spec.whatwg.org/#queue-a-mutation-observer-compound-microtask
Sam Weinig
Comment 3 2015-08-28 08:59:21 PDT
Sam Weinig
Comment 4 2015-08-28 09:08:11 PDT
The attached patch fixes the issue and lays some infrastructure for future improvements in our event loop model. There is one thing I am not clear on that I need to investigate further, which is are we performing the Microtask checkpoints at the right times. Previous to this patch, we had two sets of callers who seemed to be trying to implement doing work on microtask checkpoints: MutationObservers and MicroTaskQueue. They had some overlap, and some disagreement. I decided that since our MutationObserver code has been around longer is actually used (the old microtask code is seemingly only used for testing so far), I would stick with it, but I think a more careful audit of the HTML5 spec is needed to fully understand when those checkpoints are.
Sam Weinig
Comment 5 2015-08-28 09:09:54 PDT
Oh, also, I changed the name of the files to Microtasks.h/cpp so that I could fix the capitalization issue in the file name without ruining case-insensitive filesystem users day. I will change it to Microtask.h/cpp in a later patch.
Sam Weinig
Comment 6 2015-08-30 03:47:40 PDT
Build Bot
Comment 7 2015-08-30 04:31:55 PDT
Comment on attachment 260239 [details] WIP Attachment 260239 [details] did not pass mac-ews (mac): Output: http://webkit-queues.webkit.org/results/122669 Number of test failures exceeded the failure limit.
Build Bot
Comment 8 2015-08-30 04:31:58 PDT
Created attachment 260240 [details] Archive of layout-test-results from ews100 for mac-mavericks The attached test failures were seen while running run-webkit-tests on the mac-ews. Bot: ews100 Port: mac-mavericks Platform: Mac OS X 10.9.5
Build Bot
Comment 9 2015-08-30 04:36:49 PDT
Comment on attachment 260239 [details] WIP Attachment 260239 [details] did not pass mac-wk2-ews (mac-wk2): Output: http://webkit-queues.webkit.org/results/122673 Number of test failures exceeded the failure limit.
Build Bot
Comment 10 2015-08-30 04:36:53 PDT
Created attachment 260241 [details] Archive of layout-test-results from ews104 for mac-mavericks-wk2 The attached test failures were seen while running run-webkit-tests on the mac-wk2-ews. Bot: ews104 Port: mac-mavericks-wk2 Platform: Mac OS X 10.9.5
Sam Weinig
Comment 11 2015-08-30 11:18:37 PDT
Build Bot
Comment 12 2015-08-30 12:01:10 PDT
Comment on attachment 260244 [details] WIP Attachment 260244 [details] did not pass mac-ews (mac): Output: http://webkit-queues.webkit.org/results/123573 New failing tests: streams/reference-implementation/readable-stream.html
Build Bot
Comment 13 2015-08-30 12:01:13 PDT
Created attachment 260247 [details] Archive of layout-test-results from ews103 for mac-mavericks The attached test failures were seen while running run-webkit-tests on the mac-ews. Bot: ews103 Port: mac-mavericks Platform: Mac OS X 10.9.5
Build Bot
Comment 14 2015-08-30 12:06:59 PDT
Comment on attachment 260244 [details] WIP Attachment 260244 [details] did not pass mac-wk2-ews (mac-wk2): Output: http://webkit-queues.webkit.org/results/123576 New failing tests: streams/reference-implementation/readable-stream.html
Build Bot
Comment 15 2015-08-30 12:07:02 PDT
Created attachment 260249 [details] Archive of layout-test-results from ews104 for mac-mavericks-wk2 The attached test failures were seen while running run-webkit-tests on the mac-wk2-ews. Bot: ews104 Port: mac-mavericks-wk2 Platform: Mac OS X 10.9.5
Yoav Weiss
Comment 16 2015-08-31 02:50:45 PDT
Comment on attachment 260244 [details] WIP View in context: https://bugs.webkit.org/attachment.cgi?id=260244&action=review > Source/WebCore/dom/Microtasks.cpp:101 > + for (auto& task : m_tasksAppendedDuringMicrotaskCheckpoint) I believe there can be a race here where microtasks are dropped to the floor, if added after all previous ones were appended. (even if Vector is thread-safe, which I'm not sure it is)
Yoav Weiss
Comment 17 2015-08-31 02:59:04 PDT
(In reply to comment #4) > The attached patch fixes the issue and lays some infrastructure for future > improvements in our event loop model. > There is one thing I am not clear on > that I need to investigate further, which is are we performing the Microtask > checkpoints at the right times. Previous to this patch, we had two sets of > callers who seemed to be trying to implement doing work on microtask > checkpoints: MutationObservers and MicroTaskQueue. They had some overlap, > and some disagreement. I decided that since our MutationObserver code has > been around longer is actually used (the old microtask code is seemingly > only used for testing so far), I would stick with it, but I think a more > careful audit of the HTML5 spec is needed to fully understand when those > checkpoints are. The question of where to run microtask checkpoints in indeed a question needs answering. While working on https://bugs.webkit.org/show_bug.cgi?id=134488 (which relied on the Microtasks abstraction), there were multiple cases where microtasks ended up never running (e.g. when queued from a script's timer after onload, when queued from TestRunner). What I wanted to try next (and never got to) was to perform a microtask checkpoint from the runloop thread itself (via CFRunLoopObserver/WebCore:RunLoopObserver or otherwise).
Sam Weinig
Comment 18 2015-08-31 11:47:53 PDT
(In reply to comment #16) > Comment on attachment 260244 [details] > WIP > > View in context: > https://bugs.webkit.org/attachment.cgi?id=260244&action=review > > > Source/WebCore/dom/Microtasks.cpp:101 > > + for (auto& task : m_tasksAppendedDuringMicrotaskCheckpoint) > > I believe there can be a race here where microtasks are dropped to the > floor, if added after all previous ones were appended. (even if Vector is > thread-safe, which I'm not sure it is) The entire MicrotaskQueue class is not thread-safe (in the sense that each queue can only be used from a single thread at a time). Given that, do you still think there is a race?
Yoav Weiss
Comment 19 2015-08-31 12:44:54 PDT
Comment on attachment 260244 [details] WIP View in context: https://bugs.webkit.org/attachment.cgi?id=260244&action=review > Source/WebCore/dom/Microtasks.cpp:-39 > - m_microTaskQueue.append(WTF::move(task)); Why did you remove the `ASSERT(isMainThread());`? >>> Source/WebCore/dom/Microtasks.cpp:101 >>> + for (auto& task : m_tasksAppendedDuringMicrotaskCheckpoint) >> >> I believe there can be a race here where microtasks are dropped to the floor, if added after all previous ones were appended. (even if Vector is thread-safe, which I'm not sure it is) > > The entire MicrotaskQueue class is not thread-safe (in the sense that each queue can only be used from a single thread at a time). Given that, do you still think there is a race? You're right. I got myself confused. In that light, no race here AFAICT.
Sam Weinig
Comment 20 2015-08-31 12:54:31 PDT
(In reply to comment #19) > Comment on attachment 260244 [details] > WIP > > View in context: > https://bugs.webkit.org/attachment.cgi?id=260244&action=review > > > Source/WebCore/dom/Microtasks.cpp:-39 > > - m_microTaskQueue.append(WTF::move(task)); > > Why did you remove the `ASSERT(isMainThread());`? In a future patch, MicrotaskQueue will also be used on Worker threads (though not using the shared main thread queue), so this does not need to be only called on the main thread.
Yoav Weiss
Comment 21 2015-08-31 13:57:45 PDT
(In reply to comment #20) > (In reply to comment #19) > > Comment on attachment 260244 [details] > > WIP > > > > View in context: > > https://bugs.webkit.org/attachment.cgi?id=260244&action=review > > > > > Source/WebCore/dom/Microtasks.cpp:-39 > > > - m_microTaskQueue.append(WTF::move(task)); > > > > Why did you remove the `ASSERT(isMainThread());`? > > In a future patch, MicrotaskQueue will also be used on Worker threads > (though not using the shared main thread queue), so this does not need to > be only called on the main thread. OK, makes sense
Sam Weinig
Comment 22 2015-09-09 15:52:41 PDT
*** Bug 149003 has been marked as a duplicate of this bug. ***
Sam Weinig
Comment 23 2015-10-10 15:36:49 PDT
Sam Weinig
Comment 24 2015-10-11 10:16:51 PDT
Chris Dumez
Comment 25 2015-10-15 11:17:25 PDT
Comment on attachment 262865 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=262865&action=review r=me with comments. > Source/WebCore/bindings/js/JSDOMWindowBase.cpp:217 > + static Ref<JSDOMWindowMicrotaskCallback> create(JSDOMWindowBase* globalObject, PassRefPtr<JSC::Microtask> task) PassRefPtr? :) We use Ref<>&& or RefPtr<>&& nowadays. Also, I am suspecting you don't need the JSC:: given that you already don't use it for JSC::Strong. > Source/WebCore/bindings/js/JSDOMWindowBase.cpp:229 > + JSMainThreadExecState::runTask(exec, *m_task.get()); *m_task .get() is redundant. Also, if m_task cannot be null, we could use a Ref<JSC::Microtask> for m_task data member and merely pass m_task here. > Source/WebCore/bindings/js/JSDOMWindowBase.cpp:235 > + JSDOMWindowMicrotaskCallback(JSDOMWindowBase* globalObject, PassRefPtr<JSC::Microtask> task) Ditto about PassRefPtr. > Source/WebCore/bindings/js/JSDOMWindowBase.cpp:245 > +void JSDOMWindowBase::queueTaskToEventLoop(const JSGlobalObject* object, PassRefPtr<JSC::Microtask> task) Let's not use PassRefPtr. > Source/WebCore/bindings/js/JSDOMWindowBase.cpp:249 > + RefPtr<JSDOMWindowMicrotaskCallback> callback = JSDOMWindowMicrotaskCallback::create((JSDOMWindowBase*)thisObject, task); Looks like this should be a Ref<>. Also, it would be nicer to use a static_cast<>(). > Source/WebCore/dom/ActiveDOMCallbackMicrotask.h:35 > +class ActiveDOMCallbackMicrotask : public Microtask, public ActiveDOMCallback { Could be final? > Source/WebCore/dom/ActiveDOMCallbackMicrotask.h:46 > + // be accessed via the ScriptExecutionContext, which should hold a reference to the relevent typo in relevant? > Source/WebCore/dom/Microtasks.cpp:46 > + task->run(); Please make sure m_microTaskQueue cannot be modified by the calls to task->run() (I haven't checked). > Source/WebCore/dom/Microtasks.cpp:63 > + for (size_t i = 0; i < m_microtaskQueue.size(); ++i) { m_microtaskQueue.removeFirstMatching([&task](const std::unique_ptr<MicroTask>& item) { return item.get() == &task; }); > Source/WebCore/dom/Microtasks.cpp:69 > + for (size_t i = 0; i < m_tasksAppendedDuringMicrotaskCheckpoint.size(); ++i) { Could use Vector::removeFirstMatching(). > Source/WebCore/dom/Microtasks.h:3 > + * Copyright (C) 2015 Akamai Technologies Inc. All rights reserved. Diff problem? This looks like an old file (not a new one). Still using the odd camel-case naming. > Source/WebCore/dom/Microtasks.h:63 > + WEBCORE_EXPORT void remove(const Microtask&); Why does it need to be exported if it is private? > Source/WebCore/dom/Microtasks.h:67 > + bool m_performingMicrotaskCheckpoint = false; Don't we usually use the { false } variant? > Source/WebCore/dom/MutationObserver.cpp:166 > + virtual ~MutationObserverMicrotask() unnecessary I believe. > Source/WebCore/dom/MutationObserver.cpp:171 > + virtual Result run() override? > Tools/TestWebKitAPI/Tests/WebCore/Microtask.cpp:148 > + auto* keepableRef = keepable.get(); Ref -> Ptr ? > Tools/TestWebKitAPI/Tests/WebCore/Microtask.cpp:163 > + auto* keepableRef = keepable.get(); Ref -> Ptr? > LayoutTests/fast/dom/microtask-promise-mutation-observer-order.html:70 > + for (task of tasks) { extra curly brackets?
Yoav Weiss
Comment 26 2015-10-19 01:37:06 PDT
Comment on attachment 262865 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=262865&action=review > Source/WebCore/bindings/js/JSMainThreadExecState.cpp:48 > + MicrotaskQueue::mainThreadQueue().performMicrotaskCheckpoint(); Will this trigger microtask checkpoints also after running timer based functions?
Xabier Rodríguez Calvar
Comment 27 2015-11-04 01:36:42 PST
*** Bug 150867 has been marked as a duplicate of this bug. ***
Xabier Rodríguez Calvar
Comment 28 2015-11-09 01:58:16 PST
*** Bug 150976 has been marked as a duplicate of this bug. ***
Radar WebKit Bug Importer
Comment 29 2015-12-02 14:28:51 PST
Sam Weinig
Comment 30 2015-12-03 11:18:41 PST
Xabier Rodríguez Calvar
Comment 31 2015-12-07 11:21:54 PST
Alexey Proskuryakov
Comment 32 2016-08-29 13:54:40 PDT
Looks like this made WebKit behave differently from Firefox and Chrome in at least once case, see bug 161291.
Note You need to log in before you can comment on or make changes to this bug.