From 14c77e53f5e6c0b172bc2e1fcc5d1c70d41c0e20 Mon Sep 17 00:00:00 2001 From: roytam1 Date: Thu, 17 Jun 2021 09:58:49 +0800 Subject: [PATCH] import changes from `dev' branch of rmottola/Arctic-Fox: - Bug 1161590 - Ignore blocklist preference in nightly and aurora. r=jrmuizelaar (47ec8bee6) - Bug 1162299 - Distinguish between all features and unrecognized feature. r=kats (e9705844f) - Add compositor, layers, and rendering info to nsIGfxInfo. (bug 1179051 part 5, r=mattwoodrow) (b4e6da05f) - Bug 1186002. Avoid testing for recreate on broken drivers. r=dvander (10506f4f2) - Bug 1156407 - part 1 - use static_assert instead of PR_STATIC_ASSERT; r=mccr8 (ff53e05ba) - Bug 1156407 - part 2 - make CALLBACK_TYPE enum a private implementation detail of nsTimerImpl; r=mccr8 (de0cc6527) - Bug 1156407 - part 3 - get rid of NS_NewTimer; r=mccr8 (c598b96e0) - Bug 1095433: fix the race condition in the Task Tracer that crashes processes forked from Nuwa. r=tlee (cffe07827) - Bug 1113562 - Expected delay time of tasks should not be the latency of those kind. r=sinker (f422ae04e) - Bug 1155059: Patch 1&2 - Convert Dispatch() and friends to already_AddRefed<> r=froydnj (2ca9850af) - Bug 1155059: Patch 4 - invoke NS_ASSERTION if DispatchToMainThread fails to get MainThread ptr r=froydnj (651903c22) - Bug 1155059: Patch 3&7 - fix leaks in Promise, ConsoleService and JS Finalize r=froydnj (b57cb08d9) - Bug 1155059: Patch 5 - clean up ServiceWorkers and avoid leaks r=nikhil (666245af8) - Bug 1155059: Patch 6 - fix problems with gfxFontInfoLoader shutdown sequence r=jdaggett (332e8bd76) - Bug 1155059: Patch 8 - Don't leak runnables when MediaCache/FileBlockCache get shut down after XPCOM is in final shutdown r=cpearce (18f36fa25) - Bug 1155059: Patch 9 - Modify DataChannel.cpp to use updated API r=froydnj (c5415703c) - Bug 1176446 - TextureClientD3D11 should take into account the layer backend when allocating a surface. r=bas (3c1b59296) - Bug 1176363 - Part 1: Stop using DrawTargets off the main thread. r=mattwoodrow (624e8107a) - Bug 1176363 - Part 2: Allow mapping of SourceSurfaceRawData from multiple threads. r=bas (38c8363cf) - Fix d3d11 texture sharing checks being preserved across device resets. (bug 1183910 part 6, r=mattwoodrow) (658121c50) - Clear the blur cache after device resets. (bug 1188032, r=bas) (c362b2ec6) --- dom/base/WebSocket.cpp | 20 +- dom/media/FileBlockCache.cpp | 13 +- dom/media/MediaCache.cpp | 10 +- dom/media/MediaManager.cpp | 20 +- dom/media/SharedThreadPool.h | 14 +- dom/promise/Promise.cpp | 17 +- dom/workers/RuntimeService.cpp | 2 +- dom/workers/ServiceWorkerRegistration.cpp | 7 +- dom/workers/WorkerPrivate.cpp | 66 +++--- dom/workers/WorkerPrivate.h | 12 +- dom/workers/WorkerRunnable.cpp | 27 ++- dom/workers/WorkerThread.cpp | 43 ++-- dom/workers/WorkerThread.h | 11 +- gfx/2d/SourceSurfaceRawData.h | 64 +++++- gfx/layers/ImageContainer.cpp | 17 +- gfx/layers/TextureDIB.cpp | 19 ++ gfx/layers/TextureDIB.h | 2 + gfx/layers/basic/TextureClientX11.cpp | 15 ++ gfx/layers/basic/TextureClientX11.h | 2 + gfx/layers/client/TextureClient.cpp | 34 +++- gfx/layers/client/TextureClient.h | 9 + gfx/layers/d3d11/TextureD3D11.cpp | 108 +++++++--- gfx/layers/d3d11/TextureD3D11.h | 2 + gfx/layers/d3d9/TextureD3D9.cpp | 71 ++++--- gfx/layers/d3d9/TextureD3D9.h | 2 + gfx/layers/opengl/GrallocTextureClient.cpp | 34 ++++ gfx/layers/opengl/GrallocTextureClient.h | 2 + gfx/src/gfxTelemetry.cpp | 36 ++++ gfx/src/gfxTelemetry.h | 46 +++++ gfx/src/moz.build | 2 + gfx/thebes/gfxFontInfoLoader.cpp | 21 +- gfx/thebes/gfxPlatform.cpp | 25 +++ gfx/thebes/gfxPlatform.h | 15 ++ gfx/thebes/gfxPrefs.h | 6 + gfx/thebes/gfxWindowsPlatform.cpp | 111 +++++++--- gfx/thebes/gfxWindowsPlatform.h | 19 +- ipc/chromium/src/base/message_loop.cc | 3 + layout/base/nsPresShell.cpp | 6 +- media/mtransport/runnable_utils.h | 2 +- netwerk/base/nsSocketTransportService2.cpp | 14 +- netwerk/base/nsSocketTransportService2.h | 4 + netwerk/base/nsStreamTransportService.cpp | 12 +- netwerk/base/nsStreamTransportService.h | 4 + netwerk/sctp/datachannel/DataChannel.cpp | 73 ++++--- .../FinalizationWitnessService.cpp | 5 +- .../components/telemetry/docs/environment.rst | 24 ++- tools/profiler/GeckoTaskTracer.cpp | 191 ++++++++++-------- tools/profiler/GeckoTaskTracer.h | 9 +- tools/profiler/GeckoTaskTracerImpl.h | 10 +- tools/profiler/TracedTaskCommon.cpp | 140 +++++++------ tools/profiler/TracedTaskCommon.h | 63 ++---- widget/GfxInfoBase.cpp | 115 ++++++++++- widget/GfxInfoBase.h | 10 + widget/nsBaseWidget.cpp | 2 + widget/nsIGfxInfo.idl | 40 +++- widget/windows/GfxInfo.cpp | 36 ++++ widget/windows/GfxInfo.h | 2 + xpcom/base/nsConsoleService.cpp | 6 +- xpcom/glue/nsThreadUtils.cpp | 25 ++- xpcom/glue/nsThreadUtils.h | 3 + xpcom/string/nsTSubstring.cpp | 3 +- xpcom/threads/LazyIdleThread.cpp | 22 +- xpcom/threads/LazyIdleThread.h | 4 + xpcom/threads/TimerThread.cpp | 6 +- xpcom/threads/nsEventQueue.cpp | 7 + xpcom/threads/nsEventQueue.h | 3 + xpcom/threads/nsIEventTarget.idl | 70 +++++-- xpcom/threads/nsIThreadPool.idl | 2 +- xpcom/threads/nsThread.cpp | 63 ++++-- xpcom/threads/nsThread.h | 15 +- xpcom/threads/nsThreadPool.cpp | 24 ++- xpcom/threads/nsThreadPool.h | 6 + xpcom/threads/nsTimerImpl.cpp | 81 ++++---- xpcom/threads/nsTimerImpl.h | 44 ++-- 74 files changed, 1487 insertions(+), 586 deletions(-) create mode 100644 gfx/src/gfxTelemetry.cpp create mode 100644 gfx/src/gfxTelemetry.h diff --git a/dom/base/WebSocket.cpp b/dom/base/WebSocket.cpp index c77e7757a1..b684cdf604 100644 --- a/dom/base/WebSocket.cpp +++ b/dom/base/WebSocket.cpp @@ -80,6 +80,10 @@ public: NS_DECL_NSIREQUEST NS_DECL_THREADSAFE_ISUPPORTS NS_DECL_NSIEVENTTARGET + // missing from NS_DECL_NSIEVENTTARGET because MSVC + nsresult Dispatch(nsIRunnable* aEvent, uint32_t aFlags) { + return Dispatch(nsCOMPtr(aEvent).forget(), aFlags); + } explicit WebSocketImpl(WebSocket* aWebSocket) : mWebSocket(aWebSocket) @@ -2667,7 +2671,7 @@ class WorkerRunnableDispatcher final : public WorkerRunnable public: WorkerRunnableDispatcher(WebSocketImpl* aImpl, WorkerPrivate* aWorkerPrivate, - nsIRunnable* aEvent) + already_AddRefed&& aEvent) : WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount) , mWebSocketImpl(aImpl) , mEvent(aEvent) @@ -2712,11 +2716,19 @@ private: } // anonymous namespace NS_IMETHODIMP -WebSocketImpl::Dispatch(nsIRunnable* aEvent, uint32_t aFlags) +WebSocketImpl::DispatchFromScript(nsIRunnable* aEvent, uint32_t aFlags) { + nsCOMPtr event(aEvent); + return Dispatch(event.forget(), aFlags); +} + +NS_IMETHODIMP +WebSocketImpl::Dispatch(already_AddRefed&& aEvent, uint32_t aFlags) +{ + nsCOMPtr event_ref(aEvent); // If the target is the main-thread we can just dispatch the runnable. if (mIsMainThread) { - return NS_DispatchToMainThread(aEvent); + return NS_DispatchToMainThread(event_ref.forget()); } MutexAutoLock lock(mMutex); @@ -2733,7 +2745,7 @@ WebSocketImpl::Dispatch(nsIRunnable* aEvent, uint32_t aFlags) // If the target is a worker, we have to use a custom WorkerRunnableDispatcher // runnable. nsRefPtr event = - new WorkerRunnableDispatcher(this, mWorkerPrivate, aEvent); + new WorkerRunnableDispatcher(this, mWorkerPrivate, event_ref.forget()); if (!event->Dispatch(nullptr)) { return NS_ERROR_FAILURE; diff --git a/dom/media/FileBlockCache.cpp b/dom/media/FileBlockCache.cpp index 6461b6fc5b..4cc9ef219c 100644 --- a/dom/media/FileBlockCache.cpp +++ b/dom/media/FileBlockCache.cpp @@ -74,9 +74,16 @@ void FileBlockCache::Close() // opening more streams, while the media cache is shutting down and // releasing memory etc! Also note we close mFD in the destructor so // as to not disturb any IO that's currently running. - nsCOMPtr event = new ShutdownThreadEvent(mThread); - mThread = nullptr; - NS_DispatchToMainThread(event); + nsCOMPtr mainThread = do_GetMainThread(); + if (mainThread) { + nsCOMPtr event = new ShutdownThreadEvent(mThread); + mainThread->Dispatch(event.forget(), NS_DISPATCH_NORMAL); + } else { + // we're on Mainthread already, *and* the event queues are already + // shut down, so no events should occur - certainly not creations of + // new streams. + mThread->Shutdown(); + } } } diff --git a/dom/media/MediaCache.cpp b/dom/media/MediaCache.cpp index 61b6692625..9cb684b3bd 100644 --- a/dom/media/MediaCache.cpp +++ b/dom/media/MediaCache.cpp @@ -1409,8 +1409,14 @@ MediaCache::QueueUpdate() if (mUpdateQueued) return; mUpdateQueued = true; - nsCOMPtr event = new UpdateEvent(); - NS_DispatchToMainThread(event); + // XXX MediaCache does updates when decoders are still running at + // shutdown and get freed in the final cycle-collector cleanup. So + // don't leak a runnable in that case. + nsCOMPtr mainThread = do_GetMainThread(); + if (mainThread) { + nsCOMPtr event = new UpdateEvent(); + mainThread->Dispatch(event.forget(), NS_DISPATCH_NORMAL); + } } void diff --git a/dom/media/MediaManager.cpp b/dom/media/MediaManager.cpp index 5189d23093..20ac7528b4 100644 --- a/dom/media/MediaManager.cpp +++ b/dom/media/MediaManager.cpp @@ -808,19 +808,19 @@ void MediaOperationTask::ReturnCallbackError(nsresult rv, const char* errorLog) { MM_LOG(("%s , rv=%d", errorLog, rv)); - NS_DispatchToMainThread(new ReleaseMediaOperationResource(mStream.forget(), - mOnTracksAvailableCallback.forget())); + NS_DispatchToMainThread(do_AddRef(new ReleaseMediaOperationResource(mStream.forget(), + mOnTracksAvailableCallback.forget()))); nsString log; log.AssignASCII(errorLog); nsCOMPtr onSuccess; nsRefPtr error = new MediaMgrError( NS_LITERAL_STRING("InternalError"), log); - NS_DispatchToMainThread( + NS_DispatchToMainThread(do_AddRef( new ErrorCallbackRunnable(onSuccess, mOnFailure, *error, - mWindowID)); + mWindowID))); } /** @@ -1207,9 +1207,9 @@ public: MOZ_ASSERT(!mOnSuccess); MOZ_ASSERT(!mOnFailure); - NS_DispatchToMainThread(runnable); + NS_DispatchToMainThread(runnable.forget()); // Do after ErrorCallbackRunnable Run()s, as it checks active window list - NS_DispatchToMainThread(new GetUserMediaListenerRemove(mWindowID, mListener)); + NS_DispatchToMainThread(do_AddRef(new GetUserMediaListenerRemove(mWindowID, mListener))); } void @@ -1379,10 +1379,10 @@ public: peerIdentity = new PeerIdentity(mConstraints.mPeerIdentity); } - NS_DispatchToMainThread(new GetUserMediaStreamRunnable( + NS_DispatchToMainThread(do_AddRef(new GetUserMediaStreamRunnable( mOnSuccess, mOnFailure, mWindowID, mListener, aAudioSource, aVideoSource, peerIdentity - )); + ))); MOZ_ASSERT(!mOnSuccess); MOZ_ASSERT(!mOnFailure); @@ -2233,7 +2233,7 @@ MediaManager::Observe(nsISupports* aSubject, const char* aTopic, { MOZ_ASSERT(MediaManager::IsInMediaThread()); mozilla::ipc::BackgroundChild::CloseForCurrentThread(); - NS_DispatchToMainThread(mReply); + NS_DispatchToMainThread(mReply.forget()); } nsRefPtr mReply; }; @@ -2692,7 +2692,7 @@ GetUserMediaCallbackMediaStreamListener::NotifyFinished(MediaStreamGraph* aGraph { mFinished = true; Invalidate(); // we know it's been activated - NS_DispatchToMainThread(new GetUserMediaListenerRemove(mWindowID, this)); + NS_DispatchToMainThread(do_AddRef(new GetUserMediaListenerRemove(mWindowID, this))); } // Called from the MediaStreamGraph thread diff --git a/dom/media/SharedThreadPool.h b/dom/media/SharedThreadPool.h index ff457438dc..f93f1c5006 100644 --- a/dom/media/SharedThreadPool.h +++ b/dom/media/SharedThreadPool.h @@ -45,7 +45,19 @@ public: // Forward behaviour to wrapped thread pool implementation. NS_FORWARD_SAFE_NSITHREADPOOL(mPool); - NS_FORWARD_SAFE_NSIEVENTTARGET(mEventTarget); + + // See bug 1155059 - MSVC forces us to not declare Dispatch normally in idl + // NS_FORWARD_SAFE_NSIEVENTTARGET(mEventTarget); + nsresult Dispatch(nsIRunnable *event, uint32_t flags) { return !mEventTarget ? NS_ERROR_NULL_POINTER : mEventTarget->Dispatch(event, flags); } + + NS_IMETHOD DispatchFromScript(nsIRunnable *event, uint32_t flags) override { + return Dispatch(event, flags); + } + + NS_IMETHOD Dispatch(already_AddRefed&& event, uint32_t flags) override + { return !mEventTarget ? NS_ERROR_NULL_POINTER : mEventTarget->Dispatch(Move(event), flags); } + + NS_IMETHOD IsOnCurrentThread(bool *_retval) override { return !mEventTarget ? NS_ERROR_NULL_POINTER : mEventTarget->IsOnCurrentThread(_retval); } // Creates necessary statics. Called once at startup. static void InitStatics(); diff --git a/dom/promise/Promise.cpp b/dom/promise/Promise.cpp index 9196d8a0ad..8ef404378b 100644 --- a/dom/promise/Promise.cpp +++ b/dom/promise/Promise.cpp @@ -1214,10 +1214,17 @@ Promise::MaybeReportRejected() // Now post an event to do the real reporting async // Since Promises preserve their wrapper, it is essential to nsRefPtr<> the // AsyncErrorReporter, otherwise if the call to DispatchToMainThread fails, it - // will leak. See Bug 958684. - nsRefPtr r = - new AsyncErrorReporter(CycleCollectedJSRuntime::Get()->Runtime(), xpcReport); - NS_DispatchToMainThread(r); + // will leak. See Bug 958684. So... don't use DispatchToMainThread() + nsCOMPtr mainThread = do_GetMainThread(); + if (NS_WARN_IF(!mainThread)) { + // Would prefer NS_ASSERTION, but that causes failure in xpcshell tests + NS_WARNING("!!! Trying to report rejected Promise after MainThread shutdown"); + } + if (mainThread) { + nsRefPtr r = + new AsyncErrorReporter(CycleCollectedJSRuntime::Get()->Runtime(), xpcReport); + mainThread->Dispatch(r.forget(), NS_DISPATCH_NORMAL); + } } #endif // defined(DOM_PROMISE_DEPRECATED_REPORTING) @@ -1634,7 +1641,7 @@ PromiseWorkerProxy::RunCallback(JSContext* aCx, if (!runnable->Dispatch(aCx)) { nsRefPtr runnable = new PromiseWorkerProxyControlRunnable(mWorkerPrivate, this); - mWorkerPrivate->DispatchControlRunnable(runnable); + mWorkerPrivate->DispatchControlRunnable(runnable.forget()); } } diff --git a/dom/workers/RuntimeService.cpp b/dom/workers/RuntimeService.cpp index 11b8a98aa5..43f4ea927a 100644 --- a/dom/workers/RuntimeService.cpp +++ b/dom/workers/RuntimeService.cpp @@ -1724,7 +1724,7 @@ RuntimeService::ScheduleWorker(JSContext* aCx, WorkerPrivate* aWorkerPrivate) nsCOMPtr runnable = new WorkerThreadPrimaryRunnable(aWorkerPrivate, thread, JS_GetParentRuntime(aCx)); - if (NS_FAILED(thread->DispatchPrimaryRunnable(friendKey, runnable))) { + if (NS_FAILED(thread->DispatchPrimaryRunnable(friendKey, runnable.forget()))) { UnregisterWorker(aCx, aWorkerPrivate); JS_ReportError(aCx, "Could not dispatch to thread!"); return false; diff --git a/dom/workers/ServiceWorkerRegistration.cpp b/dom/workers/ServiceWorkerRegistration.cpp index a5c18fd718..0d93b0407f 100644 --- a/dom/workers/ServiceWorkerRegistration.cpp +++ b/dom/workers/ServiceWorkerRegistration.cpp @@ -325,7 +325,10 @@ public: return false; } - NS_SUCCEEDED(NS_DispatchToMainThread(this)); + nsCOMPtr that(this); + if (NS_WARN_IF(NS_FAILED(NS_DispatchToMainThread(this)))) { + NS_ASSERTION(false, "Failed to dispatch update back to MainThread in ServiceWorker"); + } return true; } @@ -815,6 +818,8 @@ ServiceWorkerRegistrationWorkerThread::Update() MOZ_ASSERT(worker); worker->AssertIsOnWorkerThread(); + // XXX: this pattern guarantees we won't know which thread UpdateRunnable + // will die on (here or MainThread) nsRefPtr r = new UpdateRunnable(worker, mScope); r->Dispatch(); } diff --git a/dom/workers/WorkerPrivate.cpp b/dom/workers/WorkerPrivate.cpp index 2fa2e361d0..fc7eccb92a 100644 --- a/dom/workers/WorkerPrivate.cpp +++ b/dom/workers/WorkerPrivate.cpp @@ -1847,7 +1847,14 @@ public: protected: NS_IMETHOD - Dispatch(nsIRunnable* aRunnable, uint32_t aFlags) override + DispatchFromScript(nsIRunnable* aRunnable, uint32_t aFlags) override + { + nsCOMPtr runnable(aRunnable); + return Dispatch(runnable.forget(), aFlags); + } + + NS_IMETHOD + Dispatch(already_AddRefed&& aRunnable, uint32_t aFlags) override { // This should only happen on the timer thread. MOZ_ASSERT(!NS_IsMainThread()); @@ -1858,7 +1865,8 @@ protected: // Run the runnable we're given now (should just call DummyCallback()), // otherwise the timer thread will leak it... If we run this after // dispatch running the event can race against resetting the timer. - aRunnable->Run(); + nsCOMPtr runnable(aRunnable); + runnable->Run(); // This can fail if we're racing to terminate or cancel, should be handled // by the terminate or cancel code. @@ -2851,10 +2859,11 @@ WorkerPrivateParent::WrapObject(JSContext* aCx, JS::Handle a template nsresult -WorkerPrivateParent::DispatchPrivate(WorkerRunnable* aRunnable, +WorkerPrivateParent::DispatchPrivate(already_AddRefed&& aRunnable, nsIEventTarget* aSyncLoopTarget) { // May be called on any thread! + nsRefPtr runnable(aRunnable); WorkerPrivate* self = ParentAsWorkerPrivate(); @@ -2865,7 +2874,7 @@ WorkerPrivateParent::DispatchPrivate(WorkerRunnable* aRunnable, if (!self->mThread) { if (ParentStatus() == Pending || self->mStatus == Pending) { - mPreStartRunnables.AppendElement(aRunnable); + mPreStartRunnables.AppendElement(runnable); return NS_OK; } @@ -2883,9 +2892,9 @@ WorkerPrivateParent::DispatchPrivate(WorkerRunnable* aRunnable, nsresult rv; if (aSyncLoopTarget) { - rv = aSyncLoopTarget->Dispatch(aRunnable, NS_DISPATCH_NORMAL); + rv = aSyncLoopTarget->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL); } else { - rv = self->mThread->Dispatch(WorkerThreadFriendKey(), aRunnable); + rv = self->mThread->DispatchAnyThread(WorkerThreadFriendKey(), runnable.forget()); } if (NS_WARN_IF(NS_FAILED(rv))) { @@ -2935,13 +2944,11 @@ WorkerPrivateParent::DisableDebugger() template nsresult WorkerPrivateParent::DispatchControlRunnable( - WorkerControlRunnable* aWorkerControlRunnable) + already_AddRefed&& aWorkerControlRunnable) { // May be called on any thread! - - MOZ_ASSERT(aWorkerControlRunnable); - - nsRefPtr runnable = aWorkerControlRunnable; + nsRefPtr runnable(aWorkerControlRunnable); + MOZ_ASSERT(runnable); WorkerPrivate* self = ParentAsWorkerPrivate(); @@ -2975,13 +2982,13 @@ WorkerPrivateParent::DispatchControlRunnable( template nsresult WorkerPrivateParent::DispatchDebuggerRunnable( - WorkerRunnable *aDebuggerRunnable) + already_AddRefed&& aDebuggerRunnable) { // May be called on any thread! - MOZ_ASSERT(aDebuggerRunnable); + nsRefPtr runnable(aDebuggerRunnable); - nsRefPtr runnable = aDebuggerRunnable; + MOZ_ASSERT(runnable); WorkerPrivate* self = ParentAsWorkerPrivate(); @@ -3005,19 +3012,20 @@ WorkerPrivateParent::DispatchDebuggerRunnable( template already_AddRefed -WorkerPrivateParent::MaybeWrapAsWorkerRunnable(nsIRunnable* aRunnable) +WorkerPrivateParent::MaybeWrapAsWorkerRunnable(already_AddRefed&& aRunnable) { // May be called on any thread! - MOZ_ASSERT(aRunnable); + nsCOMPtr runnable(aRunnable); + MOZ_ASSERT(runnable); nsRefPtr workerRunnable = - WorkerRunnable::FromRunnable(aRunnable); + WorkerRunnable::FromRunnable(runnable); if (workerRunnable) { return workerRunnable.forget(); } - nsCOMPtr cancelable = do_QueryInterface(aRunnable); + nsCOMPtr cancelable = do_QueryInterface(runnable); if (!cancelable) { MOZ_CRASH("All runnables destined for a worker thread must be cancelable!"); } @@ -5408,7 +5416,7 @@ WorkerPrivate::RunBeforeNextEvent(nsIRunnable* aRunnable) // to run. if (mPreemptingRunnableInfos.Length() == 1 && !NS_HasPendingEvents(mThread)) { nsRefPtr dummyRunnable = new DummyRunnable(this); - if (NS_FAILED(Dispatch(dummyRunnable))) { + if (NS_FAILED(Dispatch(dummyRunnable.forget()))) { NS_WARNING("RunBeforeNextEvent called after the thread is shutting " "down!"); mPreemptingRunnableInfos.Clear(); @@ -7102,7 +7110,7 @@ WorkerPrivate::SetThread(WorkerThread* aThread) if (!mPreStartRunnables.IsEmpty()) { for (uint32_t index = 0; index < mPreStartRunnables.Length(); index++) { MOZ_ALWAYS_TRUE(NS_SUCCEEDED( - mThread->Dispatch(friendKey, mPreStartRunnables[index]))); + mThread->DispatchAnyThread(friendKey, mPreStartRunnables[index].forget()))); } mPreStartRunnables.Clear(); } @@ -7359,9 +7367,19 @@ NS_INTERFACE_MAP_END template NS_IMETHODIMP WorkerPrivateParent:: -EventTarget::Dispatch(nsIRunnable* aRunnable, uint32_t aFlags) +EventTarget::DispatchFromScript(nsIRunnable* aRunnable, uint32_t aFlags) +{ + nsCOMPtr event(aRunnable); + return Dispatch(event.forget(), aFlags); +} + +template +NS_IMETHODIMP +WorkerPrivateParent:: +EventTarget::Dispatch(already_AddRefed&& aRunnable, uint32_t aFlags) { // May be called on any thread! + nsCOMPtr event(aRunnable); // Workers only support asynchronous dispatch for now. if (NS_WARN_IF(aFlags != NS_DISPATCH_NORMAL)) { @@ -7378,12 +7396,12 @@ EventTarget::Dispatch(nsIRunnable* aRunnable, uint32_t aFlags) return NS_ERROR_UNEXPECTED; } - if (aRunnable) { - workerRunnable = mWorkerPrivate->MaybeWrapAsWorkerRunnable(aRunnable); + if (event) { + workerRunnable = mWorkerPrivate->MaybeWrapAsWorkerRunnable(event.forget()); } nsresult rv = - mWorkerPrivate->DispatchPrivate(workerRunnable, mNestedEventTarget); + mWorkerPrivate->DispatchPrivate(workerRunnable.forget(), mNestedEventTarget); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } diff --git a/dom/workers/WorkerPrivate.h b/dom/workers/WorkerPrivate.h index 9ad022fd8d..6f08479acf 100644 --- a/dom/workers/WorkerPrivate.h +++ b/dom/workers/WorkerPrivate.h @@ -232,7 +232,7 @@ private: ErrorResult& aRv); nsresult - DispatchPrivate(WorkerRunnable* aRunnable, nsIEventTarget* aSyncLoopTarget); + DispatchPrivate(already_AddRefed&& aRunnable, nsIEventTarget* aSyncLoopTarget); public: virtual JSObject* @@ -257,19 +257,19 @@ public: } nsresult - Dispatch(WorkerRunnable* aRunnable) + Dispatch(already_AddRefed&& aRunnable) { - return DispatchPrivate(aRunnable, nullptr); + return DispatchPrivate(Move(aRunnable), nullptr); } nsresult - DispatchControlRunnable(WorkerControlRunnable* aWorkerControlRunnable); + DispatchControlRunnable(already_AddRefed&& aWorkerControlRunnable); nsresult - DispatchDebuggerRunnable(WorkerRunnable* aDebuggerRunnable); + DispatchDebuggerRunnable(already_AddRefed&& aDebuggerRunnable); already_AddRefed - MaybeWrapAsWorkerRunnable(nsIRunnable* aRunnable); + MaybeWrapAsWorkerRunnable(already_AddRefed&& aRunnable); already_AddRefed GetEventTarget(); diff --git a/dom/workers/WorkerRunnable.cpp b/dom/workers/WorkerRunnable.cpp index bbf95f22d0..59a5b1109a 100644 --- a/dom/workers/WorkerRunnable.cpp +++ b/dom/workers/WorkerRunnable.cpp @@ -136,25 +136,27 @@ WorkerRunnable::Dispatch(JSContext* aCx) bool WorkerRunnable::DispatchInternal() { + nsRefPtr runnable(this); + if (mBehavior == WorkerThreadModifyBusyCount || mBehavior == WorkerThreadUnchangedBusyCount) { if (IsDebuggerRunnable()) { - return NS_SUCCEEDED(mWorkerPrivate->DispatchDebuggerRunnable(this)); + return NS_SUCCEEDED(mWorkerPrivate->DispatchDebuggerRunnable(runnable.forget())); } else { - return NS_SUCCEEDED(mWorkerPrivate->Dispatch(this)); + return NS_SUCCEEDED(mWorkerPrivate->Dispatch(runnable.forget())); } } MOZ_ASSERT(mBehavior == ParentThreadUnchangedBusyCount); if (WorkerPrivate* parent = mWorkerPrivate->GetParent()) { - return NS_SUCCEEDED(parent->Dispatch(this)); + return NS_SUCCEEDED(parent->Dispatch(runnable.forget())); } nsCOMPtr mainThread = do_GetMainThread(); MOZ_ASSERT(mainThread); - return NS_SUCCEEDED(mainThread->Dispatch(this, NS_DISPATCH_NORMAL)); + return NS_SUCCEEDED(mainThread->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL)); } void @@ -417,7 +419,8 @@ bool WorkerSyncRunnable::DispatchInternal() { if (mSyncLoopTarget) { - return NS_SUCCEEDED(mSyncLoopTarget->Dispatch(this, NS_DISPATCH_NORMAL)); + nsRefPtr runnable(this); + return NS_SUCCEEDED(mSyncLoopTarget->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL)); } return WorkerRunnable::DispatchInternal(); @@ -477,7 +480,8 @@ StopSyncLoopRunnable::DispatchInternal() { MOZ_ASSERT(mSyncLoopTarget); - return NS_SUCCEEDED(mSyncLoopTarget->Dispatch(this, NS_DISPATCH_NORMAL)); + nsRefPtr runnable(this); + return NS_SUCCEEDED(mSyncLoopTarget->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL)); } void @@ -503,18 +507,20 @@ WorkerControlRunnable::WorkerControlRunnable(WorkerPrivate* aWorkerPrivate, bool WorkerControlRunnable::DispatchInternal() { + nsRefPtr runnable(this); + if (mBehavior == WorkerThreadUnchangedBusyCount) { - return NS_SUCCEEDED(mWorkerPrivate->DispatchControlRunnable(this)); + return NS_SUCCEEDED(mWorkerPrivate->DispatchControlRunnable(runnable.forget())); } if (WorkerPrivate* parent = mWorkerPrivate->GetParent()) { - return NS_SUCCEEDED(parent->DispatchControlRunnable(this)); + return NS_SUCCEEDED(parent->DispatchControlRunnable(runnable.forget())); } nsCOMPtr mainThread = do_GetMainThread(); MOZ_ASSERT(mainThread); - return NS_SUCCEEDED(mainThread->Dispatch(this, NS_DISPATCH_NORMAL)); + return NS_SUCCEEDED(mainThread->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL)); } void @@ -545,8 +551,9 @@ WorkerMainThreadRunnable::Dispatch(JSContext* aCx) AutoSyncLoopHolder syncLoop(mWorkerPrivate); mSyncLoopTarget = syncLoop.EventTarget(); + nsRefPtr runnable(this); - if (NS_FAILED(NS_DispatchToMainThread(this, NS_DISPATCH_NORMAL))) { + if (NS_FAILED(NS_DispatchToMainThread(runnable.forget(), NS_DISPATCH_NORMAL))) { JS_ReportError(aCx, "Failed to dispatch to main thread!"); return false; } diff --git a/dom/workers/WorkerThread.cpp b/dom/workers/WorkerThread.cpp index c0c9907945..29fa26556d 100644 --- a/dom/workers/WorkerThread.cpp +++ b/dom/workers/WorkerThread.cpp @@ -139,11 +139,13 @@ WorkerThread::SetWorker(const WorkerThreadFriendKey& /* aKey */, nsresult WorkerThread::DispatchPrimaryRunnable(const WorkerThreadFriendKey& /* aKey */, - nsIRunnable* aRunnable) + already_AddRefed&& aRunnable) { + nsCOMPtr runnable(aRunnable); + #ifdef DEBUG MOZ_ASSERT(PR_GetCurrentThread() != mThread); - MOZ_ASSERT(aRunnable); + MOZ_ASSERT(runnable); { MutexAutoLock lock(mLock); @@ -152,7 +154,7 @@ WorkerThread::DispatchPrimaryRunnable(const WorkerThreadFriendKey& /* aKey */, } #endif - nsresult rv = nsThread::Dispatch(aRunnable, NS_DISPATCH_NORMAL); + nsresult rv = nsThread::Dispatch(runnable.forget(), NS_DISPATCH_NORMAL); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } @@ -161,8 +163,8 @@ WorkerThread::DispatchPrimaryRunnable(const WorkerThreadFriendKey& /* aKey */, } nsresult -WorkerThread::Dispatch(const WorkerThreadFriendKey& /* aKey */, - WorkerRunnable* aWorkerRunnable) +WorkerThread::DispatchAnyThread(const WorkerThreadFriendKey& /* aKey */, + already_AddRefed&& aWorkerRunnable) { // May be called on any thread! @@ -181,8 +183,9 @@ WorkerThread::Dispatch(const WorkerThreadFriendKey& /* aKey */, } } #endif + nsCOMPtr runnable(aWorkerRunnable); - nsresult rv = nsThread::Dispatch(aWorkerRunnable, NS_DISPATCH_NORMAL); + nsresult rv = nsThread::Dispatch(runnable.forget(), NS_DISPATCH_NORMAL); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } @@ -197,9 +200,17 @@ WorkerThread::Dispatch(const WorkerThreadFriendKey& /* aKey */, NS_IMPL_ISUPPORTS_INHERITED0(WorkerThread, nsThread) NS_IMETHODIMP -WorkerThread::Dispatch(nsIRunnable* aRunnable, uint32_t aFlags) +WorkerThread::DispatchFromScript(nsIRunnable* aRunnable, uint32_t aFlags) +{ + nsCOMPtr runnable(aRunnable); + return Dispatch(runnable.forget(), aFlags); +} + +NS_IMETHODIMP +WorkerThread::Dispatch(already_AddRefed&& aRunnable, uint32_t aFlags) { // May be called on any thread! + nsCOMPtr runnable(aRunnable); // in case we exit early // Workers only support asynchronous dispatch. if (NS_WARN_IF(aFlags != NS_DISPATCH_NORMAL)) { @@ -209,8 +220,8 @@ WorkerThread::Dispatch(nsIRunnable* aRunnable, uint32_t aFlags) const bool onWorkerThread = PR_GetCurrentThread() == mThread; #ifdef DEBUG - if (aRunnable && !onWorkerThread) { - nsCOMPtr cancelable = do_QueryInterface(aRunnable); + if (runnable && !onWorkerThread) { + nsCOMPtr cancelable = do_QueryInterface(runnable); { MutexAutoLock lock(mLock); @@ -245,18 +256,14 @@ WorkerThread::Dispatch(nsIRunnable* aRunnable, uint32_t aFlags) } } - nsIRunnable* runnableToDispatch; - nsRefPtr workerRunnable; - - if (aRunnable && onWorkerThread) { - workerRunnable = workerPrivate->MaybeWrapAsWorkerRunnable(aRunnable); - runnableToDispatch = workerRunnable; + nsresult rv; + if (runnable && onWorkerThread) { + nsRefPtr workerRunnable = workerPrivate->MaybeWrapAsWorkerRunnable(runnable.forget()); + rv = nsThread::Dispatch(workerRunnable.forget(), NS_DISPATCH_NORMAL); } else { - runnableToDispatch = aRunnable; + rv = nsThread::Dispatch(runnable.forget(), NS_DISPATCH_NORMAL); } - nsresult rv = nsThread::Dispatch(runnableToDispatch, NS_DISPATCH_NORMAL); - if (!onWorkerThread && workerPrivate) { // We need to wake the worker thread if we're not already on the right // thread and the dispatch succeeded. diff --git a/dom/workers/WorkerThread.h b/dom/workers/WorkerThread.h index 846f8c6dfa..4bff77999e 100644 --- a/dom/workers/WorkerThread.h +++ b/dom/workers/WorkerThread.h @@ -66,11 +66,11 @@ public: nsresult DispatchPrimaryRunnable(const WorkerThreadFriendKey& aKey, - nsIRunnable* aRunnable); + already_AddRefed&& aRunnable); nsresult - Dispatch(const WorkerThreadFriendKey& aKey, - WorkerRunnable* aWorkerRunnable); + DispatchAnyThread(const WorkerThreadFriendKey& aKey, + already_AddRefed&& aWorkerRunnable); uint32_t RecursionDepth(const WorkerThreadFriendKey& aKey) const; @@ -84,7 +84,10 @@ private: // This should only be called by consumers that have an // nsIEventTarget/nsIThread pointer. NS_IMETHOD - Dispatch(nsIRunnable* aRunnable, uint32_t aFlags) override; + Dispatch(already_AddRefed&& aRunnable, uint32_t aFlags) override; + + NS_IMETHOD + DispatchFromScript(nsIRunnable* aRunnable, uint32_t aFlags) override; }; } // namespace workers diff --git a/gfx/2d/SourceSurfaceRawData.h b/gfx/2d/SourceSurfaceRawData.h index de31ca893b..e5773b6cc5 100644 --- a/gfx/2d/SourceSurfaceRawData.h +++ b/gfx/2d/SourceSurfaceRawData.h @@ -8,6 +8,7 @@ #include "2D.h" #include "Tools.h" +#include "mozilla/Atomics.h" namespace mozilla { namespace gfx { @@ -16,8 +17,14 @@ class SourceSurfaceRawData : public DataSourceSurface { public: MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DataSourceSurfaceRawData) - SourceSurfaceRawData() {} - ~SourceSurfaceRawData() { if(mOwnData) delete [] mRawData; } + SourceSurfaceRawData() + : mMapCount(0) + {} + ~SourceSurfaceRawData() + { + if(mOwnData) delete [] mRawData; + MOZ_ASSERT(mMapCount == 0); + } virtual uint8_t *GetData() { return mRawData; } virtual int32_t Stride() { return mStride; } @@ -34,11 +41,38 @@ public: virtual void GuaranteePersistance(); + // Althought Map (and Moz2D in general) isn't normally threadsafe, + // we want to allow it for SourceSurfaceRawData since it should + // always be fine (for reading at least). + // + // This is the same as the base class implementation except using + // mMapCount instead of mIsMapped since that breaks for multithread. + // + // Once mfbt supports Monitors we should implement proper read/write + // locking to prevent write races. + virtual bool Map(MapType, MappedSurface *aMappedSurface) override + { + aMappedSurface->mData = GetData(); + aMappedSurface->mStride = Stride(); + bool success = !!aMappedSurface->mData; + if (success) { + mMapCount++; + } + return success; + } + + virtual void Unmap() override + { + mMapCount--; + MOZ_ASSERT(mMapCount >= 0); + } + private: uint8_t *mRawData; int32_t mStride; SurfaceFormat mFormat; IntSize mSize; + Atomic mMapCount; bool mOwnData; }; @@ -46,7 +80,13 @@ class SourceSurfaceAlignedRawData : public DataSourceSurface { public: MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DataSourceSurfaceAlignedRawData) - SourceSurfaceAlignedRawData() {} + SourceSurfaceAlignedRawData() + : mMapCount(0) + {} + ~SourceSurfaceAlignedRawData() + { + MOZ_ASSERT(mMapCount == 0); + } virtual uint8_t *GetData() { return mArray; } virtual int32_t Stride() { return mStride; } @@ -63,11 +103,29 @@ public: int32_t aStride, bool aZero); + virtual bool Map(MapType, MappedSurface *aMappedSurface) override + { + aMappedSurface->mData = GetData(); + aMappedSurface->mStride = Stride(); + bool success = !!aMappedSurface->mData; + if (success) { + mMapCount++; + } + return success; + } + + virtual void Unmap() override + { + mMapCount--; + MOZ_ASSERT(mMapCount >= 0); + } + private: AlignedArray mArray; int32_t mStride; SurfaceFormat mFormat; IntSize mSize; + Atomic mMapCount; }; } // namespace gfx diff --git a/gfx/layers/ImageContainer.cpp b/gfx/layers/ImageContainer.cpp index d334b6f9ce..ac09877c70 100644 --- a/gfx/layers/ImageContainer.cpp +++ b/gfx/layers/ImageContainer.cpp @@ -578,20 +578,17 @@ CairoImage::GetTextureClient(CompositableClient *aClient) if (!textureClient) { return nullptr; } - MOZ_ASSERT(textureClient->CanExposeDrawTarget()); + if (!textureClient->Lock(OpenMode::OPEN_WRITE_ONLY)) { return nullptr; } - TextureClientAutoUnlock autoUnolck(textureClient); - { - // We must not keep a reference to the DrawTarget after it has been unlocked. - DrawTarget* dt = textureClient->BorrowDrawTarget(); - if (!dt) { - return nullptr; - } - dt->CopySurface(surface, IntRect(IntPoint(), surface->GetSize()), IntPoint()); - } + TextureClientAutoUnlock autoUnlock(textureClient); + + RefPtr dataSurf = surface->GetDataSurface(); + textureClient->UpdateFromSurface(dataSurf); + + textureClient->SyncWithObject(forwarder->GetSyncObject()); mTextureClients.Put(forwarder->GetSerial(), textureClient); return textureClient; diff --git a/gfx/layers/TextureDIB.cpp b/gfx/layers/TextureDIB.cpp index 068f93ec1a..cd76822abb 100644 --- a/gfx/layers/TextureDIB.cpp +++ b/gfx/layers/TextureDIB.cpp @@ -56,6 +56,25 @@ TextureClientDIB::BorrowDrawTarget() return mDrawTarget; } +void +TextureClientDIB::UpdateFromSurface(gfx::DataSourceSurface* aSurface) +{ + MOZ_ASSERT(mIsLocked && IsAllocated()); + + nsRefPtr imgSurf = mSurface->GetAsImageSurface(); + + DataSourceSurface::MappedSurface sourceMap; + aSurface->Map(DataSourceSurface::READ, &sourceMap); + + for (int y = 0; y < aSurface->GetSize().height; y++) { + memcpy(imgSurf->Data() + imgSurf->Stride() * y, + sourceMap.mData + sourceMap.mStride * y, + aSurface->GetSize().width * BytesPerPixel(aSurface->GetFormat())); + } + + aSurface->Unmap(); +} + TextureClientMemoryDIB::TextureClientMemoryDIB(ISurfaceAllocator* aAllocator, gfx::SurfaceFormat aFormat, TextureFlags aFlags) diff --git a/gfx/layers/TextureDIB.h b/gfx/layers/TextureDIB.h index 59ff3a920f..b3e2c4f68b 100644 --- a/gfx/layers/TextureDIB.h +++ b/gfx/layers/TextureDIB.h @@ -34,6 +34,8 @@ public: virtual gfx::DrawTarget* BorrowDrawTarget() override; + virtual void UpdateFromSurface(gfx::DataSourceSurface* aSurface) override; + virtual bool HasInternalBuffer() const override { return true; } protected: diff --git a/gfx/layers/basic/TextureClientX11.cpp b/gfx/layers/basic/TextureClientX11.cpp index a7b582ffe4..01d2d30c32 100644 --- a/gfx/layers/basic/TextureClientX11.cpp +++ b/gfx/layers/basic/TextureClientX11.cpp @@ -150,3 +150,18 @@ TextureClientX11::BorrowDrawTarget() return mDrawTarget; } + +void +TextureClientX11::UpdateFromSurface(gfx::DataSourceSurface* aSurface) +{ + MOZ_ASSERT(CanExposeDrawTarget()); + + DrawTarget* dt = BorrowDrawTarget(); + + if (!dt) { + gfxCriticalError() << "Failed to borrow drawtarget for TextureClientX11::UpdateFromSurface"; + return; + } + + dt->CopySurface(aSurface, IntRect(IntPoint(), aSurface->GetSize()), IntPoint()); +} \ No newline at end of file diff --git a/gfx/layers/basic/TextureClientX11.h b/gfx/layers/basic/TextureClientX11.h index 30b4d528c5..e1afb8b009 100644 --- a/gfx/layers/basic/TextureClientX11.h +++ b/gfx/layers/basic/TextureClientX11.h @@ -43,6 +43,8 @@ class TextureClientX11 : public TextureClient virtual gfx::DrawTarget* BorrowDrawTarget() override; + virtual void UpdateFromSurface(gfx::DataSourceSurface* aSurface) override; + virtual gfx::SurfaceFormat GetFormat() const override { return mFormat; } virtual bool HasInternalBuffer() const override { return false; } diff --git a/gfx/layers/client/TextureClient.cpp b/gfx/layers/client/TextureClient.cpp index cd29866823..e4c254add7 100644 --- a/gfx/layers/client/TextureClient.cpp +++ b/gfx/layers/client/TextureClient.cpp @@ -357,7 +357,8 @@ TextureClient::CreateForDrawing(ISurfaceAllocator* aAllocator, aMoz2DBackend == gfx::BackendType::CAIRO && aAllocator->IsSameProcess() && aSize.width <= maxTextureSize && - aSize.height <= maxTextureSize) { + aSize.height <= maxTextureSize && + NS_IsMainThread()) { if (gfxWindowsPlatform::GetPlatform()->GetD3D9Device()) { texture = new TextureClientD3D9(aAllocator, aFormat, aTextureFlags); } @@ -365,7 +366,8 @@ TextureClient::CreateForDrawing(ISurfaceAllocator* aAllocator, if (!texture && aFormat == SurfaceFormat::B8G8R8X8 && aAllocator->IsSameProcess() && - aMoz2DBackend == gfx::BackendType::CAIRO) { + aMoz2DBackend == gfx::BackendType::CAIRO && + NS_IsMainThread()) { if (aAllocator->IsSameProcess()) { texture = new TextureClientMemoryDIB(aAllocator, aFormat, aTextureFlags); } else { @@ -813,6 +815,7 @@ gfx::DrawTarget* BufferTextureClient::BorrowDrawTarget() { MOZ_ASSERT(IsValid()); + MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(mLocked, "BorrowDrawTarget should be called on locked textures only"); if (!mLocked) { return nullptr; @@ -838,6 +841,33 @@ BufferTextureClient::BorrowDrawTarget() return mDrawTarget; } +void +BufferTextureClient::UpdateFromSurface(gfx::DataSourceSurface* aSurface) +{ + ImageDataSerializer serializer(GetBuffer(), GetBufferSize()); + + RefPtr surface = serializer.GetAsSurface(); + + if (surface->GetSize() != aSurface->GetSize() || surface->GetFormat() != aSurface->GetFormat()) { + gfxCriticalError() << "Attempt to update texture client from a surface with a different size or format! This: " << surface->GetSize() << " " << surface->GetFormat() << " Other: " << aSurface->GetSize() << " " << aSurface->GetFormat(); + return; + } + + DataSourceSurface::MappedSurface sourceMap; + DataSourceSurface::MappedSurface destMap; + aSurface->Map(DataSourceSurface::READ, &sourceMap); + surface->Map(DataSourceSurface::WRITE, &destMap); + + for (int y = 0; y < aSurface->GetSize().height; y++) { + memcpy(destMap.mData + destMap.mStride * y, + sourceMap.mData + sourceMap.mStride * y, + aSurface->GetSize().width * BytesPerPixel(aSurface->GetFormat())); + } + + aSurface->Unmap(); + surface->Unmap(); +} + bool BufferTextureClient::Lock(OpenMode aMode) { diff --git a/gfx/layers/client/TextureClient.h b/gfx/layers/client/TextureClient.h index c081812921..6fb4bb38bf 100644 --- a/gfx/layers/client/TextureClient.h +++ b/gfx/layers/client/TextureClient.h @@ -243,6 +243,7 @@ public: /** * Returns a DrawTarget to draw into the TextureClient. + * This function should never be called when not on the main thread! * * This must never be called on a TextureClient that is not sucessfully locked. * When called several times within one Lock/Unlock pair, this method should @@ -268,6 +269,12 @@ public: */ virtual gfx::DrawTarget* BorrowDrawTarget() { return nullptr; } + /** + * This function can be used to update the contents of the TextureClient + * off the main thread. + */ + virtual void UpdateFromSurface(gfx::DataSourceSurface* aSurface) { MOZ_CRASH(); } + // TextureClients that can expose a DrawTarget should override this method. virtual gfx::SurfaceFormat GetFormat() const { @@ -586,6 +593,8 @@ public: virtual gfx::DrawTarget* BorrowDrawTarget() override; + virtual void UpdateFromSurface(gfx::DataSourceSurface* aSurface) override; + virtual bool AllocateForSurface(gfx::IntSize aSize, TextureAllocationFlags aFlags = ALLOC_DEFAULT) override; diff --git a/gfx/layers/d3d11/TextureD3D11.cpp b/gfx/layers/d3d11/TextureD3D11.cpp index b6e198766e..8e84822dc3 100644 --- a/gfx/layers/d3d11/TextureD3D11.cpp +++ b/gfx/layers/d3d11/TextureD3D11.cpp @@ -244,7 +244,8 @@ TextureClientD3D11::CreateSimilar(TextureFlags aFlags, void TextureClientD3D11::SyncWithObject(SyncObject* aSyncObject) { - if (!aSyncObject) { + if (!aSyncObject || !NS_IsMainThread()) { + // When off the main thread we sync using a keyed mutex per texture. return; } @@ -278,33 +279,35 @@ TextureClientD3D11::Lock(OpenMode aMode) return false; } - // Make sure that successful write-lock means we will have a DrawTarget to - // write into. - if (aMode & OpenMode::OPEN_WRITE) { - mDrawTarget = BorrowDrawTarget(); - if (!mDrawTarget) { - Unlock(); - return false; + if (NS_IsMainThread()) { + // Make sure that successful write-lock means we will have a DrawTarget to + // write into. + if (aMode & OpenMode::OPEN_WRITE) { + mDrawTarget = BorrowDrawTarget(); + if (!mDrawTarget) { + Unlock(); + return false; + } } - } - if (mNeedsClear) { - mDrawTarget = BorrowDrawTarget(); - if (!mDrawTarget) { + if (mNeedsClear) { + mDrawTarget = BorrowDrawTarget(); + if (!mDrawTarget) { Unlock(); return false; + } + mDrawTarget->ClearRect(Rect(0, 0, GetSize().width, GetSize().height)); + mNeedsClear = false; } - mDrawTarget->ClearRect(Rect(0, 0, GetSize().width, GetSize().height)); - mNeedsClear = false; - } - if (mNeedsClearWhite) { - mDrawTarget = BorrowDrawTarget(); - if (!mDrawTarget) { + if (mNeedsClearWhite) { + mDrawTarget = BorrowDrawTarget(); + if (!mDrawTarget) { Unlock(); return false; + } + mDrawTarget->FillRect(Rect(0, 0, GetSize().width, GetSize().height), ColorPattern(Color(1.0, 1.0, 1.0, 1.0))); + mNeedsClearWhite = false; } - mDrawTarget->FillRect(Rect(0, 0, GetSize().width, GetSize().height), ColorPattern(Color(1.0, 1.0, 1.0, 1.0))); - mNeedsClearWhite = false; } return true; @@ -327,7 +330,7 @@ TextureClientD3D11::Unlock() mDrawTarget->Flush(); } - if (mReadbackSink && mTexture10) { + if (NS_IsMainThread() && mReadbackSink && mTexture10) { ID3D10Device* device = gfxWindowsPlatform::GetPlatform()->GetD3D10Device(); D3D10_TEXTURE2D_DESC desc; @@ -363,6 +366,7 @@ DrawTarget* TextureClientD3D11::BorrowDrawTarget() { MOZ_ASSERT(mIsLocked, "Calling TextureClient::BorrowDrawTarget without locking :("); + MOZ_ASSERT(NS_IsMainThread()); if (!mIsLocked || (!mTexture && !mTexture10)) { gfxCriticalError() << "Attempted to borrow a DrawTarget without locking the texture."; @@ -387,6 +391,53 @@ TextureClientD3D11::BorrowDrawTarget() return mDrawTarget; } +void +TextureClientD3D11::UpdateFromSurface(gfx::DataSourceSurface* aSurface) +{ + DataSourceSurface::MappedSurface sourceMap; + aSurface->Map(DataSourceSurface::READ, &sourceMap); + + if (mDrawTarget) { + // Ensure unflushed work from our outstanding drawtarget won't override this + // update later. + mDrawTarget->Flush(); + } + + if (mSize != aSurface->GetSize() || mFormat != aSurface->GetFormat()) { + gfxCriticalError() << "Attempt to update texture client from a surface with a different size or format!"; + return; + } + + if (mTexture) { + RefPtr device; + mTexture->GetDevice(byRef(device)); + RefPtr ctx; + device->GetImmediateContext(byRef(ctx)); + + D3D11_BOX box; + box.front = 0; + box.back = 1; + box.top = box.left = 0; + box.right = aSurface->GetSize().width; + box.bottom = aSurface->GetSize().height; + + ctx->UpdateSubresource(mTexture, 0, &box, sourceMap.mData, sourceMap.mStride, 0); + } else { + RefPtr device; + mTexture10->GetDevice(byRef(device)); + + D3D10_BOX box; + box.front = 0; + box.back = 1; + box.top = box.left = 0; + box.right = aSurface->GetSize().width; + box.bottom = aSurface->GetSize().height; + + device->UpdateSubresource(mTexture10, 0, &box, sourceMap.mData, sourceMap.mStride, 0); + } + aSurface->Unmap(); +} + static const GUID sD3D11TextureUsage = { 0xd89275b0, 0x6c7d, 0x4038, { 0xb5, 0xfa, 0x4d, 0x87, 0x16, 0xd5, 0xcc, 0x4e } }; @@ -446,9 +497,15 @@ TextureClientD3D11::AllocateForSurface(gfx::IntSize aSize, TextureAllocationFlag return false; } - ID3D11Device* d3d11device = gfxWindowsPlatform::GetPlatform()->GetD3D11ContentDevice(); + gfxWindowsPlatform* windowsPlatform = gfxWindowsPlatform::GetPlatform(); + ID3D11Device* d3d11device = windowsPlatform->GetD3D11DeviceForCurrentThread(); - if (gfxPrefs::Direct2DUse1_1() && d3d11device) { + // When we're not on the main thread we're not going to be using Direct2D + // to access the contents of this texture client so we will always use D3D11. + bool haveD3d11Backend = windowsPlatform->GetContentBackend() == BackendType::DIRECT2D1_1 || !NS_IsMainThread(); + + if (haveD3d11Backend) { + MOZ_ASSERT(d3d11device != nullptr); CD3D11_TEXTURE2D_DESC newDesc(mFormat == SurfaceFormat::A8 ? DXGI_FORMAT_R8_UNORM : DXGI_FORMAT_B8G8R8A8_UNORM, aSize.width, aSize.height, 1, 1, @@ -456,6 +513,11 @@ TextureClientD3D11::AllocateForSurface(gfx::IntSize aSize, TextureAllocationFlag newDesc.MiscFlags = D3D11_RESOURCE_MISC_SHARED; + if (!NS_IsMainThread()) { + // On the main thread we use the syncobject to handle synchronization. + newDesc.MiscFlags = D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX; + } + hr = d3d11device->CreateTexture2D(&newDesc, nullptr, byRef(mTexture)); if (FAILED(hr)) { gfxCriticalError(CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(aSize))) << "[D3D11] 2 CreateTexture2D failure " << aSize << " Code: " << gfx::hexa(hr); diff --git a/gfx/layers/d3d11/TextureD3D11.h b/gfx/layers/d3d11/TextureD3D11.h index 911f86ae69..e2c6327ee0 100644 --- a/gfx/layers/d3d11/TextureD3D11.h +++ b/gfx/layers/d3d11/TextureD3D11.h @@ -117,6 +117,8 @@ public: virtual gfx::DrawTarget* BorrowDrawTarget() override; + virtual void UpdateFromSurface(gfx::DataSourceSurface* aSurface) override; + virtual bool AllocateForSurface(gfx::IntSize aSize, TextureAllocationFlags aFlags = ALLOC_DEFAULT) override; diff --git a/gfx/layers/d3d9/TextureD3D9.cpp b/gfx/layers/d3d9/TextureD3D9.cpp index 3c6131a0dd..b88ca6fd44 100644 --- a/gfx/layers/d3d9/TextureD3D9.cpp +++ b/gfx/layers/d3d9/TextureD3D9.cpp @@ -498,35 +498,6 @@ TextureClientD3D9::Lock(OpenMode aMode) mIsLocked = true; - // Make sure that successful write-lock means we will have a DrawTarget to - // write into. - if (aMode & OpenMode::OPEN_WRITE) { - mDrawTarget = BorrowDrawTarget(); - if (!mDrawTarget) { - Unlock(); - return false; - } - } - - if (mNeedsClear) { - mDrawTarget = BorrowDrawTarget(); - if (!mDrawTarget) { - Unlock(); - return false; - } - mDrawTarget->ClearRect(Rect(0, 0, GetSize().width, GetSize().height)); - mNeedsClear = false; - } - if (mNeedsClearWhite) { - mDrawTarget = BorrowDrawTarget(); - if (!mDrawTarget) { - Unlock(); - return false; - } - mDrawTarget->FillRect(Rect(0, 0, GetSize().width, GetSize().height), ColorPattern(Color(1.0, 1.0, 1.0, 1.0))); - mNeedsClearWhite = false; - } - return true; } @@ -601,9 +572,51 @@ TextureClientD3D9::BorrowDrawTarget() mLockRect = true; } + if (mNeedsClear) { + mDrawTarget->ClearRect(Rect(0, 0, GetSize().width, GetSize().height)); + mNeedsClear = false; + } + if (mNeedsClearWhite) { + mDrawTarget->FillRect(Rect(0, 0, GetSize().width, GetSize().height), ColorPattern(Color(1.0, 1.0, 1.0, 1.0))); + mNeedsClearWhite = false; + } + return mDrawTarget; } +void +TextureClientD3D9::UpdateFromSurface(gfx::DataSourceSurface* aSurface) +{ + MOZ_ASSERT(mIsLocked && mD3D9Surface); + + // gfxWindowsSurface don't support transparency so we can't use the d3d9 + // windows surface optimization. + // Instead we have to use a gfxImageSurface and fallback for font drawing. + D3DLOCKED_RECT rect; + HRESULT hr = mD3D9Surface->LockRect(&rect, nullptr, 0); + if (FAILED(hr) || !rect.pBits) { + gfxCriticalError() << "Failed to lock rect borrowing the target in D3D9 " << hexa(hr); + return; + } + + if (mSize != aSurface->GetSize() || mFormat != aSurface->GetFormat()) { + gfxCriticalError() << "Attempt to update texture client from a surface with a different size or format! This: " << mSize << " " << mFormat << " Other: " << aSurface->GetSize() << " " << aSurface->GetFormat(); + return; + } + + DataSourceSurface::MappedSurface sourceMap; + aSurface->Map(DataSourceSurface::READ, &sourceMap); + + for (int y = 0; y < aSurface->GetSize().height; y++) { + memcpy((uint8_t*)rect.pBits + rect.Pitch * y, + sourceMap.mData + sourceMap.mStride * y, + aSurface->GetSize().width * BytesPerPixel(aSurface->GetFormat())); + } + + aSurface->Unmap(); + mD3D9Surface->UnlockRect(); +} + bool TextureClientD3D9::AllocateForSurface(gfx::IntSize aSize, TextureAllocationFlags aFlags) { diff --git a/gfx/layers/d3d9/TextureD3D9.h b/gfx/layers/d3d9/TextureD3D9.h index 6278c40c25..612e11cff6 100644 --- a/gfx/layers/d3d9/TextureD3D9.h +++ b/gfx/layers/d3d9/TextureD3D9.h @@ -202,6 +202,8 @@ public: virtual gfx::DrawTarget* BorrowDrawTarget() override; + virtual void UpdateFromSurface(gfx::DataSourceSurface* aSurface) override; + virtual bool AllocateForSurface(gfx::IntSize aSize, TextureAllocationFlags aFlags = ALLOC_DEFAULT) override; diff --git a/gfx/layers/opengl/GrallocTextureClient.cpp b/gfx/layers/opengl/GrallocTextureClient.cpp index e45827655d..35dd08c2dd 100644 --- a/gfx/layers/opengl/GrallocTextureClient.cpp +++ b/gfx/layers/opengl/GrallocTextureClient.cpp @@ -210,6 +210,40 @@ GrallocTextureClientOGL::BorrowDrawTarget() return mDrawTarget; } +void +GrallocTextureClientOGL::UpdateFromSurface(gfx::DataSourceSurface* aSurface) +{ + MOZ_ASSERT(IsValid()); + MOZ_ASSERT(mMappedBuffer, "Calling TextureClient::BorrowDrawTarget without locking :("); + + if (!IsValid() || !IsAllocated() || !mMappedBuffer) { + return; + } + + gfx::SurfaceFormat format = SurfaceFormatForPixelFormat(mGraphicBuffer->getPixelFormat()); + if (mSize != aSurface->GetSize() || mFormat != aSurface->GetFormat()) { + gfxCriticalError() << "Attempt to update texture client from a surface with a different size or format! This: " << mSize << " " << format << " Other: " << aSurface->GetSize() << " " << aSurface->GetFormat(); + return; + } + + long pixelStride = mGraphicBuffer->getStride(); + long byteStride = pixelStride * BytesPerPixel(format); + + DataSourceSurface::MappedSurface sourceMap; + + aSurface->Map(DataSourceSurface::READ, &sourceMap); + + uint8_t* buffer = GetBuffer(); + + for (int y = 0; y < aSurface->GetSize().height; y++) { + memcpy(buffer + byteStride * y, + sourceMap.mData + sourceMap.mStride * y, + aSurface->GetSize().width * BytesPerPixel(aSurface->GetFormat())); + } + + aSurface->Unmap(); +} + bool GrallocTextureClientOGL::AllocateForSurface(gfx::IntSize aSize, TextureAllocationFlags) diff --git a/gfx/layers/opengl/GrallocTextureClient.h b/gfx/layers/opengl/GrallocTextureClient.h index 71d4defd40..d560405b50 100644 --- a/gfx/layers/opengl/GrallocTextureClient.h +++ b/gfx/layers/opengl/GrallocTextureClient.h @@ -82,6 +82,8 @@ public: virtual gfx::DrawTarget* BorrowDrawTarget() override; + virtual void UpdateFromSurface(gfx::DataSourceSurface* aSurface) override; + virtual bool AllocateForSurface(gfx::IntSize aSize, TextureAllocationFlags aFlags = ALLOC_DEFAULT) override; diff --git a/gfx/src/gfxTelemetry.cpp b/gfx/src/gfxTelemetry.cpp new file mode 100644 index 0000000000..027c6761ed --- /dev/null +++ b/gfx/src/gfxTelemetry.cpp @@ -0,0 +1,36 @@ +/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "gfxTelemetry.h" + +namespace mozilla { +namespace gfx { + +const char* +FeatureStatusToString(FeatureStatus aStatus) +{ + switch (aStatus) { + case FeatureStatus::Unused: + return "unused"; + case FeatureStatus::Unavailable: + return "unavailable"; + case FeatureStatus::Blocked: + return "blocked"; + case FeatureStatus::Blacklisted: + return "blacklisted"; + case FeatureStatus::Failed: + return "failed"; + case FeatureStatus::Disabled: + return "disabled"; + case FeatureStatus::Available: + return "available"; + default: + MOZ_ASSERT_UNREACHABLE("missing status case"); + return "unknown"; + } +} + +} // namespace gfx +} // namespace mozilla diff --git a/gfx/src/gfxTelemetry.h b/gfx/src/gfxTelemetry.h new file mode 100644 index 0000000000..e65c107c38 --- /dev/null +++ b/gfx/src/gfxTelemetry.h @@ -0,0 +1,46 @@ +/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#ifndef gfx_src_gfxTelemetry_h__ +#define gfx_src_gfxTelemetry_h__ + +namespace mozilla { +namespace gfx { + +// Describes the status of a graphics feature, in terms of whether or not we've +// attempted to initialize the feature, and if so, whether or not it succeeded +// (and if not, why). +enum class FeatureStatus +{ + // This feature has not been requested. + Unused, + + // This feature is unavailable due to Safe Mode or not being included with + // the operating system. + Unavailable, + + // This feature was blocked for reasons outside the blacklist, such as a + // runtime test failing. + Blocked, + + // This feature has been blocked by the graphics blacklist. + Blacklisted, + + // This feature was attempted but failed to activate. + Failed, + + // This feature was explicitly disabled by the user. + Disabled, + + // This feature is available for use. + Available +}; + +const char* FeatureStatusToString(FeatureStatus aStatus); + +} // namespace gfx +} // namespace mozilla + +#endif // gfx_src_gfxTelemetry_h__ diff --git a/gfx/src/moz.build b/gfx/src/moz.build index 8f9bff1415..3d26b88016 100644 --- a/gfx/src/moz.build +++ b/gfx/src/moz.build @@ -18,6 +18,7 @@ EXPORTS += [ 'FilterSupport.h', 'gfxCore.h', 'gfxCrashReporterUtils.h', + 'gfxTelemetry.h', 'nsBoundingMetrics.h', 'nsColor.h', 'nsColorNameList.h', @@ -56,6 +57,7 @@ UNIFIED_SOURCES += [ 'DriverInitCrashDetection.cpp', 'FilterSupport.cpp', 'gfxCrashReporterUtils.cpp', + 'gfxTelemetry.cpp', 'nsColor.cpp', 'nsFont.cpp', 'nsFontMetrics.cpp', diff --git a/gfx/thebes/gfxFontInfoLoader.cpp b/gfx/thebes/gfxFontInfoLoader.cpp index 8e276808a6..ee32a961e4 100644 --- a/gfx/thebes/gfxFontInfoLoader.cpp +++ b/gfx/thebes/gfxFontInfoLoader.cpp @@ -66,7 +66,6 @@ FontInfoLoadCompleteEvent::Run() loader->FinalizeLoader(mFontInfo); - mFontInfo = nullptr; return NS_OK; } @@ -81,7 +80,6 @@ AsyncFontInfoLoader::Run() // post a completion event that transfer the data to the fontlist NS_DispatchToMainThread(mCompleteEvent); - mFontInfo = nullptr; return NS_OK; } @@ -140,23 +138,27 @@ gfxFontInfoLoader::StartLoader(uint32_t aDelay, uint32_t aInterval) InitLoader(); // start async load - mState = stateAsyncLoad; nsresult rv = NS_NewNamedThread("Font Loader", getter_AddRefs(mFontLoaderThread), nullptr); - if (NS_FAILED(rv)) { + if (NS_WARN_IF(NS_FAILED(rv))) { return; } + mState = stateAsyncLoad; nsCOMPtr loadEvent = new AsyncFontInfoLoader(mFontInfo); - mFontLoaderThread->Dispatch(loadEvent, NS_DISPATCH_NORMAL); + mFontLoaderThread->Dispatch(loadEvent.forget(), NS_DISPATCH_NORMAL); } void gfxFontInfoLoader::FinalizeLoader(FontInfoData *aFontInfo) { - // avoid loading data if loader has already been canceled + // Avoid loading data if loader has already been canceled. + // This should mean that CancelLoader() ran and the Load + // thread has already Shutdown(), and likely before processing + // the Shutdown event it handled the load event and sent back + // our Completion event, thus we end up here. if (mState != stateAsyncLoad) { return; } @@ -187,8 +189,11 @@ gfxFontInfoLoader::CancelLoader() mTimer = nullptr; } if (mFontLoaderThread) { - mFontLoaderThread->Shutdown(); - mFontLoaderThread = nullptr; + // NOTE: Shutdown() runs the event loop, and we can get timer events + // ensure that we can't try to do this twice! + nsCOMPtr temp; + temp.swap(mFontLoaderThread); + temp->Shutdown(); } RemoveShutdownObserver(); CleanupLoader(); diff --git a/gfx/thebes/gfxPlatform.cpp b/gfx/thebes/gfxPlatform.cpp index 3e0d04a40d..e7f7942c4e 100644 --- a/gfx/thebes/gfxPlatform.cpp +++ b/gfx/thebes/gfxPlatform.cpp @@ -417,6 +417,7 @@ gfxPlatform::gfxPlatform() , mTileHeight(-1) , mAzureCanvasBackendCollector(this, &gfxPlatform::GetAzureBackendInfo) , mApzSupportCollector(this, &gfxPlatform::GetApzSupportInfo) + , mCompositorBackend(layers::LayersBackend::LAYERS_NONE) { mAllowDownloadableFonts = UNINITIALIZED_VALUE; mFallbackUsesCmaps = UNINITIALIZED_VALUE; @@ -448,6 +449,12 @@ gfxPlatform::GetPlatform() return gPlatform; } +bool +gfxPlatform::Initialized() +{ + return !!gPlatform; +} + void RecordingPrefChanged(const char *aPrefName, void *aClosure) { if (Preferences::GetBool("gfx.2d.recording", false)) { @@ -2558,3 +2565,21 @@ gfxPlatform::GetCompositorBackends(bool useAcceleration, nsTArray obsvc = services::GetObserverService()) { + obsvc->NotifyObservers(nullptr, "compositor:created", nullptr); + } +} diff --git a/gfx/thebes/gfxPlatform.h b/gfx/thebes/gfxPlatform.h index 22cac92dea..0df4151818 100644 --- a/gfx/thebes/gfxPlatform.h +++ b/gfx/thebes/gfxPlatform.h @@ -188,6 +188,12 @@ public: */ static gfxPlatform *GetPlatform(); + /** + * Returns whether or not graphics has been initialized yet. This is + * intended for Telemetry where we don't necessarily want to initialize + * graphics just to observe its state. + */ + static bool Initialized(); /** * Shut down Thebes. @@ -662,6 +668,11 @@ public: */ static bool PerfWarnings(); + void NotifyCompositorCreated(mozilla::layers::LayersBackend aBackend); + mozilla::layers::LayersBackend GetCompositorBackend() const { + return mCompositorBackend; + } + protected: gfxPlatform(); virtual ~gfxPlatform(); @@ -782,6 +793,10 @@ private: mozilla::RefPtr mRecorder; mozilla::RefPtr mSkiaGlue; + + // Backend that we are compositing with. NONE, if no compositor has been + // created yet. + mozilla::layers::LayersBackend mCompositorBackend; }; #endif /* GFX_PLATFORM_H */ diff --git a/gfx/thebes/gfxPrefs.h b/gfx/thebes/gfxPrefs.h index a2699e4926..f047a7a084 100644 --- a/gfx/thebes/gfxPrefs.h +++ b/gfx/thebes/gfxPrefs.h @@ -201,6 +201,12 @@ private: DECL_GFX_PREF(Once, "gfx.android.rgb16.force", AndroidRGB16Force, bool, false); #if defined(ANDROID) DECL_GFX_PREF(Once, "gfx.apitrace.enabled", UseApitrace, bool, false); +#endif +#if defined(RELEASE_BUILD) + // "Skip" means this is locked to the default value in beta and release. + DECL_GFX_PREF(Skip, "gfx.blocklist.all", BlocklistAll, int32_t, 0); +#else + DECL_GFX_PREF(Once, "gfx.blocklist.all", BlocklistAll, int32_t, 0); #endif DECL_GFX_PREF(Live, "gfx.canvas.auto_accelerate.min_calls", CanvasAutoAccelerateMinCalls, int32_t, 4); DECL_GFX_PREF(Live, "gfx.canvas.auto_accelerate.min_frames", CanvasAutoAccelerateMinFrames, int32_t, 30); diff --git a/gfx/thebes/gfxWindowsPlatform.cpp b/gfx/thebes/gfxWindowsPlatform.cpp index b1114f1552..b6afb06130 100644 --- a/gfx/thebes/gfxWindowsPlatform.cpp +++ b/gfx/thebes/gfxWindowsPlatform.cpp @@ -376,6 +376,8 @@ gfxWindowsPlatform::gfxWindowsPlatform() , mIsWARP(false) , mHasDeviceReset(false) , mDoesD3D11TextureSharingWork(false) + , mD3D11Status(FeatureStatus::Unused) + , mD2DStatus(FeatureStatus::Unused) { mUseClearTypeForDownloadableFonts = UNINITIALIZED_VALUE; mUseClearTypeAlways = UNINITIALIZED_VALUE; @@ -435,7 +437,7 @@ gfxWindowsPlatform::CanUseHardwareVideoDecoding() return !IsWARP() && gfxPlatform::CanUseHardwareVideoDecoding(); } -void +FeatureStatus gfxWindowsPlatform::InitD2DSupport() { #ifdef CAIRO_HAS_D2D_SURFACE @@ -457,30 +459,41 @@ gfxWindowsPlatform::InitD2DSupport() // If D2D is blocked or D3D9 is prefered, and D2D is not force-enabled, then // we don't attempt to use D2D. - if ((d2dBlocked || gfxPrefs::LayersPreferD3D9()) && !gfxPrefs::Direct2DForceEnabled()) { - return; + if (!gfxPrefs::Direct2DForceEnabled()) { + if (d2dBlocked) { + return FeatureStatus::Blacklisted; + } + if (gfxPrefs::LayersPreferD3D9()) { + return FeatureStatus::Disabled; + } } // Do not ever try to use D2D if it's explicitly disabled or if we're not // using DWrite fonts. if (gfxPrefs::Direct2DDisabled() || mUsingGDIFonts) { - return; + return FeatureStatus::Disabled; } - ID3D11Device* device = GetD3D11Device(); - if (IsVistaOrLater() && - !InSafeMode() && - device && - mDoesD3D11TextureSharingWork) - { - VerifyD2DDevice(gfxPrefs::Direct2DForceEnabled()); - if (mD3D10Device && GetD3D11Device()) { - mRenderMode = RENDER_DIRECT2D; - mUseDirectWrite = true; - } - } else { - mD3D10Device = nullptr; + if (!IsVistaOrLater() || !GetD3D11Device()) { + return FeatureStatus::Unavailable; } + if (!mDoesD3D11TextureSharingWork) { + return FeatureStatus::Failed; + } + if (InSafeMode()) { + return FeatureStatus::Blocked; + } + + VerifyD2DDevice(gfxPrefs::Direct2DForceEnabled()); + if (!mD3D10Device || !GetD3D11Device()) { + return FeatureStatus::Failed; + } + + mRenderMode = RENDER_DIRECT2D; + mUseDirectWrite = true; + return FeatureStatus::Available; +#else + return FeatureStatus::Unavailable; #endif } @@ -543,6 +556,7 @@ gfxWindowsPlatform::UpdateRenderMode() imgLoader::Singleton()->ClearCache(true); imgLoader::Singleton()->ClearCache(false); + gfxAlphaBoxBlur::ShutdownBlurCache(); Factory::SetDirect3D11Device(nullptr); didReset = true; @@ -551,7 +565,7 @@ gfxWindowsPlatform::UpdateRenderMode() mRenderMode = RENDER_GDI; mUseDirectWrite = gfxPrefs::DirectWriteFontRenderingEnabled(); - InitD2DSupport(); + mD2DStatus = InitD2DSupport(); InitDWriteSupport(); uint32_t canvasMask = BackendTypeBit(BackendType::CAIRO); @@ -1576,6 +1590,15 @@ gfxWindowsPlatform::GetD3D11ImageBridgeDevice() return mD3D11ImageBridgeDevice; } +ID3D11Device* +gfxWindowsPlatform::GetD3D11DeviceForCurrentThread() +{ + if (NS_IsMainThread()) { + return GetD3D11ContentDevice(); + } else { + return GetD3D11ImageBridgeDevice(); + } +} ReadbackManagerD3D11* gfxWindowsPlatform::GetReadbackManager() @@ -1723,6 +1746,12 @@ CheckForAdapterMismatch(ID3D11Device *device) void CheckIfRenderTargetViewNeedsRecreating(ID3D11Device *device) { + // CreateTexture2D is known to crash on lower feature levels, see bugs + // 1170211 and 1089413. + if (device->GetFeatureLevel() < D3D_FEATURE_LEVEL_10_0) { + return; + } + nsRefPtr deviceContext; device->GetImmediateContext(getter_AddRefs(deviceContext)); int backbufferWidth = 32; int backbufferHeight = 32; @@ -1882,15 +1911,7 @@ bool DoesD3D11TextureSharingWorkInternal(ID3D11Device *device, DXGI_FORMAT forma bool DoesD3D11TextureSharingWork(ID3D11Device *device) { - static bool checked; - static bool result; - - if (checked) - return result; - checked = true; - - result = DoesD3D11TextureSharingWorkInternal(device, DXGI_FORMAT_B8G8R8A8_UNORM, D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE); - return result; + return DoesD3D11TextureSharingWorkInternal(device, DXGI_FORMAT_B8G8R8A8_UNORM, D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE); } bool DoesD3D11AlphaTextureSharingWork(ID3D11Device *device) @@ -2057,15 +2078,17 @@ gfxWindowsPlatform::InitD3D11Devices() mD3D11DeviceInitialized = true; mDoesD3D11TextureSharingWork = false; - MOZ_ASSERT(!mD3D11Device); + MOZ_ASSERT(!mD3D11Device); DriverInitCrashDetection detectCrashes; if (InSafeMode() || detectCrashes.DisableAcceleration()) { + mD3D11Status = FeatureStatus::Blocked; return; } D3D11Status status = CheckD3D11Support(); if (status == D3D11Status::Blocked) { + mD3D11Status = FeatureStatus::Blacklisted; return; } @@ -2075,6 +2098,7 @@ gfxWindowsPlatform::InitD3D11Devices() if (!sD3D11CreateDeviceFn) { // We should just be on Windows Vista or XP in this case. + mD3D11Status = FeatureStatus::Unavailable; return; } @@ -2098,6 +2122,7 @@ gfxWindowsPlatform::InitD3D11Devices() (status == D3D11Status::TryWARP || status == D3D11Status::ForceWARP)) { AttemptWARPDeviceCreation(featureLevels); + mD3D11Status = FeatureStatus::Failed; } if (!mD3D11Device) { @@ -2106,6 +2131,7 @@ gfxWindowsPlatform::InitD3D11Devices() } mD3D11Device->SetExceptionMode(0); + mD3D11Status = FeatureStatus::Available; // We create our device for D2D content drawing here. Normally we don't use // D2D content drawing when using WARP. However when WARP is forced by @@ -2400,3 +2426,32 @@ gfxWindowsPlatform::GetAcceleratedCompositorBackends(nsTArray& aB } } } + +FeatureStatus +gfxWindowsPlatform::GetD2D1Status() +{ + if (GetD2DStatus() != FeatureStatus::Available || + !Factory::SupportsD2D1()) + { + return FeatureStatus::Unavailable; + } + + if (!GetD3D11ContentDevice()) { + return FeatureStatus::Failed; + } + + if (!gfxPrefs::Direct2DUse1_1()) { + return FeatureStatus::Disabled; + } + return FeatureStatus::Available; +} + +unsigned +gfxWindowsPlatform::GetD3D11Version() +{ + ID3D11Device* device = GetD3D11Device(); + if (!device) { + return 0; + } + return device->GetFeatureLevel(); +} diff --git a/gfx/thebes/gfxWindowsPlatform.h b/gfx/thebes/gfxWindowsPlatform.h index 24438eb4f1..3cfcc85dce 100644 --- a/gfx/thebes/gfxWindowsPlatform.h +++ b/gfx/thebes/gfxWindowsPlatform.h @@ -20,6 +20,7 @@ #include "gfxDWriteFonts.h" #endif #include "gfxPlatform.h" +#include "gfxTelemetry.h" #include "gfxTypes.h" #include "mozilla/Attributes.h" #include "mozilla/Atomics.h" @@ -243,6 +244,7 @@ public: ID3D10Device1 *GetD3D10Device() { return mD3D10Device; } ID3D11Device *GetD3D11Device(); ID3D11Device *GetD3D11ContentDevice(); + ID3D11Device* GetD3D11DeviceForCurrentThread(); // Device to be used on the ImageBridge thread ID3D11Device *GetD3D11ImageBridgeDevice(); @@ -261,6 +263,18 @@ public: } bool SupportsApzTouchInput() const override; + // Return the diagnostic status of DirectX initialization. If + // initialization has not been attempted, this returns + // FeatureStatus::Unused. + mozilla::gfx::FeatureStatus GetD3D11Status() const { + return mD3D11Status; + } + mozilla::gfx::FeatureStatus GetD2DStatus() const { + return mD2DStatus; + } + unsigned GetD3D11Version(); + mozilla::gfx::FeatureStatus GetD2D1Status(); + virtual already_AddRefed CreateHardwareVsyncSource() override; static mozilla::Atomic sD3D11MemoryUsed; static mozilla::Atomic sD3D9MemoryUsed; @@ -298,7 +312,7 @@ private: bool AttemptD3D11ContentDeviceCreation(const nsTArray& aFeatureLevels); // Used by UpdateRenderMode(). - void InitD2DSupport(); + mozilla::gfx::FeatureStatus InitD2DSupport(); void InitDWriteSupport(); IDXGIAdapter1 *GetDXGIAdapter(); @@ -326,6 +340,9 @@ private: bool mDoesD3D11TextureSharingWork; DeviceResetReason mDeviceResetReason; + mozilla::gfx::FeatureStatus mD3D11Status; + mozilla::gfx::FeatureStatus mD2DStatus; + virtual void GetPlatformCMSOutputProfile(void* &mem, size_t &size); }; diff --git a/ipc/chromium/src/base/message_loop.cc b/ipc/chromium/src/base/message_loop.cc index c1602657c2..3c9316155a 100644 --- a/ipc/chromium/src/base/message_loop.cc +++ b/ipc/chromium/src/base/message_loop.cc @@ -32,6 +32,7 @@ #endif #ifdef MOZ_TASK_TRACER #include "GeckoTaskTracer.h" +#include "TracedTaskCommon.h" #endif #include "MessagePump.h" @@ -287,6 +288,7 @@ void MessageLoop::PostIdleTask( #ifdef MOZ_TASK_TRACER task = mozilla::tasktracer::CreateTracedTask(task); + (static_cast(task))->DispatchTask(); #endif task->SetBirthPlace(from_here); @@ -301,6 +303,7 @@ void MessageLoop::PostTask_Helper( #ifdef MOZ_TASK_TRACER task = mozilla::tasktracer::CreateTracedTask(task); + (static_cast(task))->DispatchTask(delay_ms); #endif task->SetBirthPlace(from_here); diff --git a/layout/base/nsPresShell.cpp b/layout/base/nsPresShell.cpp index 9a2ce8a5a3..fd04ffdce7 100644 --- a/layout/base/nsPresShell.cpp +++ b/layout/base/nsPresShell.cpp @@ -6974,11 +6974,11 @@ PresShell::HandleEvent(nsIFrame* aFrame, // Make touch events, mouse events and hardware key events to be the source // events of TaskTracer, and originate the rest correlation tasks from here. SourceEventType type = SourceEventType::Unknown; - if (WidgetTouchEvent* inputEvent = aEvent->AsTouchEvent()) { + if (aEvent->AsTouchEvent()) { type = SourceEventType::Touch; - } else if (WidgetMouseEvent* inputEvent = aEvent->AsMouseEvent()) { + } else if (aEvent->AsMouseEvent()) { type = SourceEventType::Mouse; - } else if (WidgetKeyboardEvent* inputEvent = aEvent->AsKeyboardEvent()) { + } else if (aEvent->AsKeyboardEvent()) { type = SourceEventType::Key; } AutoSourceEvent taskTracerEvent(type); diff --git a/media/mtransport/runnable_utils.h b/media/mtransport/runnable_utils.h index 465c42e289..3733fcbeb1 100644 --- a/media/mtransport/runnable_utils.h +++ b/media/mtransport/runnable_utils.h @@ -44,7 +44,7 @@ RunOnThreadInternal(nsIEventTarget *thread, nsIRunnable *runnable, uint32_t flag return rv; } if (!on) { - return thread->Dispatch(runnable_ref, flags); + return thread->Dispatch(runnable_ref.forget(), flags); } } return runnable_ref->Run(); diff --git a/netwerk/base/nsSocketTransportService2.cpp b/netwerk/base/nsSocketTransportService2.cpp index 566bd07c95..f379d96c2c 100644 --- a/netwerk/base/nsSocketTransportService2.cpp +++ b/netwerk/base/nsSocketTransportService2.cpp @@ -114,13 +114,21 @@ nsSocketTransportService::GetThreadSafely() } NS_IMETHODIMP -nsSocketTransportService::Dispatch(nsIRunnable *event, uint32_t flags) +nsSocketTransportService::DispatchFromScript(nsIRunnable *event, uint32_t flags) { - SOCKET_LOG(("STS dispatch [%p]\n", event)); + nsCOMPtr event_ref(event); + return Dispatch(event_ref.forget(), flags); +} + +NS_IMETHODIMP +nsSocketTransportService::Dispatch(already_AddRefed&& event, uint32_t flags) +{ + nsCOMPtr event_ref(event); + SOCKET_LOG(("STS dispatch [%p]\n", event_ref.get())); nsCOMPtr thread = GetThreadSafely(); nsresult rv; - rv = thread ? thread->Dispatch(event, flags) : NS_ERROR_NOT_INITIALIZED; + rv = thread ? thread->Dispatch(event_ref.forget(), flags) : NS_ERROR_NOT_INITIALIZED; if (rv == NS_ERROR_UNEXPECTED) { // Thread is no longer accepting events. We must have just shut it // down on the main thread. Pretend we never saw it. diff --git a/netwerk/base/nsSocketTransportService2.h b/netwerk/base/nsSocketTransportService2.h index 83b266bcfc..7c54fceb1e 100644 --- a/netwerk/base/nsSocketTransportService2.h +++ b/netwerk/base/nsSocketTransportService2.h @@ -79,6 +79,10 @@ public: NS_DECL_NSITHREADOBSERVER NS_DECL_NSIRUNNABLE NS_DECL_NSIOBSERVER + // missing from NS_DECL_NSIEVENTTARGET because MSVC + nsresult Dispatch(nsIRunnable* aEvent, uint32_t aFlags) { + return Dispatch(nsCOMPtr(aEvent).forget(), aFlags); + } nsSocketTransportService(); diff --git a/netwerk/base/nsStreamTransportService.cpp b/netwerk/base/nsStreamTransportService.cpp index 78b46fcbc6..b506c6160b 100644 --- a/netwerk/base/nsStreamTransportService.cpp +++ b/netwerk/base/nsStreamTransportService.cpp @@ -502,8 +502,16 @@ NS_IMPL_ISUPPORTS(nsStreamTransportService, nsIObserver) NS_IMETHODIMP -nsStreamTransportService::Dispatch(nsIRunnable *task, uint32_t flags) +nsStreamTransportService::DispatchFromScript(nsIRunnable *task, uint32_t flags) { + nsCOMPtr event(task); + return Dispatch(event.forget(), flags); +} + +NS_IMETHODIMP +nsStreamTransportService::Dispatch(already_AddRefed&& task, uint32_t flags) +{ + nsCOMPtr event(task); // so it gets released on failure paths nsCOMPtr pool; { mozilla::MutexAutoLock lock(mShutdownLock); @@ -513,7 +521,7 @@ nsStreamTransportService::Dispatch(nsIRunnable *task, uint32_t flags) pool = mPool; } NS_ENSURE_TRUE(pool, NS_ERROR_NOT_INITIALIZED); - return pool->Dispatch(task, flags); + return pool->Dispatch(event.forget(), flags); } NS_IMETHODIMP diff --git a/netwerk/base/nsStreamTransportService.h b/netwerk/base/nsStreamTransportService.h index f52877295e..723bb3df9a 100644 --- a/netwerk/base/nsStreamTransportService.h +++ b/netwerk/base/nsStreamTransportService.h @@ -21,6 +21,10 @@ public: NS_DECL_NSISTREAMTRANSPORTSERVICE NS_DECL_NSIEVENTTARGET NS_DECL_NSIOBSERVER + // missing from NS_DECL_NSIEVENTTARGET because MSVC + nsresult Dispatch(nsIRunnable* aEvent, uint32_t aFlags) { + return Dispatch(nsCOMPtr(aEvent).forget(), aFlags); + } nsresult Init(); diff --git a/netwerk/sctp/datachannel/DataChannel.cpp b/netwerk/sctp/datachannel/DataChannel.cpp index 15880c20d0..73d6295b96 100644 --- a/netwerk/sctp/datachannel/DataChannel.cpp +++ b/netwerk/sctp/datachannel/DataChannel.cpp @@ -476,9 +476,9 @@ DataChannelConnection::StartDefer() { nsresult rv; if (!NS_IsMainThread()) { - NS_DispatchToMainThread(new DataChannelOnMessageAvailable( - DataChannelOnMessageAvailable::START_DEFER, - this, (DataChannel *) nullptr)); + NS_DispatchToMainThread(do_AddRef(new DataChannelOnMessageAvailable( + DataChannelOnMessageAvailable::START_DEFER, + this, (DataChannel *) nullptr))); return; } @@ -615,9 +615,9 @@ DataChannelConnection::CompleteConnect(TransportFlow *flow, TransportLayer::Stat } } // Note: currently this doesn't actually notify the application - NS_DispatchToMainThread(new DataChannelOnMessageAvailable( - DataChannelOnMessageAvailable::ON_CONNECTION, - this)); + NS_DispatchToMainThread(do_AddRef(new DataChannelOnMessageAvailable( + DataChannelOnMessageAvailable::ON_CONNECTION, + this))); return; } @@ -713,7 +713,7 @@ DataChannelConnection::SctpDtlsOutput(void *addr, void *buffer, size_t length, peer->mSTS->Dispatch(WrapRunnable( nsRefPtr(peer), &DataChannelConnection::SendPacket, data, length, true), - NS_DISPATCH_NORMAL); + NS_DISPATCH_NORMAL); res = 0; // cheat! Packets can always be dropped later anyways } return res; @@ -774,9 +774,9 @@ DataChannelConnection::Listen(unsigned short port) // Notify Connection open // XXX We need to make sure connection sticks around until the message is delivered LOG(("%s: sending ON_CONNECTION for %p", __FUNCTION__, this)); - NS_DispatchToMainThread(new DataChannelOnMessageAvailable( + NS_DispatchToMainThread(do_AddRef(new DataChannelOnMessageAvailable( DataChannelOnMessageAvailable::ON_CONNECTION, - this, (DataChannel *) nullptr)); + this, (DataChannel *) nullptr))); return true; } @@ -852,9 +852,9 @@ DataChannelConnection::Connect(const char *addr, unsigned short port) // Notify Connection open // XXX We need to make sure connection sticks around until the message is delivered LOG(("%s: sending ON_CONNECTION for %p", __FUNCTION__, this)); - NS_DispatchToMainThread(new DataChannelOnMessageAvailable( + NS_DispatchToMainThread(do_AddRef(new DataChannelOnMessageAvailable( DataChannelOnMessageAvailable::ON_CONNECTION, - this, (DataChannel *) nullptr)); + this, (DataChannel *) nullptr))); return true; } #endif @@ -1053,9 +1053,9 @@ DataChannelConnection::SendDeferredMessages() channel->mState = OPEN; channel->mReady = true; LOG(("%s: sending ON_CHANNEL_OPEN for %p", __FUNCTION__, channel.get())); - NS_DispatchToMainThread(new DataChannelOnMessageAvailable( + NS_DispatchToMainThread(do_AddRef(new DataChannelOnMessageAvailable( DataChannelOnMessageAvailable::ON_CHANNEL_OPEN, this, - channel)); + channel))); sent = true; } else { if (errno == EAGAIN || errno == EWOULDBLOCK) { @@ -1065,9 +1065,9 @@ DataChannelConnection::SendDeferredMessages() mStreams[channel->mStream] = nullptr; channel->mState = CLOSED; // Don't need to reset; we didn't open it - NS_DispatchToMainThread(new DataChannelOnMessageAvailable( + NS_DispatchToMainThread(do_AddRef(new DataChannelOnMessageAvailable( DataChannelOnMessageAvailable::ON_CHANNEL_CLOSED, this, - channel)); + channel))); } } } @@ -1233,9 +1233,9 @@ DataChannelConnection::HandleOpenRequestMessage(const struct rtcweb_datachannel_ LOG(("%s: sending ON_CHANNEL_CREATED for %s/%s: %u (state %u)", __FUNCTION__, channel->mLabel.get(), channel->mProtocol.get(), stream, channel->mState)); - NS_DispatchToMainThread(new DataChannelOnMessageAvailable( + NS_DispatchToMainThread(do_AddRef(new DataChannelOnMessageAvailable( DataChannelOnMessageAvailable::ON_CHANNEL_CREATED, - this, channel)); + this, channel))); LOG(("%s: deferring sending ON_CHANNEL_OPEN for %p", __FUNCTION__, channel.get())); @@ -1463,9 +1463,9 @@ DataChannelConnection::HandleAssociationChangeEvent(const struct sctp_assoc_chan SetEvenOdd(); - NS_DispatchToMainThread(new DataChannelOnMessageAvailable( + NS_DispatchToMainThread(do_AddRef(new DataChannelOnMessageAvailable( DataChannelOnMessageAvailable::ON_CONNECTION, - this)); + this))); LOG(("DTLS connect() succeeded! Entering connected mode")); // Open any streams pending... @@ -1480,18 +1480,18 @@ DataChannelConnection::HandleAssociationChangeEvent(const struct sctp_assoc_chan case SCTP_COMM_LOST: LOG(("Association change: SCTP_COMM_LOST")); // This association is toast, so also close all the channels -- from mainthread! - NS_DispatchToMainThread(new DataChannelOnMessageAvailable( + NS_DispatchToMainThread(do_AddRef(new DataChannelOnMessageAvailable( DataChannelOnMessageAvailable::ON_DISCONNECTED, - this)); + this))); break; case SCTP_RESTART: LOG(("Association change: SCTP_RESTART")); break; case SCTP_SHUTDOWN_COMP: LOG(("Association change: SCTP_SHUTDOWN_COMP")); - NS_DispatchToMainThread(new DataChannelOnMessageAvailable( + NS_DispatchToMainThread(do_AddRef(new DataChannelOnMessageAvailable( DataChannelOnMessageAvailable::ON_DISCONNECTED, - this)); + this))); break; case SCTP_CANT_STR_ASSOC: LOG(("Association change: SCTP_CANT_STR_ASSOC")); @@ -1754,9 +1754,9 @@ DataChannelConnection::HandleStreamResetEvent(const struct sctp_stream_reset_eve // Mark the stream for reset (the reset is sent below) ResetOutgoingStream(channel->mStream); } - NS_DispatchToMainThread(new DataChannelOnMessageAvailable( + NS_DispatchToMainThread(do_AddRef(new DataChannelOnMessageAvailable( DataChannelOnMessageAvailable::ON_CHANNEL_CLOSED, this, - channel)); + channel))); mStreams[channel->mStream] = nullptr; LOG(("Disconnected DataChannel %p from connection %p", @@ -1847,9 +1847,9 @@ DataChannelConnection::HandleStreamChangeEvent(const struct sctp_stream_change_e (strchg->strchange_flags & SCTP_STREAM_CHANGE_FAILED)) { /* XXX: Signal to the other end. */ channel->mState = CLOSED; - NS_DispatchToMainThread(new DataChannelOnMessageAvailable( + NS_DispatchToMainThread(do_AddRef(new DataChannelOnMessageAvailable( DataChannelOnMessageAvailable::ON_CHANNEL_CLOSED, this, - channel)); + channel))); // maybe fire onError (bug 843625) } else { stream = FindFreeStream(); @@ -2130,9 +2130,9 @@ DataChannelConnection::OpenFinish(already_AddRefed&& aChannel) if (channel->mFlags & DATA_CHANNEL_FLAGS_FINISH_OPEN) { // We already returned the channel to the app. NS_ERROR("Failed to send open request"); - NS_DispatchToMainThread(new DataChannelOnMessageAvailable( + NS_DispatchToMainThread(do_AddRef(new DataChannelOnMessageAvailable( DataChannelOnMessageAvailable::ON_CHANNEL_CLOSED, this, - channel)); + channel))); } // If we haven't returned the channel yet, it will get destroyed when we exit // this function. @@ -2150,9 +2150,9 @@ DataChannelConnection::OpenFinish(already_AddRefed&& aChannel) channel->mReady = true; // FIX? Move into DOMDataChannel? I don't think we can send it yet here LOG(("%s: sending ON_CHANNEL_OPEN for %p", __FUNCTION__, channel.get())); - NS_DispatchToMainThread(new DataChannelOnMessageAvailable( + NS_DispatchToMainThread(do_AddRef(new DataChannelOnMessageAvailable( DataChannelOnMessageAvailable::ON_CHANNEL_OPEN, this, - channel)); + channel))); return channel.forget(); @@ -2161,9 +2161,9 @@ request_error_cleanup: if (channel->mFlags & DATA_CHANNEL_FLAGS_FINISH_OPEN) { // We already returned the channel to the app. NS_ERROR("Failed to request more streams"); - NS_DispatchToMainThread(new DataChannelOnMessageAvailable( + NS_DispatchToMainThread(do_AddRef(new DataChannelOnMessageAvailable( DataChannelOnMessageAvailable::ON_CHANNEL_CLOSED, this, - channel)); + channel))); return channel.forget(); } // we'll be destroying the channel, but it never really got set up @@ -2330,8 +2330,7 @@ DataChannelConnection::SendBlob(uint16_t stream, nsIInputStream *aBlob) } } - nsCOMPtr runnable = new ReadBlobRunnable(this, stream, aBlob); - mInternalIOThread->Dispatch(runnable, NS_DISPATCH_NORMAL); + mInternalIOThread->Dispatch(do_AddRef(new ReadBlobRunnable(this, stream, aBlob)), NS_DISPATCH_NORMAL); return 0; } @@ -2580,9 +2579,9 @@ DataChannel::AppReady() mReady = true; if (mState == WAITING_TO_OPEN) { mState = OPEN; - NS_DispatchToMainThread(new DataChannelOnMessageAvailable( + NS_DispatchToMainThread(do_AddRef(new DataChannelOnMessageAvailable( DataChannelOnMessageAvailable::ON_CHANNEL_OPEN, mConnection, - this)); + this))); for (uint32_t i = 0; i < mQueuedMessages.Length(); ++i) { nsCOMPtr runnable = mQueuedMessages[i]; MOZ_ASSERT(runnable); diff --git a/toolkit/components/finalizationwitness/FinalizationWitnessService.cpp b/toolkit/components/finalizationwitness/FinalizationWitnessService.cpp index 2851f48200..9e7d3ff89d 100644 --- a/toolkit/components/finalizationwitness/FinalizationWitnessService.cpp +++ b/toolkit/components/finalizationwitness/FinalizationWitnessService.cpp @@ -106,7 +106,10 @@ void Finalize(JSFreeOp *fop, JSObject *objSelf) // Notify observers. Since we are executed during garbage-collection, // we need to dispatch the notification to the main thread. - (void)NS_DispatchToMainThread(event); + nsCOMPtr mainThread = do_GetMainThread(); + if (mainThread) { + mainThread->Dispatch(event.forget(), NS_DISPATCH_NORMAL); + } // We may fail at dispatching to the main thread if we arrive too late // during shutdown. In that case, there is not much we can do. } diff --git a/toolkit/components/telemetry/docs/environment.rst b/toolkit/components/telemetry/docs/environment.rst index dda14693b3..f6bfbc0e86 100644 --- a/toolkit/components/telemetry/docs/environment.rst +++ b/toolkit/components/telemetry/docs/environment.rst @@ -101,7 +101,7 @@ Structure:: gfx: { D2DEnabled: , // null on failure DWriteEnabled: , // null on failure - DWriteVersion: , // null on failure + //DWriteVersion: , // temporarily removed, pending bug 1154500 adapters: [ { description: , // e.g. "Intel(R) HD Graphics 4600", null on failure @@ -116,6 +116,28 @@ Structure:: }, ... ], + features: { + compositor: , // Layers backend for compositing (eg "d3d11", "none", "opengl") + + // Each the following features can have one of the following statuses: + // "unused" - This feature has not been requested. + // "unavailable" - Safe Mode or OS restriction prevents use. + // "blocked" - Blocked due to an internal condition such as safe mode. + // "blacklisted" - Blocked due to a blacklist restriction. + // "disabled" - User explicitly disabled this default feature. + // "failed" - This feature was attempted but failed to initialize. + // "available" - User has this feature available. + "d3d11" { // This feature is Windows-only. + status: , + warp: , // Software rendering (WARP) mode was chosen. + textureSharing: // Whether or not texture sharing works. + version: , // The D3D11 device feature level. + }, + "d2d" { // This feature is Windows-only. + status: , + version: , // Either "1.0" or "1.1". + }, + }, }, }, addons: { diff --git a/tools/profiler/GeckoTaskTracer.cpp b/tools/profiler/GeckoTaskTracer.cpp index 31cbd3d80b..02927a7138 100644 --- a/tools/profiler/GeckoTaskTracer.cpp +++ b/tools/profiler/GeckoTaskTracer.cpp @@ -7,6 +7,7 @@ #include "GeckoTaskTracer.h" #include "GeckoTaskTracerImpl.h" +#include "mozilla/MathAlgorithms.h" #include "mozilla/StaticMutex.h" #include "mozilla/ThreadLocal.h" #include "mozilla/TimeStamp.h" @@ -27,13 +28,32 @@ static pid_t gettid() } #endif +// NS_ENSURE_TRUE_VOID() without the warning on the debug build. +#define ENSURE_TRUE_VOID(x) \ + do { \ + if (MOZ_UNLIKELY(!(x))) { \ + return; \ + } \ + } while(0) + +// NS_ENSURE_TRUE() without the warning on the debug build. +#define ENSURE_TRUE(x, ret) \ + do { \ + if (MOZ_UNLIKELY(!(x))) { \ + return ret; \ + } \ + } while(0) + namespace mozilla { namespace tasktracer { -static mozilla::ThreadLocal* sTraceInfoTLS = nullptr; +static mozilla::ThreadLocal sTraceInfoTLS; static mozilla::StaticMutex sMutex; + +// The generation of TraceInfo. It will be > 0 if the Task Tracer is started and +// <= 0 if stopped. +static mozilla::Atomic sStarted; static nsTArray>* sTraceInfos = nullptr; -static bool sIsLoggingStarted = false; static PRTime sStartTime; static const char sJSLabelPrefix[] = "#tt#"; @@ -52,24 +72,16 @@ AllocTraceInfo(int aTid) StaticMutexAutoLock lock(sMutex); nsAutoPtr* info = sTraceInfos->AppendElement( - new TraceInfo(aTid, sIsLoggingStarted)); + new TraceInfo(aTid)); return info->get(); } -static bool -IsInitialized() -{ - return sTraceInfoTLS ? sTraceInfoTLS->initialized() : false; -} - static void SaveCurTraceInfo() { TraceInfo* info = GetOrCreateTraceInfo(); - if (!info) { - return; - } + ENSURE_TRUE_VOID(info); info->mSavedCurTraceSourceId = info->mCurTraceSourceId; info->mSavedCurTraceSourceType = info->mCurTraceSourceType; @@ -80,9 +92,7 @@ static void RestoreCurTraceInfo() { TraceInfo* info = GetOrCreateTraceInfo(); - if (!info) { - return; - } + ENSURE_TRUE_VOID(info); info->mCurTraceSourceId = info->mSavedCurTraceSourceId; info->mCurTraceSourceType = info->mSavedCurTraceSourceType; @@ -92,14 +102,14 @@ RestoreCurTraceInfo() static void CreateSourceEvent(SourceEventType aType) { - NS_ENSURE_TRUE_VOID(IsInitialized()); - // Save the currently traced source event info. SaveCurTraceInfo(); // Create a new unique task id. uint64_t newId = GenNewUniqueTaskId(); TraceInfo* info = GetOrCreateTraceInfo(); + ENSURE_TRUE_VOID(info); + info->mCurTraceSourceId = newId; info->mCurTraceSourceType = aType; info->mCurTaskId = newId; @@ -129,54 +139,57 @@ CreateSourceEvent(SourceEventType aType) static void DestroySourceEvent() { - NS_ENSURE_TRUE_VOID(IsInitialized()); - // Log a fake end for this source event. TraceInfo* info = GetOrCreateTraceInfo(); + ENSURE_TRUE_VOID(info); + LogEnd(info->mCurTraceSourceId, info->mCurTraceSourceId); // Restore the previously saved source event info. RestoreCurTraceInfo(); } +inline static bool +IsStartLogging() +{ + return sStarted; +} + +static void +SetLogStarted(bool aIsStartLogging) +{ + MOZ_ASSERT(aIsStartLogging != IsStartLogging()); + sStarted = aIsStartLogging; + + StaticMutexAutoLock lock(sMutex); + if (!aIsStartLogging) { + for (uint32_t i = 0; i < sTraceInfos->Length(); ++i) { + (*sTraceInfos)[i]->mObsolete = true; + } + } +} + static void CleanUp() { + SetLogStarted(false); StaticMutexAutoLock lock(sMutex); if (sTraceInfos) { delete sTraceInfos; sTraceInfos = nullptr; } - - // pthread_key_delete() is not called at the destructor of - // mozilla::ThreadLocal (Bug 1064672). - if (sTraceInfoTLS) { - delete sTraceInfoTLS; - sTraceInfoTLS = nullptr; - } } -static void -SetLogStarted(bool aIsStartLogging) +inline static void +ObsoleteCurrentTraceInfos() { - // TODO: This is called from a signal handler. Use semaphore instead. - StaticMutexAutoLock lock(sMutex); - + // Note that we can't and don't need to acquire sMutex here because this + // function is called before the other threads are recreated. for (uint32_t i = 0; i < sTraceInfos->Length(); ++i) { - (*sTraceInfos)[i]->mStartLogging = aIsStartLogging; + (*sTraceInfos)[i]->mObsolete = true; } - - sIsLoggingStarted = aIsStartLogging; } - -static bool -IsStartLogging(TraceInfo* aInfo) -{ - StaticMutexAutoLock lock(sMutex); - return aInfo ? aInfo->mStartLogging : false; -} - } // namespace anonymous nsCString* @@ -197,17 +210,15 @@ void InitTaskTracer(uint32_t aFlags) { if (aFlags & FORKED_AFTER_NUWA) { - CleanUp(); + ObsoleteCurrentTraceInfos(); + return; } - MOZ_ASSERT(!sTraceInfoTLS); - sTraceInfoTLS = new ThreadLocal(); - MOZ_ASSERT(!sTraceInfos); sTraceInfos = new nsTArray>(); - if (!sTraceInfoTLS->initialized()) { - unused << sTraceInfoTLS->init(); + if (!sTraceInfoTLS.initialized()) { + unused << sTraceInfoTLS.init(); } } @@ -217,15 +228,36 @@ ShutdownTaskTracer() CleanUp(); } +static void +FreeTraceInfo(TraceInfo* aTraceInfo) +{ + StaticMutexAutoLock lock(sMutex); + if (aTraceInfo) { + sTraceInfos->RemoveElement(aTraceInfo); + } +} + +void FreeTraceInfo() +{ + FreeTraceInfo(sTraceInfoTLS.get()); +} + TraceInfo* GetOrCreateTraceInfo() { - NS_ENSURE_TRUE(IsInitialized(), nullptr); + ENSURE_TRUE(sTraceInfoTLS.initialized(), nullptr); + ENSURE_TRUE(IsStartLogging(), nullptr); + + TraceInfo* info = sTraceInfoTLS.get(); + if (info && info->mObsolete) { + // TraceInfo is obsolete: remove it. + FreeTraceInfo(info); + info = nullptr; + } - TraceInfo* info = sTraceInfoTLS->get(); if (!info) { info = AllocTraceInfo(gettid()); - sTraceInfoTLS->set(info); + sTraceInfoTLS.set(info); } return info; @@ -235,7 +267,7 @@ uint64_t GenNewUniqueTaskId() { TraceInfo* info = GetOrCreateTraceInfo(); - NS_ENSURE_TRUE(info, 0); + ENSURE_TRUE(info, 0); pid_t tid = gettid(); uint64_t taskid = ((uint64_t)tid << 32) | ++info->mLastUniqueTaskId; @@ -257,7 +289,7 @@ SetCurTraceInfo(uint64_t aSourceEventId, uint64_t aParentTaskId, SourceEventType aSourceEventType) { TraceInfo* info = GetOrCreateTraceInfo(); - NS_ENSURE_TRUE_VOID(info); + ENSURE_TRUE_VOID(info); info->mCurTraceSourceId = aSourceEventId; info->mCurTaskId = aParentTaskId; @@ -269,7 +301,7 @@ GetCurTraceInfo(uint64_t* aOutSourceEventId, uint64_t* aOutParentTaskId, SourceEventType* aOutSourceEventType) { TraceInfo* info = GetOrCreateTraceInfo(); - NS_ENSURE_TRUE_VOID(info); + ENSURE_TRUE_VOID(info); *aOutSourceEventId = info->mCurTraceSourceId; *aOutParentTaskId = info->mCurTaskId; @@ -279,18 +311,28 @@ GetCurTraceInfo(uint64_t* aOutSourceEventId, uint64_t* aOutParentTaskId, void LogDispatch(uint64_t aTaskId, uint64_t aParentTaskId, uint64_t aSourceEventId, SourceEventType aSourceEventType) +{ + LogDispatch(aTaskId, aParentTaskId, aSourceEventId, aSourceEventType, 0); +} + +void +LogDispatch(uint64_t aTaskId, uint64_t aParentTaskId, uint64_t aSourceEventId, + SourceEventType aSourceEventType, int aDelayTimeMs) { TraceInfo* info = GetOrCreateTraceInfo(); - if (!IsStartLogging(info)) { - return; - } + ENSURE_TRUE_VOID(info); + + // aDelayTimeMs is the expected delay time in milliseconds, thus the dispatch + // time calculated of it might be slightly off in the real world. + uint64_t time = (aDelayTimeMs <= 0) ? GetTimestamp() : + GetTimestamp() + aDelayTimeMs; // Log format: // [0 taskId dispatchTime sourceEventId sourceEventType parentTaskId] nsCString* log = info->AppendLog(); if (log) { log->AppendPrintf("%d %lld %lld %lld %d %lld", - ACTION_DISPATCH, aTaskId, GetTimestamp(), aSourceEventId, + ACTION_DISPATCH, aTaskId, time, aSourceEventId, aSourceEventType, aParentTaskId); } } @@ -299,9 +341,7 @@ void LogBegin(uint64_t aTaskId, uint64_t aSourceEventId) { TraceInfo* info = GetOrCreateTraceInfo(); - if (!IsStartLogging(info)) { - return; - } + ENSURE_TRUE_VOID(info); // Log format: // [1 taskId beginTime processId threadId] @@ -316,9 +356,7 @@ void LogEnd(uint64_t aTaskId, uint64_t aSourceEventId) { TraceInfo* info = GetOrCreateTraceInfo(); - if (!IsStartLogging(info)) { - return; - } + ENSURE_TRUE_VOID(info); // Log format: // [2 taskId endTime] @@ -332,9 +370,7 @@ void LogVirtualTablePtr(uint64_t aTaskId, uint64_t aSourceEventId, int* aVptr) { TraceInfo* info = GetOrCreateTraceInfo(); - if (!IsStartLogging(info)) { - return; - } + ENSURE_TRUE_VOID(info); // Log format: // [4 taskId address] @@ -344,18 +380,6 @@ LogVirtualTablePtr(uint64_t aTaskId, uint64_t aSourceEventId, int* aVptr) } } -void -FreeTraceInfo() -{ - NS_ENSURE_TRUE_VOID(IsInitialized()); - - StaticMutexAutoLock lock(sMutex); - TraceInfo* info = GetOrCreateTraceInfo(); - if (info) { - sTraceInfos->RemoveElement(info); - } -} - AutoSourceEvent::AutoSourceEvent(SourceEventType aType) { CreateSourceEvent(aType); @@ -369,9 +393,7 @@ AutoSourceEvent::~AutoSourceEvent() void AddLabel(const char* aFormat, ...) { TraceInfo* info = GetOrCreateTraceInfo(); - if (!IsStartLogging(info)) { - return; - } + ENSURE_TRUE_VOID(info); va_list args; va_start(args, aFormat); @@ -404,7 +426,7 @@ StopLogging() } TraceInfoLogsType* -GetLoggedData() +GetLoggedData(TimeStamp aTimeStamp) { TraceInfoLogsType* result = new TraceInfoLogsType(); @@ -430,5 +452,8 @@ GetJSLabelPrefix() return sJSLabelPrefix; } +#undef ENSURE_TRUE_VOID +#undef ENSURE_TRUE + } // namespace tasktracer } // namespace mozilla diff --git a/tools/profiler/GeckoTaskTracer.h b/tools/profiler/GeckoTaskTracer.h index 5ce8208a5f..acb00e2af0 100644 --- a/tools/profiler/GeckoTaskTracer.h +++ b/tools/profiler/GeckoTaskTracer.h @@ -37,10 +37,6 @@ namespace tasktracer { enum { FORKED_AFTER_NUWA = 1 << 0 }; -void InitTaskTracer(uint32_t aFlags = 0); -void ShutdownTaskTracer(); - -class FakeTracedTask; enum SourceEventType { Unknown = 0, @@ -59,6 +55,9 @@ public: ~AutoSourceEvent(); }; +void InitTaskTracer(uint32_t aFlags = 0); +void ShutdownTaskTracer(); + // Add a label to the currently running task, aFormat is the message to log, // followed by corresponding parameters. void AddLabel(const char* aFormat, ...); @@ -78,8 +77,6 @@ Task* CreateTracedTask(Task* aTask); already_AddRefed CreateTracedRunnable(nsIRunnable* aRunnable); -already_AddRefed CreateFakeTracedTask(int* aVptr); - // Free the TraceInfo allocated on a thread's TLS. Currently we are wrapping // tasks running on nsThreads and base::thread, so FreeTraceInfo is called at // where nsThread and base::thread release themselves. diff --git a/tools/profiler/GeckoTaskTracerImpl.h b/tools/profiler/GeckoTaskTracerImpl.h index c00305cb12..00e54b9769 100644 --- a/tools/profiler/GeckoTaskTracerImpl.h +++ b/tools/profiler/GeckoTaskTracerImpl.h @@ -18,7 +18,7 @@ typedef nsTArray TraceInfoLogsType; struct TraceInfo { - TraceInfo(uint32_t aThreadId, bool aStartLogging) + TraceInfo(uint32_t aThreadId) : mCurTraceSourceId(0) , mCurTaskId(0) , mSavedCurTraceSourceId(0) @@ -27,7 +27,7 @@ struct TraceInfo , mSavedCurTraceSourceType(Unknown) , mThreadId(aThreadId) , mLastUniqueTaskId(0) - , mStartLogging(aStartLogging) + , mObsolete(false) , mLogsMutex("TraceInfoMutex") { MOZ_COUNT_CTOR(TraceInfo); @@ -46,7 +46,7 @@ struct TraceInfo SourceEventType mSavedCurTraceSourceType; uint32_t mThreadId; uint32_t mLastUniqueTaskId; - bool mStartLogging; + mozilla::Atomic mObsolete; // This mutex protects the following log array because MoveLogsInto() might // be called on another thread. @@ -86,6 +86,10 @@ enum ActionType { void LogDispatch(uint64_t aTaskId, uint64_t aParentTaskId, uint64_t aSourceEventId, SourceEventType aSourceEventType); +void LogDispatch(uint64_t aTaskId, uint64_t aParentTaskId, + uint64_t aSourceEventId, SourceEventType aSourceEventType, + int aDelayTimeMs); + void LogBegin(uint64_t aTaskId, uint64_t aSourceEventId); void LogEnd(uint64_t aTaskId, uint64_t aSourceEventId); diff --git a/tools/profiler/TracedTaskCommon.cpp b/tools/profiler/TracedTaskCommon.cpp index 990c931104..1794d2e780 100644 --- a/tools/profiler/TracedTaskCommon.cpp +++ b/tools/profiler/TracedTaskCommon.cpp @@ -7,49 +7,80 @@ #include "GeckoTaskTracerImpl.h" #include "TracedTaskCommon.h" +// NS_ENSURE_TRUE_VOID() without the warning on the debug build. +#define ENSURE_TRUE_VOID(x) \ + do { \ + if (MOZ_UNLIKELY(!(x))) { \ + return; \ + } \ + } while(0) + namespace mozilla { namespace tasktracer { TracedTaskCommon::TracedTaskCommon() - : mSourceEventId(0) - , mSourceEventType(SourceEventType::Unknown) + : mSourceEventType(SourceEventType::Unknown) + , mSourceEventId(0) + , mParentTaskId(0) + , mTaskId(0) + , mIsTraceInfoInit(false) +{ +} + +TracedTaskCommon::~TracedTaskCommon() { - Init(); } void TracedTaskCommon::Init() { TraceInfo* info = GetOrCreateTraceInfo(); - NS_ENSURE_TRUE_VOID(info); + ENSURE_TRUE_VOID(info); mTaskId = GenNewUniqueTaskId(); mSourceEventId = info->mCurTraceSourceId; mSourceEventType = info->mCurTraceSourceType; - - LogDispatch(mTaskId, info->mCurTaskId, mSourceEventId, mSourceEventType); + mParentTaskId = info->mCurTaskId; + mIsTraceInfoInit = true; } void -TracedTaskCommon::SetTraceInfo() +TracedTaskCommon::DispatchTask(int aDelayTimeMs) { - TraceInfo* info = GetOrCreateTraceInfo(); - if (!info) { - return; - } - - info->mCurTraceSourceId = mSourceEventId; - info->mCurTraceSourceType = mSourceEventType; - info->mCurTaskId = mTaskId; + LogDispatch(mTaskId, mParentTaskId, mSourceEventId, mSourceEventType, + aDelayTimeMs); } void -TracedTaskCommon::ClearTraceInfo() +TracedTaskCommon::GetTLSTraceInfo() { TraceInfo* info = GetOrCreateTraceInfo(); - if (!info) { - return; + ENSURE_TRUE_VOID(info); + + mSourceEventType = info->mCurTraceSourceType; + mSourceEventId = info->mCurTraceSourceId; + mTaskId = info->mCurTaskId; + mIsTraceInfoInit = true; +} + +void +TracedTaskCommon::SetTLSTraceInfo() +{ + TraceInfo* info = GetOrCreateTraceInfo(); + ENSURE_TRUE_VOID(info); + + if (mIsTraceInfoInit) { + info->mCurTraceSourceId = mSourceEventId; + info->mCurTraceSourceType = mSourceEventType; + info->mCurTaskId = mTaskId; } +} + +void +TracedTaskCommon::ClearTLSTraceInfo() +{ + TraceInfo* info = GetOrCreateTraceInfo(); + ENSURE_TRUE_VOID(info); info->mCurTraceSourceId = 0; info->mCurTraceSourceType = SourceEventType::Unknown; @@ -63,19 +94,23 @@ TracedRunnable::TracedRunnable(nsIRunnable* aOriginalObj) : TracedTaskCommon() , mOriginalObj(aOriginalObj) { + Init(); LogVirtualTablePtr(mTaskId, mSourceEventId, *(int**)(aOriginalObj)); } +TracedRunnable::~TracedRunnable() +{ +} + NS_IMETHODIMP TracedRunnable::Run() { + SetTLSTraceInfo(); LogBegin(mTaskId, mSourceEventId); - - SetTraceInfo(); nsresult rv = mOriginalObj->Run(); - ClearTraceInfo(); - LogEnd(mTaskId, mSourceEventId); + ClearTLSTraceInfo(); + return rv; } @@ -86,54 +121,26 @@ TracedTask::TracedTask(Task* aOriginalObj) : TracedTaskCommon() , mOriginalObj(aOriginalObj) { + Init(); LogVirtualTablePtr(mTaskId, mSourceEventId, *(int**)(aOriginalObj)); } +TracedTask::~TracedTask() +{ + if (mOriginalObj) { + delete mOriginalObj; + mOriginalObj = nullptr; + } +} + void TracedTask::Run() { + SetTLSTraceInfo(); LogBegin(mTaskId, mSourceEventId); - - SetTraceInfo(); mOriginalObj->Run(); - ClearTraceInfo(); - LogEnd(mTaskId, mSourceEventId); -} - -FakeTracedTask::FakeTracedTask(int* aVptr) - : TracedTaskCommon() -{ - LogVirtualTablePtr(mTaskId, mSourceEventId, aVptr); -} - -void -FakeTracedTask::BeginFakeTracedTask() -{ - LogBegin(mTaskId, mSourceEventId); - SetTraceInfo(); -} - -void -FakeTracedTask::EndFakeTracedTask() -{ - ClearTraceInfo(); - LogEnd(mTaskId, mSourceEventId); -} - -AutoRunFakeTracedTask::AutoRunFakeTracedTask(FakeTracedTask* aFakeTracedTask) - : mFakeTracedTask(aFakeTracedTask) -{ - if (mFakeTracedTask) { - mFakeTracedTask->BeginFakeTracedTask(); - } -} - -AutoRunFakeTracedTask::~AutoRunFakeTracedTask() -{ - if (mFakeTracedTask) { - mFakeTracedTask->EndFakeTracedTask(); - } + ClearTLSTraceInfo(); } /** @@ -158,16 +165,5 @@ CreateTracedTask(Task* aTask) return task; } -/** - * CreateFakeTracedTask() returns a FakeTracedTask tracking the event which is - * not dispatched from its parent task directly, such as timer events. - */ -already_AddRefed -CreateFakeTracedTask(int* aVptr) -{ - nsRefPtr task(new FakeTracedTask(aVptr)); - return task.forget(); -} - } // namespace tasktracer } // namespace mozilla diff --git a/tools/profiler/TracedTaskCommon.h b/tools/profiler/TracedTaskCommon.h index 17bff38cd3..b3c5fae033 100644 --- a/tools/profiler/TracedTaskCommon.h +++ b/tools/profiler/TracedTaskCommon.h @@ -19,22 +19,25 @@ class TracedTaskCommon { public: TracedTaskCommon(); - virtual ~TracedTaskCommon() {} + virtual ~TracedTaskCommon(); + + void DispatchTask(int aDelayTimeMs = 0); + + void SetTLSTraceInfo(); + void GetTLSTraceInfo(); + void ClearTLSTraceInfo(); protected: void Init(); - // Sets up the metadata on the current thread's TraceInfo for this task. - // After Run(), ClearTraceInfo is called to reset the metadata. - void SetTraceInfo(); - void ClearTraceInfo(); - - // Its own task Id, an unique number base on its thread Id and a last unique - // task Id stored in its TraceInfo. - uint64_t mTaskId; - - uint64_t mSourceEventId; + // TraceInfo of TLS will be set by the following parameters, including source + // event type, source event ID, parent task ID, and task ID of this traced + // task/runnable. SourceEventType mSourceEventType; + uint64_t mSourceEventId; + uint64_t mParentTaskId; + uint64_t mTaskId; + bool mIsTraceInfoInit; }; class TracedRunnable : public TracedTaskCommon @@ -46,7 +49,7 @@ public: TracedRunnable(nsIRunnable* aOriginalObj); private: - virtual ~TracedRunnable() {} + virtual ~TracedRunnable(); nsCOMPtr mOriginalObj; }; @@ -56,13 +59,7 @@ class TracedTask : public TracedTaskCommon { public: TracedTask(Task* aOriginalObj); - ~TracedTask() - { - if (mOriginalObj) { - delete mOriginalObj; - mOriginalObj = nullptr; - } - } + ~TracedTask(); virtual void Run(); @@ -70,34 +67,6 @@ private: Task* mOriginalObj; }; -// FakeTracedTask is for tracking events that are not directly dispatched from -// their parents, e.g. The timer events. -class FakeTracedTask : public TracedTaskCommon -{ -public: - NS_INLINE_DECL_REFCOUNTING(FakeTracedTask) - - FakeTracedTask(int* aVptr); - void BeginFakeTracedTask(); - void EndFakeTracedTask(); -private: - virtual ~FakeTracedTask() {} - - // No copy allowed. - FakeTracedTask() = delete; - FakeTracedTask(const FakeTracedTask& aTask) = delete; - FakeTracedTask& operator=(const FakeTracedTask& aTask) = delete; -}; - -class AutoRunFakeTracedTask -{ -public: - AutoRunFakeTracedTask(FakeTracedTask* aFakeTracedTask); - ~AutoRunFakeTracedTask(); -private: - nsRefPtr mFakeTracedTask; -}; - } // namespace tasktracer } // namespace mozilla diff --git a/widget/GfxInfoBase.cpp b/widget/GfxInfoBase.cpp index 42a485962a..fa666f2043 100644 --- a/widget/GfxInfoBase.cpp +++ b/widget/GfxInfoBase.cpp @@ -32,6 +32,8 @@ #include "mozilla/dom/ContentChild.h" #include "mozilla/gfx/2D.h" #include "mozilla/gfx/Logging.h" +#include "gfxPrefs.h" +#include "gfxPlatform.h" using namespace mozilla::widget; using namespace mozilla; @@ -161,7 +163,7 @@ GetPrefValueForFeature(int32_t aFeature, int32_t& aValue) if (!prefname) return false; - aValue = false; + aValue = nsIGfxInfo::FEATURE_STATUS_UNKNOWN; return NS_SUCCEEDED(Preferences::GetInt(prefname, &aValue)); } @@ -307,6 +309,7 @@ BlacklistDevicesToDeviceFamily(nsIDOMHTMLCollection* aDevices) static int32_t BlacklistFeatureToGfxFeature(const nsAString& aFeature) { + MOZ_ASSERT(!aFeature.IsEmpty()); if (aFeature.EqualsLiteral("DIRECT2D")) return nsIGfxInfo::FEATURE_DIRECT2D; else if (aFeature.EqualsLiteral("DIRECT3D_9_LAYERS")) @@ -333,7 +336,13 @@ BlacklistFeatureToGfxFeature(const nsAString& aFeature) return nsIGfxInfo::FEATURE_STAGEFRIGHT; else if (aFeature.EqualsLiteral("WEBRTC_HW_ACCELERATION")) return nsIGfxInfo::FEATURE_WEBRTC_HW_ACCELERATION; - return 0; + + // If we don't recognize the feature, it may be new, and something + // this version doesn't understand. So, nothing to do. This is + // different from feature not being specified at all, in which case + // this method should not get called and we should continue with the + // "all features" blocklisting. + return -1; } static int32_t @@ -521,6 +530,11 @@ BlacklistEntryToDriverInfo(nsIDOMNode* aBlacklistEntry, getter_AddRefs(dataNode))) { BlacklistNodeToTextValue(dataNode, dataValue); aDriverInfo.mFeature = BlacklistFeatureToGfxFeature(dataValue); + if (aDriverInfo.mFeature < 0) { + // If we don't recognize the feature, we do not want to proceed. + gfxWarning() << "Unrecognized feature " << NS_ConvertUTF16toUTF8(dataValue).get(); + return false; + } } // BLOCKED_DRIVER_VERSION @@ -649,6 +663,17 @@ GfxInfoBase::Init() NS_IMETHODIMP GfxInfoBase::GetFeatureStatus(int32_t aFeature, int32_t* aStatus) { + int32_t blocklistAll = gfxPrefs::BlocklistAll(); + if (blocklistAll > 0) { + gfxCriticalErrorOnce(gfxCriticalError::DefaultOptions(false)) << "Forcing blocklisting all features"; + *aStatus = FEATURE_BLOCKED_DEVICE; + return NS_OK; + } else if (blocklistAll < 0) { + gfxCriticalErrorOnce(gfxCriticalError::DefaultOptions(false)) << "Ignoring any feature blocklisting."; + *aStatus = FEATURE_STATUS_OK; + return NS_OK; + } + if (GetPrefValueForFeature(aFeature, *aStatus)) return NS_OK; @@ -1141,6 +1166,92 @@ GfxInfoBase::GetMonitors(JSContext* aCx, JS::MutableHandleValue aResult) return NS_OK; } +static const char* +GetLayersBackendName(layers::LayersBackend aBackend) +{ + switch (aBackend) { + case layers::LayersBackend::LAYERS_NONE: + return "none"; + case layers::LayersBackend::LAYERS_OPENGL: + return "opengl"; + case layers::LayersBackend::LAYERS_D3D9: + return "d3d9"; + case layers::LayersBackend::LAYERS_D3D11: + return "d3d11"; + case layers::LayersBackend::LAYERS_CLIENT: + return "client"; + case layers::LayersBackend::LAYERS_BASIC: + return "basic"; + default: + MOZ_ASSERT_UNREACHABLE("unknown layers backend"); + return "unknown"; + } +} + +nsresult +GfxInfoBase::GetFeatures(JSContext* aCx, JS::MutableHandle aOut) +{ + JS::Rooted obj(aCx, JS_NewPlainObject(aCx)); + if (!obj) { + return NS_ERROR_OUT_OF_MEMORY; + } + aOut.setObject(*obj); + + layers::LayersBackend backend = gfxPlatform::Initialized() + ? gfxPlatform::GetPlatform()->GetCompositorBackend() + : layers::LayersBackend::LAYERS_NONE; + const char* backendName = GetLayersBackendName(backend); + { + JS::Rooted str(aCx, JS_NewStringCopyZ(aCx, backendName)); + JS::Rooted val(aCx, StringValue(str)); + JS_SetProperty(aCx, obj, "compositor", val); + } + + // If graphics isn't initialized yet, just stop now. + if (!gfxPlatform::Initialized()) { + return NS_OK; + } + + DescribeFeatures(aCx, obj); + return NS_OK; +} + +void +GfxInfoBase::DescribeFeatures(JSContext* cx, JS::Handle aOut) +{ +} + +bool +GfxInfoBase::InitFeatureObject(JSContext* aCx, + JS::Handle aContainer, + const char* aName, + mozilla::gfx::FeatureStatus aFeatureStatus, + JS::MutableHandle aOutObj) +{ + JS::Rooted obj(aCx, JS_NewPlainObject(aCx)); + if (!obj) { + return false; + } + + const char* status = FeatureStatusToString(aFeatureStatus); + + // Set "status". + { + JS::Rooted str(aCx, JS_NewStringCopyZ(aCx, status)); + JS::Rooted val(aCx, JS::StringValue(str)); + JS_SetProperty(aCx, obj, "status", val); + } + + // Add the feature object to the container. + { + JS::Rooted val(aCx, JS::ObjectValue(*obj)); + JS_SetProperty(aCx, aContainer, aName, val); + } + + aOutObj.set(obj); + return true; +} + GfxInfoCollectorBase::GfxInfoCollectorBase() { GfxInfoBase::AddCollector(this); diff --git a/widget/GfxInfoBase.h b/widget/GfxInfoBase.h index 0d170b0f15..d7d9e40ac2 100644 --- a/widget/GfxInfoBase.h +++ b/widget/GfxInfoBase.h @@ -19,6 +19,7 @@ #include "nsTArray.h" #include "nsString.h" #include "GfxInfoCollector.h" +#include "gfxTelemetry.h" #include "nsIGfxInfoDebug.h" #include "mozilla/Mutex.h" #include "js/Value.h" @@ -58,6 +59,7 @@ public: NS_IMETHOD GetFailures(uint32_t *failureCount, int32_t** indices, char ***failures) override; NS_IMETHOD_(void) LogFailure(const nsACString &failure) override; NS_IMETHOD GetInfo(JSContext*, JS::MutableHandle) override; + NS_IMETHOD GetFeatures(JSContext*, JS::MutableHandle) override; // Initialization function. If you override this, you must call this class's // version of Init first. @@ -103,6 +105,14 @@ protected: // (while subclasses check for more specific ones). virtual const nsTArray& GetGfxDriverInfo() = 0; + virtual void DescribeFeatures(JSContext* aCx, JS::Handle obj); + bool InitFeatureObject( + JSContext* aCx, + JS::Handle aContainer, + const char* aName, + mozilla::gfx::FeatureStatus aFeatureStatus, + JS::MutableHandle aOutObj); + private: virtual int32_t FindBlocklistedDeviceInList(const nsTArray& aDriverInfo, nsAString& aSuggestedVersion, diff --git a/widget/nsBaseWidget.cpp b/widget/nsBaseWidget.cpp index f93fef9f36..4d24312b25 100644 --- a/widget/nsBaseWidget.cpp +++ b/widget/nsBaseWidget.cpp @@ -1149,6 +1149,8 @@ void nsBaseWidget::CreateCompositor(int aWidth, int aHeight) WindowUsesOMTC(); mLayerManager = lm.forget(); + + gfxPlatform::GetPlatform()->NotifyCompositorCreated(mLayerManager->GetCompositorBackendType()); } bool nsBaseWidget::ShouldUseOffMainThreadCompositing() diff --git a/widget/nsIGfxInfo.idl b/widget/nsIGfxInfo.idl index 119ad4f31d..eafc73dcc9 100644 --- a/widget/nsIGfxInfo.idl +++ b/widget/nsIGfxInfo.idl @@ -8,7 +8,7 @@ /* NOTE: this interface is completely undesigned, not stable and likely to change */ -[scriptable, uuid(d9333198-838d-4976-881f-4d9a77819453)] +[scriptable, uuid(98690931-c9a5-4675-9ab4-90932ec32bf2)] interface nsIGfxInfo : nsISupports { /* @@ -76,7 +76,7 @@ interface nsIGfxInfo : nsISupports * A set of constants for features that we can ask this GfxInfo object * about via GetFeatureStatus */ - /* Don't assign 0 or -1 */ + /* Don't assign any value <= 0 */ /* Whether Direct2D is supported for content rendering. */ const long FEATURE_DIRECT2D = 1; /* Whether Direct3D 9 is supported for layers. */ @@ -93,15 +93,15 @@ interface nsIGfxInfo : nsISupports const long FEATURE_WEBGL_ANGLE = 7; /* Whether WebGL antialiasing is supported. */ const long FEATURE_WEBGL_MSAA = 8; - /* Whether Stagefright is supported */ + /* Whether Stagefright is supported, starting in 17. */ const long FEATURE_STAGEFRIGHT = 9; - /* Whether Webrtc Hardware acceleration is supported */ + /* Whether Webrtc Hardware acceleration is supported, starting in 31. */ const long FEATURE_WEBRTC_HW_ACCELERATION = 10; - /* Whether Direct3D 11 is supported for layers. */ + /* Whether Direct3D 11 is supported for layers, starting in 32. */ const long FEATURE_DIRECT3D_11_LAYERS = 11; - /* Whether hardware accelerated video decoding is supported. */ + /* Whether hardware accelerated video decoding is supported, starting in 36. */ const long FEATURE_HARDWARE_VIDEO_DECODING = 12; - /* Whether Direct3D 11 is supported for ANGLE. */ + /* Whether Direct3D 11 is supported for ANGLE, starting in 38. */ const long FEATURE_DIRECT3D_11_ANGLE = 13; /* @@ -146,5 +146,31 @@ interface nsIGfxInfo : nsISupports [implicit_jscontext] jsval getInfo(); + + // Returns an object containing information about graphics features. It is + // intended to be directly included into the Telemetry environment. + // + // "layers": + // { + // "compositor": "d3d9", "d3d11", "opengl", "basic", or "none" + // // ("none" indicates no compositors have been created) + // // Feature is one of "d3d9", "d3d11", "opengl", "basic", or "d2d". + // "": { + // // Each backend can have one of the following statuses: + // // "unused" - This feature has not been requested. + // // "unavailable" - OS version or restriction prevents use. + // // "blocked" - An internal condition (such as safe mode) prevents use. + // // "blacklisted" - Blocked due to a blacklist restriction. + // // "disabled" - User explicitly disabled this default feature. + // // "failed" - Feature failed to initialize. + // // "available" - User has this feature available by default. + // "status": "", + // "version": "", + // "warp": true|false, // D3D11 only. + // "textureSharing": true|false, // D3D11 only. + // } + // } + [implicit_jscontext] + jsval getFeatures(); }; diff --git a/widget/windows/GfxInfo.cpp b/widget/windows/GfxInfo.cpp index 7a602c4e89..b8cda92454 100644 --- a/widget/windows/GfxInfo.cpp +++ b/widget/windows/GfxInfo.cpp @@ -1141,6 +1141,42 @@ GfxInfo::FindMonitors(JSContext* aCx, JS::HandleObject aOutArray) return NS_OK; } +void +GfxInfo::DescribeFeatures(JSContext* aCx, JS::Handle aObj) +{ + JS::Rooted obj(aCx); + + gfxWindowsPlatform* platform = gfxWindowsPlatform::GetPlatform(); + + gfx::FeatureStatus d3d11 = platform->GetD3D11Status(); + if (!InitFeatureObject(aCx, aObj, "d3d11", d3d11, &obj)) { + return; + } + if (d3d11 == gfx::FeatureStatus::Available) { + JS::Rooted val(aCx, JS::Int32Value(platform->GetD3D11Version())); + JS_SetProperty(aCx, obj, "version", val); + + val = JS::BooleanValue(platform->IsWARP()); + JS_SetProperty(aCx, obj, "warp", val); + + val = JS::BooleanValue(platform->DoesD3D11TextureSharingWork()); + JS_SetProperty(aCx, obj, "textureSharing", val); + } + + gfx::FeatureStatus d2d = platform->GetD2DStatus(); + if (!InitFeatureObject(aCx, aObj, "d2d", d2d, &obj)) { + return; + } + { + const char* version = "1.0"; + if (platform->GetD2D1Status() == gfx::FeatureStatus::Available) + version = "1.1"; + JS::Rooted str(aCx, JS_NewStringCopyZ(aCx, version)); + JS::Rooted val(aCx, JS::StringValue(str)); + JS_SetProperty(aCx, obj, "version", val); + } +} + #ifdef DEBUG // Implement nsIGfxInfoDebug diff --git a/widget/windows/GfxInfo.h b/widget/windows/GfxInfo.h index 2967fec413..64427c8802 100644 --- a/widget/windows/GfxInfo.h +++ b/widget/windows/GfxInfo.h @@ -68,6 +68,8 @@ protected: OperatingSystem* aOS = nullptr); virtual const nsTArray& GetGfxDriverInfo(); + void DescribeFeatures(JSContext* cx, JS::Handle aOut) override; + private: void GetCountryCode(); diff --git a/xpcom/base/nsConsoleService.cpp b/xpcom/base/nsConsoleService.cpp index df422c78c9..36b2193fd7 100644 --- a/xpcom/base/nsConsoleService.cpp +++ b/xpcom/base/nsConsoleService.cpp @@ -317,7 +317,11 @@ nsConsoleService::LogMessageWithMode(nsIConsoleMessage* aMessage, } if (r) { - NS_DispatchToMainThread(r); + // avoid failing in XPCShell tests + nsCOMPtr mainThread = do_GetMainThread(); + if (mainThread) { + NS_DispatchToMainThread(r.forget()); + } } return NS_OK; diff --git a/xpcom/glue/nsThreadUtils.cpp b/xpcom/glue/nsThreadUtils.cpp index c42ebf3017..8282f36eb8 100644 --- a/xpcom/glue/nsThreadUtils.cpp +++ b/xpcom/glue/nsThreadUtils.cpp @@ -161,6 +161,22 @@ NS_DispatchToCurrentThread(nsIRunnable* aEvent) return thread->Dispatch(aEvent, NS_DISPATCH_NORMAL); } +NS_METHOD +NS_DispatchToMainThread(already_AddRefed&& aEvent, uint32_t aDispatchFlags) +{ + nsCOMPtr event(aEvent); + nsCOMPtr thread; + nsresult rv = NS_GetMainThread(getter_AddRefs(thread)); + if (NS_WARN_IF(NS_FAILED(rv))) { + NS_ASSERTION(false, "Failed NS_DispatchToMainThread() in shutdown; leaking"); + // NOTE: if you stop leaking here, adjust Promise::MaybeReportRejected(), + // which assumes a leak here, or split into leaks and no-leaks versions + nsIRunnable* temp = event.forget().take(); // leak without using "unused <<" due to Windows (boo) + return temp ? rv : rv; // to make compiler not bletch on us + } + return thread->Dispatch(event.forget(), aDispatchFlags); +} + // In the case of failure with a newly allocated runnable with a // refcount of zero, we intentionally leak the runnable, because it is // likely that the runnable is being dispatched to the main thread @@ -169,12 +185,8 @@ NS_DispatchToCurrentThread(nsIRunnable* aEvent) NS_METHOD NS_DispatchToMainThread(nsIRunnable* aEvent, uint32_t aDispatchFlags) { - nsCOMPtr thread; - nsresult rv = NS_GetMainThread(getter_AddRefs(thread)); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - return thread->Dispatch(aEvent, aDispatchFlags); + nsCOMPtr event(aEvent); + return NS_DispatchToMainThread(event.forget(), aDispatchFlags); } #ifndef XPCOM_GLUE_AVOID_NSPR @@ -374,4 +386,3 @@ nsAutoLowPriorityIO::~nsAutoLowPriorityIO() } #endif } - diff --git a/xpcom/glue/nsThreadUtils.h b/xpcom/glue/nsThreadUtils.h index 139750e790..132a924de9 100644 --- a/xpcom/glue/nsThreadUtils.h +++ b/xpcom/glue/nsThreadUtils.h @@ -123,6 +123,9 @@ extern NS_METHOD NS_DispatchToCurrentThread(nsIRunnable* aEvent); extern NS_METHOD NS_DispatchToMainThread(nsIRunnable* aEvent, uint32_t aDispatchFlags = NS_DISPATCH_NORMAL); +extern NS_METHOD +NS_DispatchToMainThread(already_AddRefed&& aEvent, + uint32_t aDispatchFlags = NS_DISPATCH_NORMAL); #ifndef XPCOM_GLUE_AVOID_NSPR /** diff --git a/xpcom/string/nsTSubstring.cpp b/xpcom/string/nsTSubstring.cpp index e871583b79..a351666aef 100644 --- a/xpcom/string/nsTSubstring.cpp +++ b/xpcom/string/nsTSubstring.cpp @@ -53,7 +53,8 @@ nsTSubstring_CharT::MutatePrep(size_type aCapacity, char_type** aOldData, // If |aCapacity > kMaxCapacity|, then our doubling algorithm may not be // able to allocate it. Just bail out in cases like that. We don't want // to be allocating 2GB+ strings anyway. - PR_STATIC_ASSERT((sizeof(nsStringBuffer) & 0x1) == 0); + static_assert((sizeof(nsStringBuffer) & 0x1) == 0, + "bad size for nsStringBuffer"); const size_type kMaxCapacity = (size_type(-1) / 2 - sizeof(nsStringBuffer)) / sizeof(char_type) - 2; if (aCapacity > kMaxCapacity) { diff --git a/xpcom/threads/LazyIdleThread.cpp b/xpcom/threads/LazyIdleThread.cpp index c599b5fcbe..d7e1c8d347 100644 --- a/xpcom/threads/LazyIdleThread.cpp +++ b/xpcom/threads/LazyIdleThread.cpp @@ -109,7 +109,7 @@ LazyIdleThread::EnableIdleTimeout() if (mThread) { nsCOMPtr runnable(new nsRunnable()); - if (NS_FAILED(Dispatch(runnable, NS_DISPATCH_NORMAL))) { + if (NS_FAILED(Dispatch(runnable.forget(), NS_DISPATCH_NORMAL))) { NS_WARNING("Failed to dispatch!"); } } @@ -302,7 +302,7 @@ LazyIdleThread::ShutdownThread() PreDispatch(); - rv = mThread->Dispatch(runnable, NS_DISPATCH_NORMAL); + rv = mThread->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } @@ -343,7 +343,7 @@ LazyIdleThread::ShutdownThread() runnable.swap(queuedRunnables[index]); MOZ_ASSERT(runnable, "Null runnable?!"); - if (NS_FAILED(Dispatch(runnable, NS_DISPATCH_NORMAL))) { + if (NS_FAILED(Dispatch(runnable.forget(), NS_DISPATCH_NORMAL))) { NS_ERROR("Failed to re-dispatch queued runnable!"); } } @@ -395,10 +395,18 @@ NS_IMPL_QUERY_INTERFACE(LazyIdleThread, nsIThread, nsIObserver) NS_IMETHODIMP -LazyIdleThread::Dispatch(nsIRunnable* aEvent, +LazyIdleThread::DispatchFromScript(nsIRunnable* aEvent, uint32_t aFlags) +{ + nsCOMPtr event(aEvent); + return Dispatch(event.forget(), aFlags); +} + +NS_IMETHODIMP +LazyIdleThread::Dispatch(already_AddRefed&& aEvent, uint32_t aFlags) { ASSERT_OWNING_THREAD(); + nsCOMPtr event(aEvent); // avoid leaks // LazyIdleThread can't always support synchronous dispatch currently. if (NS_WARN_IF(aFlags != NS_DISPATCH_NORMAL)) { @@ -412,7 +420,7 @@ LazyIdleThread::Dispatch(nsIRunnable* aEvent, // If our thread is shutting down then we can't actually dispatch right now. // Queue this runnable for later. if (UseRunnableQueue()) { - mQueuedRunnables->AppendElement(aEvent); + mQueuedRunnables->AppendElement(event); return NS_OK; } @@ -423,7 +431,7 @@ LazyIdleThread::Dispatch(nsIRunnable* aEvent, PreDispatch(); - return mThread->Dispatch(aEvent, aFlags); + return mThread->Dispatch(event.forget(), aFlags); } NS_IMETHODIMP @@ -557,7 +565,7 @@ LazyIdleThread::AfterProcessNextEvent(nsIThreadInternal* /* aThread */, return NS_ERROR_UNEXPECTED; } - nsresult rv = mOwningThread->Dispatch(runnable, NS_DISPATCH_NORMAL); + nsresult rv = mOwningThread->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } diff --git a/xpcom/threads/LazyIdleThread.h b/xpcom/threads/LazyIdleThread.h index bd3e43cd7c..44ca824d0a 100644 --- a/xpcom/threads/LazyIdleThread.h +++ b/xpcom/threads/LazyIdleThread.h @@ -45,6 +45,10 @@ public: NS_DECL_NSITIMERCALLBACK NS_DECL_NSITHREADOBSERVER NS_DECL_NSIOBSERVER + // missing from NS_DECL_NSIEVENTTARGET because MSVC + nsresult Dispatch(nsIRunnable* aEvent, uint32_t aFlags) { + return Dispatch(nsCOMPtr(aEvent).forget(), aFlags); + } enum ShutdownMethod { diff --git a/xpcom/threads/TimerThread.cpp b/xpcom/threads/TimerThread.cpp index 5168718955..7c51e30da8 100644 --- a/xpcom/threads/TimerThread.cpp +++ b/xpcom/threads/TimerThread.cpp @@ -453,9 +453,9 @@ TimerThread::AddTimerInternal(nsTimerImpl* aTimer) NS_ADDREF(aTimer); #ifdef MOZ_TASK_TRACER - // Create a FakeTracedTask, and dispatch it here. This is the start point of - // the latency. - aTimer->DispatchTracedTask(); + // Caller of AddTimer is the parent task of its timer event, so we store the + // TraceInfo here for later used. + aTimer->GetTLSTraceInfo(); #endif return insertSlot - mTimers.Elements(); diff --git a/xpcom/threads/nsEventQueue.cpp b/xpcom/threads/nsEventQueue.cpp index 5f6893b66d..a725ce23a7 100644 --- a/xpcom/threads/nsEventQueue.cpp +++ b/xpcom/threads/nsEventQueue.cpp @@ -84,6 +84,13 @@ nsEventQueue::GetEvent(bool aMayWait, nsIRunnable** aResult) void nsEventQueue::PutEvent(nsIRunnable* aRunnable) +{ + nsCOMPtr event(aRunnable); + PutEvent(event.forget()); +} + +void +nsEventQueue::PutEvent(already_AddRefed&& aRunnable) { // Avoid calling AddRef+Release while holding our monitor. nsCOMPtr event(aRunnable); diff --git a/xpcom/threads/nsEventQueue.h b/xpcom/threads/nsEventQueue.h index fc6568942e..4b5b6f02e9 100644 --- a/xpcom/threads/nsEventQueue.h +++ b/xpcom/threads/nsEventQueue.h @@ -10,6 +10,8 @@ #include #include "mozilla/ReentrantMonitor.h" #include "nsIRunnable.h" +#include "nsCOMPtr.h" +#include "mozilla/AlreadyAddRefed.h" // A threadsafe FIFO event queue... class nsEventQueue @@ -24,6 +26,7 @@ public: // a strong reference to the event after this method returns. This method // cannot fail. void PutEvent(nsIRunnable* aEvent); + void PutEvent(already_AddRefed&& aEvent); // This method gets an event from the event queue. If mayWait is true, then // the method will block the calling thread until an event is available. If diff --git a/xpcom/threads/nsIEventTarget.idl b/xpcom/threads/nsIEventTarget.idl index d54bb4d199..3f26466e52 100644 --- a/xpcom/threads/nsIEventTarget.idl +++ b/xpcom/threads/nsIEventTarget.idl @@ -5,29 +5,28 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "nsISupports.idl" +#include "nsIRunnable.idl" +%{C++ +#include "nsCOMPtr.h" +#include "mozilla/AlreadyAddRefed.h" +%} -interface nsIRunnable; +native alreadyAddRefed_nsIRunnable(already_AddRefed&&); -[scriptable, uuid(4e8febe4-6631-49dc-8ac9-308c1cb9b09c)] +[scriptable, uuid(f9d60700-e6dc-4a72-9537-689058655472)] interface nsIEventTarget : nsISupports { + /* until we can get rid of all uses, keep the non-alreadyAddRefed<> version */ /** - * Dispatch an event to this event target. This function may be called from - * any thread, and it may be called re-entrantly. - * - * @param event - * The event to dispatch. - * @param flags - * The flags modifying event dispatch. The flags are described in detail - * below. - * - * @throws NS_ERROR_INVALID_ARG - * Indicates that event is null. - * @throws NS_ERROR_UNEXPECTED - * Indicates that the thread is shutting down and has finished processing - * events, so this event would never run and has not been dispatched. + * This must be non-virtual due to issues with MSVC 2013's ordering of + * vtbls for overloads. With other platforms we can leave this virtual + * and avoid adding lots of Dispatch() methods to classes inheriting this. */ - void dispatch(in nsIRunnable event, in unsigned long flags); +%{C++ + nsresult Dispatch(nsIRunnable* aEvent, uint32_t aFlags) { + return Dispatch(nsCOMPtr(aEvent).forget(), aFlags); + } +%} /** * This flag specifies the default mode of event dispatch, whereby the event @@ -45,7 +44,7 @@ interface nsIEventTarget : nsISupports * given event to be processed. */ const unsigned long DISPATCH_SYNC = 1; - + /** * Check to see if this event target is associated with the current thread. * @@ -55,6 +54,41 @@ interface nsIEventTarget : nsISupports * this method). */ boolean isOnCurrentThread(); + + /** + * Dispatch an event to this event target. This function may be called from + * any thread, and it may be called re-entrantly. + * + * @param event + * The alreadyAddRefed<> event to dispatch + * @param flags + * The flags modifying event dispatch. The flags are described in detail + * below. + * + * @throws NS_ERROR_INVALID_ARG + * Indicates that event is null. + * @throws NS_ERROR_UNEXPECTED + * Indicates that the thread is shutting down and has finished processing + * events, so this event would never run and has not been dispatched. + */ + [noscript, binaryname(Dispatch)] void dispatchFromC(in alreadyAddRefed_nsIRunnable event, in unsigned long flags); + /** + * Version of Dispatch to expose to JS, which doesn't require an alreadyAddRefed<> + * (it will be converted to that internally) + * + * @param event + * The (raw) event to dispatch. + * @param flags + * The flags modifying event dispatch. The flags are described in detail + * below. + * + * @throws NS_ERROR_INVALID_ARG + * Indicates that event is null. + * @throws NS_ERROR_UNEXPECTED + * Indicates that the thread is shutting down and has finished processing + * events, so this event would never run and has not been dispatched. + */ + [binaryname(DispatchFromScript)] void dispatch(in nsIRunnable event, in unsigned long flags); }; %{C++ diff --git a/xpcom/threads/nsIThreadPool.idl b/xpcom/threads/nsIThreadPool.idl index 97d419c74c..77a124a85e 100644 --- a/xpcom/threads/nsIThreadPool.idl +++ b/xpcom/threads/nsIThreadPool.idl @@ -27,7 +27,7 @@ interface nsIThreadPoolListener : nsISupports * anonymous (unnamed) worker threads. An event dispatched to the thread pool * will be run on the next available worker thread. */ -[scriptable, uuid(53675068-cb3a-40e5-a026-1be5a97c9b23)] +[scriptable, uuid(cacd4a2e-2655-4ff8-894c-10c15883cd0a)] interface nsIThreadPool : nsIEventTarget { /** diff --git a/xpcom/threads/nsThread.cpp b/xpcom/threads/nsThread.cpp index 21322d00f8..0777d8e3ca 100644 --- a/xpcom/threads/nsThread.cpp +++ b/xpcom/threads/nsThread.cpp @@ -67,6 +67,7 @@ #ifdef MOZ_TASK_TRACER #include "GeckoTaskTracer.h" +#include "TracedTaskCommon.h" using namespace mozilla::tasktracer; #endif @@ -446,7 +447,7 @@ nsThread::Init() // that mThread is set properly. { MutexAutoLock lock(mLock); - mEventsRoot.PutEvent(startup); + mEventsRoot.PutEvent(startup); // retain a reference } // Wait for thread to call ThreadManager::SetupCurrentThread, which completes @@ -467,6 +468,13 @@ nsThread::InitCurrentThread() nsresult nsThread::PutEvent(nsIRunnable* aEvent, nsNestedEventTarget* aTarget) +{ + nsCOMPtr event(aEvent); + return PutEvent(event.forget(), aTarget); +} + +nsresult +nsThread::PutEvent(already_AddRefed&& aEvent, nsNestedEventTarget* aTarget) { nsCOMPtr obs; @@ -475,9 +483,11 @@ nsThread::PutEvent(nsIRunnable* aEvent, nsNestedEventTarget* aTarget) nsChainedEventQueue* queue = aTarget ? aTarget->mQueue : &mEventsRoot; if (!queue || (queue == &mEventsRoot && mEventsAreDoomed)) { NS_WARNING("An event was posted to a thread that will never run it (rejected)"); - return NS_ERROR_UNEXPECTED; + nsCOMPtr temp(aEvent); + nsIRunnable* temp2 = temp.forget().take(); // can't use unused << aEvent here due to Windows (boo) + return temp2 ? NS_ERROR_UNEXPECTED : NS_ERROR_UNEXPECTED; // to make compiler not bletch on us } - queue->PutEvent(aEvent); + queue->PutEvent(Move(aEvent)); // Make sure to grab the observer before dropping the lock, otherwise the // event that we just placed into the queue could run and eventually delete @@ -494,20 +504,23 @@ nsThread::PutEvent(nsIRunnable* aEvent, nsNestedEventTarget* aTarget) } nsresult -nsThread::DispatchInternal(nsIRunnable* aEvent, uint32_t aFlags, +nsThread::DispatchInternal(already_AddRefed&& aEvent, uint32_t aFlags, nsNestedEventTarget* aTarget) { - if (NS_WARN_IF(!aEvent)) { + nsCOMPtr event(aEvent); + if (NS_WARN_IF(!event)) { return NS_ERROR_INVALID_ARG; } if (gXPCOMThreadsShutDown && MAIN_THREAD != mIsMainThread && !aTarget) { + NS_ASSERTION(false, "Failed Dispatch after xpcom-shutdown-threads"); return NS_ERROR_ILLEGAL_DURING_SHUTDOWN; } #ifdef MOZ_TASK_TRACER - nsCOMPtr tracedRunnable = CreateTracedRunnable(aEvent); - aEvent = tracedRunnable; + nsCOMPtr tracedRunnable = CreateTracedRunnable(event); // adds a ref + (static_cast(tracedRunnable.get()))->DispatchTask(); + event = tracedRunnable.forget(); #endif if (aFlags & DISPATCH_SYNC) { @@ -521,8 +534,8 @@ nsThread::DispatchInternal(nsIRunnable* aEvent, uint32_t aFlags, // that to tell us when the event has been processed. nsRefPtr wrapper = - new nsThreadSyncDispatch(thread, aEvent); - nsresult rv = PutEvent(wrapper, aTarget); + new nsThreadSyncDispatch(thread, event.forget()); + nsresult rv = PutEvent(wrapper, aTarget); // hold a ref // Don't wait for the event to finish if we didn't dispatch it... if (NS_FAILED(rv)) { return rv; @@ -536,18 +549,25 @@ nsThread::DispatchInternal(nsIRunnable* aEvent, uint32_t aFlags, } NS_ASSERTION(aFlags == NS_DISPATCH_NORMAL, "unexpected dispatch flags"); - return PutEvent(aEvent, aTarget); + return PutEvent(event.forget(), aTarget); } //----------------------------------------------------------------------------- // nsIEventTarget NS_IMETHODIMP -nsThread::Dispatch(nsIRunnable* aEvent, uint32_t aFlags) +nsThread::DispatchFromScript(nsIRunnable* aEvent, uint32_t aFlags) { - LOG(("THRD(%p) Dispatch [%p %x]\n", this, aEvent, aFlags)); + nsCOMPtr event(aEvent); + return Dispatch(event.forget(), aFlags); +} - return DispatchInternal(aEvent, aFlags, nullptr); +NS_IMETHODIMP +nsThread::Dispatch(already_AddRefed&& aEvent, uint32_t aFlags) +{ + LOG(("THRD(%p) Dispatch [%p %x]\n", this, /* XXX aEvent */nullptr, aFlags)); + + return DispatchInternal(Move(aEvent), aFlags, nullptr); } NS_IMETHODIMP @@ -600,7 +620,7 @@ nsThread::Shutdown() // events to process. nsCOMPtr event = new nsThreadShutdownEvent(this, &context); // XXXroc What if posting the event fails due to OOM? - PutEvent(event, nullptr); + PutEvent(event.forget(), nullptr); // We could still end up with other events being added after the shutdown // task, but that's okay because we process pending events in ThreadFunc @@ -983,7 +1003,7 @@ nsThread::PopEventQueue(nsIEventTarget* aInnermostTarget) nsCOMPtr event; while (queue->GetEvent(false, getter_AddRefs(event))) { - mEvents->PutEvent(event); + mEvents->PutEvent(event.forget()); } // Don't let the event target post any more events. @@ -1028,12 +1048,19 @@ nsThreadSyncDispatch::Run() NS_IMPL_ISUPPORTS(nsThread::nsNestedEventTarget, nsIEventTarget) NS_IMETHODIMP -nsThread::nsNestedEventTarget::Dispatch(nsIRunnable* aEvent, uint32_t aFlags) +nsThread::nsNestedEventTarget::DispatchFromScript(nsIRunnable* aEvent, uint32_t aFlags) { - LOG(("THRD(%p) Dispatch [%p %x] to nested loop %p\n", mThread.get(), aEvent, + nsCOMPtr event(aEvent); + return Dispatch(event.forget(), aFlags); +} + +NS_IMETHODIMP +nsThread::nsNestedEventTarget::Dispatch(already_AddRefed&& aEvent, uint32_t aFlags) +{ + LOG(("THRD(%p) Dispatch [%p %x] to nested loop %p\n", mThread.get(), /*XXX aEvent*/ nullptr, aFlags, this)); - return mThread->DispatchInternal(aEvent, aFlags, this); + return mThread->DispatchInternal(Move(aEvent), aFlags, this); } NS_IMETHODIMP diff --git a/xpcom/threads/nsThread.h b/xpcom/threads/nsThread.h index 67e5516a3e..7b972f0cf9 100644 --- a/xpcom/threads/nsThread.h +++ b/xpcom/threads/nsThread.h @@ -16,6 +16,7 @@ #include "nsTObserverArray.h" #include "mozilla/Attributes.h" #include "nsAutoPtr.h" +#include "mozilla/AlreadyAddRefed.h" // A native thread class nsThread @@ -28,6 +29,10 @@ public: NS_DECL_NSITHREAD NS_DECL_NSITHREADINTERNAL NS_DECL_NSISUPPORTSPRIORITY + // missing from NS_DECL_NSIEVENTTARGET because MSVC + nsresult Dispatch(nsIRunnable* aEvent, uint32_t aFlags) { + return Dispatch(nsCOMPtr(aEvent).forget(), aFlags); + } enum MainThreadFlag { @@ -98,8 +103,9 @@ protected: return mEvents->GetEvent(aMayWait, aEvent); } nsresult PutEvent(nsIRunnable* aEvent, nsNestedEventTarget* aTarget); + nsresult PutEvent(already_AddRefed&& aEvent, nsNestedEventTarget* aTarget); - nsresult DispatchInternal(nsIRunnable* aEvent, uint32_t aFlags, + nsresult DispatchInternal(already_AddRefed&& aEvent, uint32_t aFlags, nsNestedEventTarget* aTarget); // Wrapper for nsEventQueue that supports chaining. @@ -121,6 +127,11 @@ protected: mQueue.PutEvent(aEvent); } + void PutEvent(already_AddRefed&& aEvent) + { + mQueue.PutEvent(mozilla::Move(aEvent)); + } + bool HasPendingEvent() { return mQueue.HasPendingEvent(); @@ -189,7 +200,7 @@ protected: class nsThreadSyncDispatch : public nsRunnable { public: - nsThreadSyncDispatch(nsIThread* aOrigin, nsIRunnable* aTask) + nsThreadSyncDispatch(nsIThread* aOrigin, already_AddRefed&& aTask) : mOrigin(aOrigin) , mSyncTask(aTask) , mResult(NS_ERROR_NOT_INITIALIZED) diff --git a/xpcom/threads/nsThreadPool.cpp b/xpcom/threads/nsThreadPool.cpp index 64f7f01eca..f8d06bf239 100644 --- a/xpcom/threads/nsThreadPool.cpp +++ b/xpcom/threads/nsThreadPool.cpp @@ -66,6 +66,13 @@ nsThreadPool::~nsThreadPool() nsresult nsThreadPool::PutEvent(nsIRunnable* aEvent) +{ + nsCOMPtr event(aEvent); + return PutEvent(event.forget()); +} + +nsresult +nsThreadPool::PutEvent(already_AddRefed&& aEvent) { // Avoid spawning a new thread while holding the event queue lock... @@ -89,7 +96,7 @@ nsThreadPool::PutEvent(nsIRunnable* aEvent) spawnThread = true; } - mEvents.PutEvent(aEvent); + mEvents.PutEvent(Move(aEvent)); stackSize = mStackSize; } @@ -235,9 +242,16 @@ nsThreadPool::Run() } NS_IMETHODIMP -nsThreadPool::Dispatch(nsIRunnable* aEvent, uint32_t aFlags) +nsThreadPool::DispatchFromScript(nsIRunnable* aEvent, uint32_t aFlags) { - LOG(("THRD-P(%p) dispatch [%p %x]\n", this, aEvent, aFlags)); + nsCOMPtr event(aEvent); + return Dispatch(event.forget(), aFlags); +} + +NS_IMETHODIMP +nsThreadPool::Dispatch(already_AddRefed&& aEvent, uint32_t aFlags) +{ + LOG(("THRD-P(%p) dispatch [%p %x]\n", this, /* XXX aEvent*/ nullptr, aFlags)); if (NS_WARN_IF(mShutdown)) { return NS_ERROR_NOT_AVAILABLE; @@ -251,7 +265,7 @@ nsThreadPool::Dispatch(nsIRunnable* aEvent, uint32_t aFlags) } nsRefPtr wrapper = - new nsThreadSyncDispatch(thread, aEvent); + new nsThreadSyncDispatch(thread, Move(aEvent)); PutEvent(wrapper); while (wrapper->IsPending()) { @@ -259,7 +273,7 @@ nsThreadPool::Dispatch(nsIRunnable* aEvent, uint32_t aFlags) } } else { NS_ASSERTION(aFlags == NS_DISPATCH_NORMAL, "unexpected dispatch flags"); - PutEvent(aEvent); + PutEvent(Move(aEvent)); } return NS_OK; } diff --git a/xpcom/threads/nsThreadPool.h b/xpcom/threads/nsThreadPool.h index 8a868ba2e4..83707b53bf 100644 --- a/xpcom/threads/nsThreadPool.h +++ b/xpcom/threads/nsThreadPool.h @@ -15,6 +15,7 @@ #include "nsCOMPtr.h" #include "nsThreadUtils.h" #include "mozilla/Attributes.h" +#include "mozilla/AlreadyAddRefed.h" class nsThreadPool final : public nsIThreadPool @@ -25,6 +26,10 @@ public: NS_DECL_NSIEVENTTARGET NS_DECL_NSITHREADPOOL NS_DECL_NSIRUNNABLE + // missing from NS_DECL_NSIEVENTTARGET because MSVC + nsresult Dispatch(nsIRunnable* aEvent, uint32_t aFlags) { + return Dispatch(nsCOMPtr(aEvent).forget(), aFlags); + } nsThreadPool(); @@ -33,6 +38,7 @@ private: void ShutdownThread(nsIThread* aThread); nsresult PutEvent(nsIRunnable* aEvent); + nsresult PutEvent(already_AddRefed&& aEvent); nsCOMArray mThreads; nsEventQueue mEvents; diff --git a/xpcom/threads/nsTimerImpl.cpp b/xpcom/threads/nsTimerImpl.cpp index 1a0acbb1ac..7c5b71005d 100644 --- a/xpcom/threads/nsTimerImpl.cpp +++ b/xpcom/threads/nsTimerImpl.cpp @@ -17,6 +17,10 @@ #ifdef MOZ_NUWA_PROCESS #include "ipc/Nuwa.h" #endif +#ifdef MOZ_TASK_TRACER +#include "GeckoTaskTracerImpl.h" +using namespace mozilla::tasktracer; +#endif using mozilla::Atomic; using mozilla::LogLevel; @@ -274,7 +278,7 @@ nsTimerImpl::Release(void) nsTimerImpl::nsTimerImpl() : mClosure(nullptr), - mCallbackType(CALLBACK_TYPE_UNKNOWN), + mCallbackType(CallbackType::Unknown), mFiring(false), mArmed(false), mCanceled(false), @@ -393,7 +397,7 @@ nsTimerImpl::InitWithFuncCallback(nsTimerCallbackFunc aFunc, } ReleaseCallback(); - mCallbackType = CALLBACK_TYPE_FUNC; + mCallbackType = CallbackType::Function; mCallback.c = aFunc; mClosure = aClosure; @@ -410,7 +414,7 @@ nsTimerImpl::InitWithCallback(nsITimerCallback* aCallback, } ReleaseCallback(); - mCallbackType = CALLBACK_TYPE_INTERFACE; + mCallbackType = CallbackType::Interface; mCallback.i = aCallback; NS_ADDREF(mCallback.i); @@ -425,7 +429,7 @@ nsTimerImpl::Init(nsIObserver* aObserver, uint32_t aDelay, uint32_t aType) } ReleaseCallback(); - mCallbackType = CALLBACK_TYPE_OBSERVER; + mCallbackType = CallbackType::Observer; mCallback.o = aObserver; NS_ADDREF(mCallback.o); @@ -449,7 +453,7 @@ nsTimerImpl::Cancel() NS_IMETHODIMP nsTimerImpl::SetDelay(uint32_t aDelay) { - if (mCallbackType == CALLBACK_TYPE_UNKNOWN && mType == TYPE_ONE_SHOT) { + if (mCallbackType == CallbackType::Unknown && mType == TYPE_ONE_SHOT) { // This may happen if someone tries to re-use a one-shot timer // by re-setting delay instead of reinitializing the timer. NS_ERROR("nsITimer->SetDelay() called when the " @@ -508,7 +512,7 @@ nsTimerImpl::GetClosure(void** aClosure) NS_IMETHODIMP nsTimerImpl::GetCallback(nsITimerCallback** aCallback) { - if (mCallbackType == CALLBACK_TYPE_INTERFACE) { + if (mCallbackType == CallbackType::Interface) { NS_IF_ADDREF(*aCallback = mCallback.i); } else if (mTimerCallbackWhileFiring) { NS_ADDREF(*aCallback = mTimerCallbackWhileFiring); @@ -531,7 +535,7 @@ nsTimerImpl::GetTarget(nsIEventTarget** aTarget) NS_IMETHODIMP nsTimerImpl::SetTarget(nsIEventTarget* aTarget) { - if (NS_WARN_IF(mCallbackType != CALLBACK_TYPE_UNKNOWN)) { + if (NS_WARN_IF(mCallbackType != CallbackType::Unknown)) { return NS_ERROR_ALREADY_INITIALIZED; } @@ -556,13 +560,6 @@ nsTimerImpl::Fire() js::ProfileEntry::Category::OTHER); #endif -#ifdef MOZ_TASK_TRACER - // mTracedTask is an instance of FakeTracedTask created by - // DispatchTracedTask(). AutoRunFakeTracedTask logs the begin/end time of the - // timer/FakeTracedTask instance in ctor/dtor. - mozilla::tasktracer::AutoRunFakeTracedTask runTracedTask(mTracedTask); -#endif - TimeStamp now = TimeStamp::Now(); if (MOZ_LOG_TEST(GetTimerLog(), LogLevel::Debug)) { TimeDuration a = now - mStart; // actual delay in intervals @@ -595,7 +592,7 @@ nsTimerImpl::Fire() timeout -= TimeDuration::FromMilliseconds(mDelay); } - if (mCallbackType == CALLBACK_TYPE_INTERFACE) { + if (mCallbackType == CallbackType::Interface) { mTimerCallbackWhileFiring = mCallback.i; } mFiring = true; @@ -603,22 +600,22 @@ nsTimerImpl::Fire() // Handle callbacks that re-init the timer, but avoid leaking. // See bug 330128. CallbackUnion callback = mCallback; - unsigned callbackType = mCallbackType; - if (callbackType == CALLBACK_TYPE_INTERFACE) { + CallbackType callbackType = mCallbackType; + if (callbackType == CallbackType::Interface) { NS_ADDREF(callback.i); - } else if (callbackType == CALLBACK_TYPE_OBSERVER) { + } else if (callbackType == CallbackType::Observer) { NS_ADDREF(callback.o); } ReleaseCallback(); switch (callbackType) { - case CALLBACK_TYPE_FUNC: + case CallbackType::Function: callback.c(this, mClosure); break; - case CALLBACK_TYPE_INTERFACE: + case CallbackType::Interface: callback.i->Notify(this); break; - case CALLBACK_TYPE_OBSERVER: + case CallbackType::Observer: callback.o->Observe(static_cast(this), NS_TIMER_CALLBACK_TOPIC, nullptr); @@ -629,15 +626,15 @@ nsTimerImpl::Fire() // If the callback didn't re-init the timer, and it's not a one-shot timer, // restore the callback state. - if (mCallbackType == CALLBACK_TYPE_UNKNOWN && + if (mCallbackType == CallbackType::Unknown && mType != TYPE_ONE_SHOT && !mCanceled) { mCallback = callback; mCallbackType = callbackType; } else { // The timer was a one-shot, or the callback was reinitialized. - if (callbackType == CALLBACK_TYPE_INTERFACE) { + if (callbackType == CallbackType::Interface) { NS_RELEASE(callback.i); - } else if (callbackType == CALLBACK_TYPE_OBSERVER) { + } else if (callbackType == CallbackType::Observer) { NS_RELEASE(callback.o); } } @@ -752,6 +749,14 @@ nsTimerImpl::PostTimerEvent(already_AddRefed aTimerRef) } } +#ifdef MOZ_TASK_TRACER + // During the dispatch of TimerEvent, we overwrite the current TraceInfo + // partially with the info saved in timer earlier, and restore it back by + // AutoSaveCurTraceInfo. + AutoSaveCurTraceInfo saveCurTraceInfo; + (timer->GetTracedTask()).SetTLSTraceInfo(); +#endif + nsIEventTarget* target = timer->mEventTarget; event->SetTimer(timer.forget()); @@ -796,21 +801,17 @@ nsTimerImpl::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const return aMallocSizeOf(this); } -// NOT FOR PUBLIC CONSUMPTION! -nsresult -NS_NewTimer(nsITimer** aResult, nsTimerCallbackFunc aCallback, void* aClosure, - uint32_t aDelay, uint32_t aType) +#ifdef MOZ_TASK_TRACER +void +nsTimerImpl::GetTLSTraceInfo() { - nsTimerImpl* timer = new nsTimerImpl(); - NS_ADDREF(timer); - - nsresult rv = timer->InitWithFuncCallback(aCallback, aClosure, - aDelay, aType); - if (NS_FAILED(rv)) { - NS_RELEASE(timer); - return rv; - } - - *aResult = timer; - return NS_OK; + mTracedTask.GetTLSTraceInfo(); } + +TracedTaskCommon +nsTimerImpl::GetTracedTask() +{ + return mTracedTask; +} +#endif + diff --git a/xpcom/threads/nsTimerImpl.h b/xpcom/threads/nsTimerImpl.h index b94177ce83..f57616842a 100644 --- a/xpcom/threads/nsTimerImpl.h +++ b/xpcom/threads/nsTimerImpl.h @@ -31,14 +31,6 @@ extern PRLogModuleInfo* GetTimerLog(); {0x84, 0x27, 0xfb, 0xab, 0x44, 0xf2, 0x9b, 0xc8} \ } -enum -{ - CALLBACK_TYPE_UNKNOWN = 0, - CALLBACK_TYPE_INTERFACE = 1, - CALLBACK_TYPE_FUNC = 2, - CALLBACK_TYPE_OBSERVER = 3 -}; - class nsTimerImpl final : public nsITimer { public: @@ -67,15 +59,20 @@ public: } #ifdef MOZ_TASK_TRACER - void DispatchTracedTask() - { - mTracedTask = mozilla::tasktracer::CreateFakeTracedTask(*(int**)(this)); - } + void GetTLSTraceInfo(); + mozilla::tasktracer::TracedTaskCommon GetTracedTask(); #endif virtual size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const override; private: + enum class CallbackType : uint8_t { + Unknown = 0, + Interface = 1, + Function = 2, + Observer = 3, + }; + ~nsTimerImpl(); nsresult InitCommon(uint32_t aType, uint32_t aDelay); @@ -84,21 +81,24 @@ private: // if we're the last owner of the callback object, make // sure that we don't recurse into ReleaseCallback in case // the callback's destructor calls Cancel() or similar. - uint8_t cbType = mCallbackType; - mCallbackType = CALLBACK_TYPE_UNKNOWN; + CallbackType cbType = mCallbackType; + mCallbackType = CallbackType::Unknown; - if (cbType == CALLBACK_TYPE_INTERFACE) { + if (cbType == CallbackType::Interface) { NS_RELEASE(mCallback.i); - } else if (cbType == CALLBACK_TYPE_OBSERVER) { + } else if (cbType == CallbackType::Observer) { NS_RELEASE(mCallback.o); } } bool IsRepeating() const { - PR_STATIC_ASSERT(TYPE_ONE_SHOT < TYPE_REPEATING_SLACK); - PR_STATIC_ASSERT(TYPE_REPEATING_SLACK < TYPE_REPEATING_PRECISE); - PR_STATIC_ASSERT(TYPE_REPEATING_PRECISE < TYPE_REPEATING_PRECISE_CAN_SKIP); + static_assert(TYPE_ONE_SHOT < TYPE_REPEATING_SLACK, + "invalid ordering of timer types!"); + static_assert(TYPE_REPEATING_SLACK < TYPE_REPEATING_PRECISE, + "invalid ordering of timer types!"); + static_assert(TYPE_REPEATING_PRECISE < TYPE_REPEATING_PRECISE_CAN_SKIP, + "invalid ordering of timer types!"); return mType >= TYPE_REPEATING_SLACK; } @@ -122,8 +122,8 @@ private: // timer is firing. nsCOMPtr mTimerCallbackWhileFiring; - // These members are set by Init (called from NS_NewTimer) and never reset. - uint8_t mCallbackType; + // These members are set by Init and never reset. + CallbackType mCallbackType; // These members are set by the initiating thread, when the timer's type is // changed and during the period where it fires on that thread. @@ -147,7 +147,7 @@ private: TimeStamp mTimeout; #ifdef MOZ_TASK_TRACER - nsRefPtr mTracedTask; + mozilla::tasktracer::TracedTaskCommon mTracedTask; #endif TimeStamp mStart, mStart2;