Closed Bug 915233 Opened 11 years ago Closed 11 years ago

Port DOM Promises to Workers

Categories

(Core :: DOM: Workers, defect)

x86_64
Linux
defect
Not set
normal

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
Attached patch promise-worker (obsolete) — 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.
Promises interact with the event loop, so this patch depends on a solution for bug 914762.
Depends on: 914762
Attached patch DOM Promises on Workers. (obsolete) — Splinter Review
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: nobody → nsm.nikhil
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+
Attached patch DOM Promises on Workers. (obsolete) — Splinter Review
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)
Attachment #813336 - Attachment is obsolete: true
Attachment #813336 - Flags: review?(khuey)
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)
Flags: needinfo?(amarchesini)
(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 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-
Attached patch DOM Promises on Workers. (obsolete) — Splinter Review
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)
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-
Attached patch DOM Promises on Workers. (obsolete) — Splinter Review
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)
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)
Flags: needinfo?(amarchesini)
Attached patch DOM Promises on Workers. (obsolete) — Splinter Review
bz for MaybeReportRejected() smaug for the mixin khuey for GetCurrentThreadWorkerPrivate()
Attachment #824797 - Flags: review?(bzbarsky)
Attachment #824797 - Flags: review?(bugs)
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 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....
(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.
> 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. ;)
Attached patch DOM Promises on Workers. (obsolete) — Splinter Review
AsyncErrorReporter boolean arg.
Attachment #824937 - Flags: review?(khuey)
Attachment #824937 - Flags: review?(bzbarsky)
Attachment #824797 - Attachment is obsolete: true
Attachment #824797 - Flags: review?(khuey)
Attachment #824797 - Flags: review?(bzbarsky)
Comment on attachment 824937 [details] [diff] [review] DOM Promises on Workers. r=me on the MaybeReportRejected bits.
Attachment #824937 - Flags: review?(bzbarsky) → review+
Attached patch DOM Promises on Workers. (obsolete) — Splinter Review
Dropping r? khuey because he added GetWorkerPrivateForCurrentThread() in another patch.
Attachment #824937 - Attachment is obsolete: true
Attachment #824937 - Flags: review?(khuey)
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+
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.
Attached patch DOM Promises on Workers. (obsolete) — Splinter Review
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.
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)
Slight fix to ensure that chromeworker and system principal checks run on main thread pref check.
Attachment #8333986 - Flags: review?(bent.mozilla)
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+
Status: NEW → RESOLVED
Closed: 11 years ago
Resolution: --- → FIXED
Target Milestone: --- → mozilla28
Keywords: feature
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+
(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)
Whiteboard: [qa-]
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: