Closed
Bug 915233
Opened 11 years ago
Closed 11 years ago
Port DOM Promises to Workers
Categories
(Core :: DOM: Workers, defect)
Tracking
()
RESOLVED
FIXED
mozilla28
People
(Reporter: nsm, Assigned: nsm)
References
Details
(Keywords: dev-doc-complete, feature, Whiteboard: [qa-])
Attachments
(1 file, 9 obsolete files)
39.07 KB,
patch
|
bent.mozilla
:
review+
|
Details | Diff | Splinter Review |
Even though Promise is in flux right now, I had this patch ready and just want to put it up so nobody has to duplicate effort.
Attachment #803094 -
Attachment is patch: true
Promises interact with the event loop, so this patch depends on a solution for bug 914762.
Depends on: 914762
Assignee | ||
Comment 2•11 years ago
|
||
Two things I'm concerned about:
1) Using the Maybe<AutoCxPusher> only on the main thread. Will the worker maintain the right context with the code I've written?
2) AsyncErrorReporter currently dispatches to current thread. I don't know where I'm supposed to see this error, don't see it in the JS console. Should it dispatch to the main thread?
Attachment #813336 -
Flags: review?(khuey)
Attachment #813336 -
Flags: review?(amarchesini)
Assignee | ||
Updated•11 years ago
|
Assignee: nobody → nsm.nikhil
Assignee | ||
Updated•11 years ago
|
Blocks: ServiceWorkers
Assignee | ||
Updated•11 years ago
|
Attachment #803094 -
Attachment is obsolete: true
Comment 3•11 years ago
|
||
Comment on attachment 813336 [details] [diff] [review]
DOM Promises on Workers.
Review of attachment 813336 [details] [diff] [review]:
-----------------------------------------------------------------
LGTM.
But before checked it in, can you ask bz or smaug about this CX question?
::: dom/promise/Promise.cpp
@@ +135,5 @@
>
> +class WorkerPromiseResolverTask MOZ_FINAL : public WorkerRunnable
> +{
> +public:
> + WorkerPromiseResolverTask(WorkerPrivate* aWorkerPrivate, Promise* aPromise, JS::Handle<JS::Value> aValue, Promise::PromiseState aState)
indent
@@ +533,5 @@
>
> nsCOMPtr<nsPIDOMWindow> win =
> do_QueryInterface(nsJSUtils::GetStaticScriptGlobal(&mResult.toObject()));
>
> + // FIXME(nsm): Send to main thread?
follow up?
@@ +626,5 @@
> + } else {
> + JSContext* cx = nsContentUtils::GetDefaultJSContextForThread();
> + WorkerPrivate* worker = GetWorkerPrivateFromContext(cx);
> + MOZ_ASSERT(worker);
> + nsRefPtr<WorkerPromiseResolverTask> task = new WorkerPromiseResolverTask(worker, this, aValue, aState);
indent.
::: dom/promise/Promise.h
@@ +32,2 @@
> friend class PromiseResolverTask;
> + friend class WorkerPromiseResolverTask;
sort
::: dom/promise/PromiseCallback.cpp
@@ +159,5 @@
> {
> + // AutoCxPusher and co. interact with xpconnect, which crashes on
> + // workers. On workers we'll get the right context from
> + // GetDefaultJSContextForThread(), and since there is only one context, we
> + // don't need to push or pop it from the stack. Is that correct?
This is a good question. Can you ping smaug or bz?
Attachment #813336 -
Flags: review?(amarchesini) → review+
Assignee | ||
Comment 4•11 years ago
|
||
bz, smaug
Could you take a look at the two comments:
1) AutoCxPusher initialization only in the main thread in dom/promise/PromiseCallback.cpp
2) Necessity of rooting mValue in WorkerPromiseResolverTask.
and tell me if I'm doing the right thing.
Attachment #817442 -
Flags: review?(bzbarsky)
Attachment #817442 -
Flags: review?(bugs)
Assignee | ||
Updated•11 years ago
|
Attachment #813336 -
Attachment is obsolete: true
Attachment #813336 -
Flags: review?(khuey)
Assignee | ||
Comment 5•11 years ago
|
||
Comment on attachment 817442 [details] [diff] [review]
DOM Promises on Workers.
Andrea, I changed MaybeReportRejected() to dispatch to main thread, but as I mentioned on IRC, normal JS exceptions thrown in a resolve/reject callback don't seem to have an errorReport. Do you know how to hit the last line? I can't see a test case for it.
Attachment #817442 -
Flags: review?(bzbarsky)
Assignee | ||
Updated•11 years ago
|
Flags: needinfo?(amarchesini)
Comment 6•11 years ago
|
||
(In reply to Nikhil Marathe [:nsm] from comment #4)
> Could you take a look at the two comments:
> 1) AutoCxPusher initialization only in the main thread in
> dom/promise/PromiseCallback.cpp
No cx stack on workers, and we do similar thing in CallbackObject.
(I wonder why Promises can't use normal CallbackObjects)
> 2) Necessity of rooting mValue in WorkerPromiseResolverTask.
> and tell me if I'm doing the right thing.
Yes, you need to root the value. Nothing would otherwise guarantee that the value stays alive.
Comment 7•11 years ago
|
||
Comment on attachment 817442 [details] [diff] [review]
DOM Promises on Workers.
>+'Promise': {
>+ 'wantsQI': False
>+},
Based on https://bugzilla.mozilla.org/show_bug.cgi?id=920196 this shouldn't be needed.
>@@ -176,17 +263,17 @@ Promise::EnabledForScope(JSContext* aCx,
> // certified app.
> if (PrefEnabled()) {
> return true;
> }
Why is reading the pref from non-main-thread ok?
>+ // Should work according to khuey since there is only one global object.
drop the this
>+ } else {
>+ JSContext* cx = nsContentUtils::GetDefaultJSContextForThread();
>+ WorkerPrivate* worker = GetWorkerPrivateFromContext(cx);
Hmm, we don't have a helper method for this? We really need the cx?
> // Now post an event to do the real reporting async
>- NS_DispatchToCurrentThread(
>+ NS_DispatchToMainThread(
> new AsyncErrorReporter(JS_GetObjectRuntime(&mResult.toObject()),
> report,
> nullptr,
> nsContentUtils::GetObjectPrincipal(&mResult.toObject()),
> win));
Does this really work? You get some sane principal even on workers?
>+ JSContext *cx = nsContentUtils::GetDefaultJSContextForThread();
* should go with the type
r- because I don't think the pref check is thread safe.
Attachment #817442 -
Flags: review?(bugs) → review-
Assignee | ||
Comment 8•11 years ago
|
||
Olli,
So I moved the pref check to the main thread using the super convenient SyncRunnable class.
It works until we have a story around doing preferences properly (although this might be the proper story).
As for the principals check. The nsContentUtils call isn't doing anything thread-unsafe or something that asserts on non main thread. mResult is also owned by the worker thread, so the compartment and thing will work out. I don't see the problem their. Of course, still need baku to tell me how to trigger this code path.
Attachment #818165 -
Flags: review?(bugs)
Assignee | ||
Updated•11 years ago
|
Attachment #817442 -
Attachment is obsolete: true
Comment 9•11 years ago
|
||
Comment on attachment 818165 [details] [diff] [review]
DOM Promises on Workers.
>+ // FIXME(nsm): Is this required in workers and is the comment still valid?
You need to root JS stuff always if you don't otherwise explicitly keep it alive.
> Promise::EnabledForScope(JSContext* aCx, JSObject* /* unused */)
> {
> // Enable if the pref is enabled or if we're chrome or if we're a
> // certified app.
>- if (PrefEnabled()) {
>+ nsCOMPtr<nsIThread> mainThread;
>+ nsresult rv = NS_GetMainThread(getter_AddRefs(mainThread));
>+ MOZ_ASSERT(NS_SUCCEEDED(rv));
>+
>+ nsRefPtr<PrefEnabledRunnable> r = new PrefEnabledRunnable();
>+ // Force dispatch (third arg) is not passed, which means it will behave like
>+ // a normal function call if running on the main thread.
>+ SyncRunnable::DispatchToThread(mainThread, r);
>+ if (r->Enabled()) {
> return true;
> }
So we want to do this runnable stuff only when we're not in the main thread.
And I think it should be ok to do it once per worker, since this can cause rather bad
pause in worker execution.
> Promise::RunResolveTask(JS::Handle<JS::Value> aValue,
> PromiseState aState,
> PromiseTaskSync aAsynchronous)
> {
> // If the synchronous flag is unset, queue a task to process our
> // accept callbacks with value.
> if (aAsynchronous == AsyncTask) {
>- nsRefPtr<PromiseResolverTask> task =
>- new PromiseResolverTask(this, aValue, aState);
>- NS_DispatchToCurrentThread(task);
>+ if (MOZ_LIKELY(NS_IsMainThread())) {
>+ nsRefPtr<PromiseResolverTask> task =
>+ new PromiseResolverTask(this, aValue, aState);
>+ NS_DispatchToCurrentThread(task);
>+ } else {
>+ JSContext* cx = nsContentUtils::GetDefaultJSContextForThread();
>+ WorkerPrivate* worker = GetWorkerPrivateFromContext(cx);
>+ MOZ_ASSERT(worker);
>+ nsRefPtr<WorkerPromiseResolverTask> task =
>+ new WorkerPromiseResolverTask(worker, this, aValue, aState);
>+ worker->Dispatch(task);
>+ }
Would it be possible to integrate the dispatching code to the *Task class so that the same class could be used in workers and main thread.
We don't want to have very similar PromiseResolverTask and WorkerPromiseResolverTask classes.
Attachment #818165 -
Flags: review?(bugs) → review-
Assignee | ||
Comment 10•11 years ago
|
||
I can't combine the two classes because WorkerRunnable requires a non-null worker in the constructor, which I cannot get on the main thread.
Also it seems a little dangerous to override nsIRunnable::Run() in a subclass when WorkerRunnable uses it for some important processing in the workers case, even though I could guarantee that it is always called on the worker thread. I've compromised by introducing PromiseResolverMixin which at least combines the logic and adds owning thread assertions.
Attachment #824310 -
Flags: review?(bugs)
Assignee | ||
Updated•11 years ago
|
Attachment #818165 -
Attachment is obsolete: true
Assignee | ||
Comment 11•11 years ago
|
||
Comment on attachment 824310 [details] [diff] [review]
DOM Promises on Workers.
Got that AsyncErrorReporter code path to trigger, crashes on worker, I'll have a new patch up soon.
Attachment #824310 -
Flags: review?(bugs)
Assignee | ||
Updated•11 years ago
|
Flags: needinfo?(amarchesini)
Assignee | ||
Comment 12•11 years ago
|
||
bz for MaybeReportRejected()
smaug for the mixin
khuey for GetCurrentThreadWorkerPrivate()
Attachment #824797 -
Flags: review?(bzbarsky)
Attachment #824797 -
Flags: review?(bugs)
Assignee | ||
Updated•11 years ago
|
Attachment #824310 -
Attachment is obsolete: true
Assignee | ||
Updated•11 years ago
|
Attachment #824797 -
Flags: review?(khuey)
Comment 13•11 years ago
|
||
Comment on attachment 824797 [details] [diff] [review]
DOM Promises on Workers.
Ok. The runnable handling will hopefully get better
once event loop in workers has been fixed.
>- nsCOMPtr<nsPIDOMWindow> win =
>- do_QueryInterface(nsJSUtils::GetStaticScriptGlobal(&mResult.toObject()));
>+ // Remains null in case of worker.
>+ nsCOMPtr<nsPIDOMWindow> win;
>+ // Remains null in case of a content worker.
>+ nsIPrincipal* principal = nullptr;
>+
>+ if (MOZ_LIKELY(NS_IsMainThread())) {
>+ win =
>+ do_QueryInterface(nsJSUtils::GetStaticScriptGlobal(&mResult.toObject()));
>+ principal = nsContentUtils::GetObjectPrincipal(&mResult.toObject());
>+ } else {
>+ WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
>+ MOZ_ASSERT(worker);
>+ // AsyncErrorReporter only cares whether principal is system principal
>+ // or not.
>+ // We have to do this workaround because worker->GetPrincipal() is main
>+ // thread only.
>+ if (worker->IsChromeWorker()) {
>+ principal = nsContentUtils::GetSystemPrincipal();
>+ }
>+ }
>
> // Now post an event to do the real reporting async
>- NS_DispatchToCurrentThread(
>+ NS_DispatchToMainThread(
> new AsyncErrorReporter(JS_GetObjectRuntime(&mResult.toObject()),
> report,
> nullptr,
>- nsContentUtils::GetObjectPrincipal(&mResult.toObject()),
>+ principal,
> win));
Principal handling is a bit scary, but should be ok, even in workers.
I think better would be to have different ctor for AsyncErrorReporter which
doesn't take nsIPrincipal but just bool to tell chrome or content.
But using principal should be fine for now.
>- AutoJSContext cx;
>+ // AutoCxPusher and co. interact with xpconnect, which crashes on
>+ // workers. On workers we'll get the right context from
>+ // GetDefaultJSContextForThread(), and since there is only one context, we
>+ // don't need to push or pop it from the stack. Is that correct?
>+ JSContext *cx = nsContentUtils::GetDefaultJSContextForThread();
* goes with the type.
Attachment #824797 -
Flags: review?(bugs) → review+
Comment 14•11 years ago
|
||
Comment on attachment 824797 [details] [diff] [review]
DOM Promises on Workers.
Using nsContentUtils::GetSystemPrincipal not on the main thread gives me the willies.
If all AsyncErrorReporter cares about is system vs not, should we change the argument it takes to a boolean instead?
Also, have you verified that nsIScriptError is safe to use off-main-thread? I assume nsIConsoleService is....
Assignee | ||
Comment 15•11 years ago
|
||
(In reply to Boris Zbarsky [:bz] from comment #14)
> Comment on attachment 824797 [details] [diff] [review]
> DOM Promises on Workers.
>
> Using nsContentUtils::GetSystemPrincipal not on the main thread gives me the
> willies.
>
> If all AsyncErrorReporter cares about is system vs not, should we change the
> argument it takes to a boolean instead?
This can be fixed to take a bool, it only uses the check to decide the category under which to show the error.
>
> Also, have you verified that nsIScriptError is safe to use off-main-thread?
> I assume nsIConsoleService is....
I am not using any of this off main thread. it's dispatched to the main thread.
Comment 16•11 years ago
|
||
> I am not using any of this off main thread. it's dispatched to the main thread.
Ah, perfect; I somehow missed the change there. ;)
Assignee | ||
Comment 17•11 years ago
|
||
AsyncErrorReporter boolean arg.
Attachment #824937 -
Flags: review?(khuey)
Attachment #824937 -
Flags: review?(bzbarsky)
Assignee | ||
Updated•11 years ago
|
Attachment #824797 -
Attachment is obsolete: true
Attachment #824797 -
Flags: review?(khuey)
Attachment #824797 -
Flags: review?(bzbarsky)
Comment 18•11 years ago
|
||
Comment on attachment 824937 [details] [diff] [review]
DOM Promises on Workers.
r=me on the MaybeReportRejected bits.
Attachment #824937 -
Flags: review?(bzbarsky) → review+
Assignee | ||
Comment 19•11 years ago
|
||
Dropping r? khuey because he added GetWorkerPrivateForCurrentThread() in another patch.
Assignee | ||
Updated•11 years ago
|
Attachment #824937 -
Attachment is obsolete: true
Attachment #824937 -
Flags: review?(khuey)
Assignee | ||
Comment 20•11 years ago
|
||
Comment on attachment 827671 [details] [diff] [review]
DOM Promises on Workers.
Promise on workers is hidden behind dom.promise.workers.enabled and checked in RegisterBindings.cpp.
Attachment #827671 -
Flags: superreview?(jonas)
You can't check the pref off the main thread.
Comment on attachment 827671 [details] [diff] [review]
DOM Promises on Workers.
Review of attachment 827671 [details] [diff] [review]:
-----------------------------------------------------------------
sr=me with that fixed.
::: dom/promise/Promise.cpp
@@ +261,5 @@
> + const char* pref;
> + if (mOnWorker) {
> + pref = "dom.promise.workers.enabled";
> + } else {
> + pref = "dom.promise.enabled";
I don't see a reason to use separate prefs. Just use the same one.
::: dom/workers/test/promise_worker.js
@@ +1,1 @@
> +function ok(a, msg) {
Is there no way we can use the same js file to test both main thread and workers? That should make it eaiser to add more tests.
Attachment #827671 -
Flags: superreview?(jonas) → superreview+
Assignee | ||
Comment 23•11 years ago
|
||
Backed out in http://hg.mozilla.org/integration/mozilla-inbound/rev/a00afeffd0ac for breaking the build: https://tbpl.mozilla.org/php/getParsedLog.php?id=30176750&tree=Mozilla-Inbound
Assignee | ||
Comment 25•11 years ago
|
||
Let's try again:
remote: https://hg.mozilla.org/integration/mozilla-inbound/rev/b2316c5a1d69
Comment 26•11 years ago
|
||
Backed out in https://hg.mozilla.org/integration/mozilla-inbound/rev/f86c5c6d100d for Windows b2g desktop bustage
Not just b2g, but all of Windows. https://hg.mozilla.org/integration/mozilla-inbound/annotate/b2316c5a1d69/dom/promise/Promise.cpp#l257 has the wrong calling convention on Windows ... you need to use the NS_IMETHOD macro.
Assignee | ||
Comment 28•11 years ago
|
||
third time sticky?
remote: https://hg.mozilla.org/integration/mozilla-inbound/rev/8222e9ae0a21
Comment 29•11 years ago
|
||
Backed out under suspicion of making Windows debug xpcshell perma-fail a la bug 845190. Good luck.
https://hg.mozilla.org/integration/mozilla-inbound/rev/95fa48bbbc0a
https://tbpl.mozilla.org/php/getParsedLog.php?id=30232602&tree=Mozilla-Inbound
Assignee | ||
Comment 30•11 years ago
|
||
The problem was SyncRunnable. The main thread may enter shutdown while the worker thread is blocked on SyncRunnable, after which the worker doesn't quit.
Assignee | ||
Updated•11 years ago
|
Attachment #827671 -
Attachment is obsolete: true
Assignee | ||
Comment 31•11 years ago
|
||
Comment on attachment 829580 [details] [diff] [review]
DOM Promises on Workers.
Switched to reading and storing pref in RuntimeService.
bent, review for RuntimeService.{h,cpp} and RegisterBindings.cpp, Promise::EnabledForScope() please.
Attachment #829580 -
Flags: review?(bent.mozilla)
Assignee | ||
Comment 32•11 years ago
|
||
Slight fix to ensure that chromeworker and system principal checks run on main thread pref check.
Attachment #8333986 -
Flags: review?(bent.mozilla)
Assignee | ||
Updated•11 years ago
|
Attachment #829580 -
Attachment is obsolete: true
Attachment #829580 -
Flags: review?(bent.mozilla)
Comment on attachment 8333986 [details] [diff] [review]
DOM Promises on Workers.
Review of attachment 8333986 [details] [diff] [review]:
-----------------------------------------------------------------
::: dom/workers/RuntimeService.cpp
@@ +200,5 @@
> +// Protected by RuntimeService::mMutex.
> +bool gPromiseEnabled;
> +
> +static int
> +PromiseEnableChanged(const char* aPrefName, void* aClosure)
You're going to hate me, but I think we should do this totally differently. When it was just Dump() I was ok with the mutex, but if we're going to start doing pref checks at random times for production-quality code we don't want all workers to block on each other.
So let's add a per-worker pref cache for the prefs we know we need. For now they're only booleans so that should be pretty tiny. We can add it to WorkerPrivate, then update them using the same basic mechanism as the JSSettings.
I'm happy to chat about this, sorry to make you do more work here :(
Attachment #8333986 -
Flags: review?(bent.mozilla) → review-
Comment on attachment 8333986 [details] [diff] [review]
DOM Promises on Workers.
Review of attachment 8333986 [details] [diff] [review]:
-----------------------------------------------------------------
Ok, this patch plus the one in bug 936700 should be fine.
::: dom/workers/moz.build
@@ +9,5 @@
> MODULE = 'dom'
>
> # Public stuff.
> EXPORTS.mozilla.dom += [
> + 'RuntimeService.h',
This is not needed any more I think
Attachment #8333986 -
Flags: review- → review+
Assignee | ||
Comment 35•11 years ago
|
||
Comment 36•11 years ago
|
||
Status: NEW → RESOLVED
Closed: 11 years ago
Resolution: --- → FIXED
Target Milestone: --- → mozilla28
Updated•11 years ago
|
Keywords: dev-doc-needed
Comment 37•11 years ago
|
||
Marking in-testsuite+ since this is covered automatically by
dom/workers/test/promise_worker.js
dom/workers/test/test_promise.html
Is there any need for manual QA for this feature?
Flags: needinfo?(nsm.nikhil)
Flags: in-testsuite+
Assignee | ||
Comment 38•11 years ago
|
||
(In reply to Ioana Budnar, QA [:ioana] from comment #37)
> Marking in-testsuite+ since this is covered automatically by
> dom/workers/test/promise_worker.js
> dom/workers/test/test_promise.html
>
> Is there any need for manual QA for this feature?
The automated tests should be enough, since they match the main thread promise tests.
Flags: needinfo?(nsm.nikhil)
Updated•11 years ago
|
Whiteboard: [qa-]
Comment 39•10 years ago
|
||
covered on https://developer.mozilla.org/en-US/docs/Web/API/Worker/Functions_and_classes_available_to_workers
Keywords: dev-doc-needed → dev-doc-complete
You need to log in
before you can comment on or make changes to this bug.
Description
•