Back out 9b419a38b9c9 (bug 1280507) for breaking offscreencanvas on Windows (even more than its usual broken state)
authorPhil Ringnalda <philringnalda@gmail.com>
Mon, 27 Jun 2016 22:48:17 -0700
changeset 302825 bb8c537dc68e88eeb7d75edd519bcf6f219f65de
parent 302824 e30eb329b1320c54228eb5fd5a6f4246f0558241
child 302826 cf174e130188f36c8e0dd333ebb378df64b06fe9
push id30376
push usercbook@mozilla.com
push dateTue, 28 Jun 2016 14:09:36 +0000
treeherdermozilla-central@e45890951ce7 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1280507
milestone50.0a1
backs out9b419a38b9c94db1fbba9e5f3a6477a2089cff01
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Back out 9b419a38b9c9 (bug 1280507) for breaking offscreencanvas on Windows (even more than its usual broken state)
dom/canvas/WebGLContext.cpp
dom/canvas/WebGLContext.h
dom/canvas/WebGLContextLossHandler.cpp
dom/canvas/WebGLContextLossHandler.h
--- a/dom/canvas/WebGLContext.cpp
+++ b/dom/canvas/WebGLContext.cpp
@@ -108,17 +108,16 @@ WebGLContextOptions::WebGLContextOptions
 
 WebGLContext::WebGLContext()
     : WebGLContextUnchecked(nullptr)
     , mBufferFetchingIsVerified(false)
     , mBufferFetchingHasPerVertex(false)
     , mMaxFetchedVertices(0)
     , mMaxFetchedInstances(0)
     , mBypassShaderValidation(false)
-    , mContextLossHandler(this)
     , mNeedsFakeNoAlpha(false)
     , mNeedsFakeNoDepth(false)
     , mNeedsFakeNoStencil(false)
     , mNeedsEmulatedLoneDepthStencil(false)
 {
     mGeneration = 0;
     mInvalidated = false;
     mCapturedFrameInvalidated = false;
@@ -170,16 +169,17 @@ WebGLContext::WebGLContext()
 
     if (NS_IsMainThread()) {
         // XXX mtseng: bug 709490, not thread safe
         WebGLMemoryTracker::AddWebGLContext(this);
     }
 
     mAllowContextRestore = true;
     mLastLossWasSimulated = false;
+    mContextLossHandler = new WebGLContextLossHandler(this);
     mContextStatus = ContextNotLost;
     mLoseContextOnMemoryPressure = false;
     mCanLoseContextInForeground = true;
     mRestoreWhenVisible = false;
 
     mAlreadyGeneratedWarnings = 0;
     mAlreadyWarnedAboutFakeVertexAttrib0 = false;
     mAlreadyWarnedAboutViewportLargerThanDest = false;
@@ -205,16 +205,19 @@ WebGLContext::~WebGLContext()
 {
     RemovePostRefreshObserver();
 
     DestroyResourcesAndContext();
     if (NS_IsMainThread()) {
         // XXX mtseng: bug 709490, not thread safe
         WebGLMemoryTracker::RemoveWebGLContext(this);
     }
+
+    mContextLossHandler->DisableTimer();
+    mContextLossHandler = nullptr;
 }
 
 template<typename T>
 static void
 ClearLinkedList(LinkedList<T>& list)
 {
     while (!list.isEmpty()) {
         list.getLast()->DeleteOnce();
@@ -1615,17 +1618,17 @@ WebGLContext::TryToRestoreContext()
         return false;
 
     return true;
 }
 
 void
 WebGLContext::RunContextLossTimer()
 {
-    mContextLossHandler.RunTimer();
+    mContextLossHandler->RunTimer();
 }
 
 class UpdateContextLossStatusTask : public CancelableRunnable
 {
     RefPtr<WebGLContext> mWebGL;
 
 public:
     explicit UpdateContextLossStatusTask(WebGLContext* webgl)
@@ -1757,17 +1760,17 @@ WebGLContext::UpdateContextLossStatus()
             // We might decide this after thinking we'd be OK restoring
             // the context, so downgrade.
             mContextStatus = ContextLost;
             return;
         }
 
         if (!TryToRestoreContext()) {
             // Failed to restore. Try again later.
-            mContextLossHandler.RunTimer();
+            mContextLossHandler->RunTimer();
             return;
         }
 
         // Revival!
         mContextStatus = ContextNotLost;
 
         if (mCanvasElement) {
             nsContentUtils::DispatchTrustedEvent(
--- a/dom/canvas/WebGLContext.h
+++ b/dom/canvas/WebGLContext.h
@@ -14,31 +14,31 @@
 #include "mozilla/CheckedInt.h"
 #include "mozilla/dom/HTMLCanvasElement.h"
 #include "mozilla/dom/TypedArray.h"
 #include "mozilla/EnumeratedArray.h"
 #include "mozilla/ErrorResult.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/LinkedList.h"
 #include "mozilla/UniquePtr.h"
+#include "mozilla/WeakPtr.h"
 #include "nsCycleCollectionNoteChild.h"
 #include "nsICanvasRenderingContextInternal.h"
 #include "nsLayoutUtils.h"
 #include "nsTArray.h"
 #include "nsWrapperCache.h"
 #include "SurfaceTypes.h"
 #include "ScopedGLHelpers.h"
 #include "TexUnpackBlob.h"
 
 #ifdef XP_MACOSX
 #include "ForceDiscreteGPUHelperCGL.h"
 #endif
 
 // Local
-#include "WebGLContextLossHandler.h"
 #include "WebGLContextUnchecked.h"
 #include "WebGLFormats.h"
 #include "WebGLObjectModel.h"
 #include "WebGLStrongTypes.h"
 #include "WebGLTexture.h"
 
 // Generated
 #include "nsIDOMEventListener.h"
@@ -85,16 +85,17 @@ class nsIDocShell;
 #define LOCAL_GL_UNPACK_FLIP_Y_WEBGL                         0x9240
 #define LOCAL_GL_UNPACK_PREMULTIPLY_ALPHA_WEBGL              0x9241
 
 namespace mozilla {
 class ScopedCopyTexImageSource;
 class ScopedResolveTexturesForDraw;
 class ScopedUnpackReset;
 class WebGLActiveInfo;
+class WebGLContextLossHandler;
 class WebGLBuffer;
 class WebGLExtensionBase;
 class WebGLFramebuffer;
 class WebGLProgram;
 class WebGLQuery;
 class WebGLRenderbuffer;
 class WebGLSampler;
 class WebGLShader;
@@ -181,16 +182,17 @@ public:
 
 class WebGLContext
     : public nsIDOMWebGLRenderingContext
     , public nsICanvasRenderingContextInternal
     , public nsSupportsWeakReference
     , public WebGLContextUnchecked
     , public WebGLRectangleObject
     , public nsWrapperCache
+    , public SupportsWeakPtr<WebGLContext>
 {
     friend class WebGL2Context;
     friend class WebGLContextUserData;
     friend class WebGLExtensionCompressedTextureATC;
     friend class WebGLExtensionCompressedTextureES3;
     friend class WebGLExtensionCompressedTextureETC1;
     friend class WebGLExtensionCompressedTexturePVRTC;
     friend class WebGLExtensionCompressedTextureS3TC;
@@ -216,16 +218,18 @@ class WebGLContext
 
 public:
     WebGLContext();
 
 protected:
     virtual ~WebGLContext();
 
 public:
+    MOZ_DECLARE_WEAKREFERENCE_TYPENAME(WebGLContext)
+
     NS_DECL_CYCLE_COLLECTING_ISUPPORTS
 
     NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(WebGLContext,
                                                            nsIDOMWebGLRenderingContext)
 
     virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto) override = 0;
 
     NS_DECL_NSIDOMWEBGLRENDERINGCONTEXT
@@ -1482,17 +1486,17 @@ protected:
     GLfloat mDepthClearValue;
 
     GLint mViewportX;
     GLint mViewportY;
     GLsizei mViewportWidth;
     GLsizei mViewportHeight;
     bool mAlreadyWarnedAboutViewportLargerThanDest;
 
-    WebGLContextLossHandler mContextLossHandler;
+    RefPtr<WebGLContextLossHandler> mContextLossHandler;
     bool mAllowContextRestore;
     bool mLastLossWasSimulated;
     ContextStatus mContextStatus;
     bool mContextLostErrorSet;
 
     // Used for some hardware (particularly Tegra 2 and 4) that likes to
     // be Flushed while doing hundreds of draw calls.
     int mDrawCallsSinceLastFlush;
--- a/dom/canvas/WebGLContextLossHandler.cpp
+++ b/dom/canvas/WebGLContextLossHandler.cpp
@@ -1,103 +1,241 @@
 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* 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 "WebGLContextLossHandler.h"
 
-#include "mozilla/DebugOnly.h"
-#include "nsISupportsImpl.h"
 #include "nsITimer.h"
 #include "nsThreadUtils.h"
 #include "WebGLContext.h"
+#include "mozilla/dom/WorkerPrivate.h"
 
 namespace mozilla {
 
-class WatchdogTimerEvent final : public nsITimerCallback
+// -------------------------------------------------------------------
+// Begin worker specific code
+// -------------------------------------------------------------------
+
+// On workers we can only dispatch CancelableRunnables, so we have to wrap the
+// timer's EventTarget to use our own cancelable runnable
+
+class ContextLossWorkerEventTarget final : public nsIEventTarget
 {
-    const WeakPtr<WebGLContextLossHandler> mHandler;
-
 public:
-    NS_DECL_ISUPPORTS
+    explicit ContextLossWorkerEventTarget(nsIEventTarget* aEventTarget)
+        : mEventTarget(aEventTarget)
+    {
+        MOZ_ASSERT(aEventTarget);
+    }
+
+    NS_DECL_NSIEVENTTARGET
+    NS_DECL_THREADSAFE_ISUPPORTS
+
+protected:
+    ~ContextLossWorkerEventTarget() {}
 
-    explicit WatchdogTimerEvent(WebGLContextLossHandler* handler)
-        : mHandler(handler)
-    { }
+private:
+    nsCOMPtr<nsIEventTarget> mEventTarget;
+};
+
+class ContextLossWorkerRunnable final : public CancelableRunnable
+{
+public:
+    explicit ContextLossWorkerRunnable(nsIRunnable* aRunnable)
+        : mRunnable(aRunnable)
+    {
+    }
+
+    nsresult Cancel() override;
+
+    NS_FORWARD_NSIRUNNABLE(mRunnable->)
+
+protected:
+    ~ContextLossWorkerRunnable() {}
 
 private:
-    virtual ~WatchdogTimerEvent() { }
-
-    NS_IMETHOD Notify(nsITimer*) override {
-        if (mHandler) {
-            mHandler->TimerCallback();
-        }
-        return NS_OK;
-    }
+    nsCOMPtr<nsIRunnable> mRunnable;
 };
 
-NS_IMPL_ISUPPORTS(WatchdogTimerEvent, nsITimerCallback, nsISupports)
+NS_IMPL_ISUPPORTS(ContextLossWorkerEventTarget, nsIEventTarget,
+                  nsISupports)
+
+NS_IMETHODIMP
+ContextLossWorkerEventTarget::DispatchFromScript(nsIRunnable* aEvent, uint32_t aFlags)
+{
+    nsCOMPtr<nsIRunnable> event(aEvent);
+    return Dispatch(event.forget(), aFlags);
+}
+
+NS_IMETHODIMP
+ContextLossWorkerEventTarget::Dispatch(already_AddRefed<nsIRunnable> aEvent,
+                                       uint32_t aFlags)
+{
+    nsCOMPtr<nsIRunnable> eventRef(aEvent);
+    RefPtr<ContextLossWorkerRunnable> wrappedEvent =
+        new ContextLossWorkerRunnable(eventRef);
+    return mEventTarget->Dispatch(wrappedEvent, aFlags);
+}
 
-////////////////////////////////////////
+NS_IMETHODIMP
+ContextLossWorkerEventTarget::DelayedDispatch(already_AddRefed<nsIRunnable>,
+                                              uint32_t)
+{
+    return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+ContextLossWorkerEventTarget::IsOnCurrentThread(bool* aResult)
+{
+    return mEventTarget->IsOnCurrentThread(aResult);
+}
+
+nsresult
+ContextLossWorkerRunnable::Cancel()
+{
+    mRunnable = nullptr;
+    return NS_OK;
+}
+
+// -------------------------------------------------------------------
+// End worker-specific code
+// -------------------------------------------------------------------
 
 WebGLContextLossHandler::WebGLContextLossHandler(WebGLContext* webgl)
-    : mWebGL(webgl)
+    : mWeakWebGL(webgl)
     , mTimer(do_CreateInstance(NS_TIMER_CONTRACTID))
-    , mTimerPending(false)
+    , mIsTimerRunning(false)
     , mShouldRunTimerAgain(false)
+    , mIsDisabled(false)
+    , mWorkerHolderAdded(false)
 #ifdef DEBUG
     , mThread(NS_GetCurrentThread())
 #endif
 {
-    MOZ_ASSERT(mThread);
 }
 
 WebGLContextLossHandler::~WebGLContextLossHandler()
 {
-    // NS_GetCurrentThread() returns null during shutdown.
-    const DebugOnly<nsIThread*> callingThread = NS_GetCurrentThread();
-    MOZ_ASSERT(callingThread == mThread || !callingThread);
+    MOZ_ASSERT(!mIsTimerRunning);
 }
 
-////////////////////
-
 void
-WebGLContextLossHandler::RunTimer()
+WebGLContextLossHandler::StartTimer(unsigned long delayMS)
 {
-    MOZ_ASSERT(NS_GetCurrentThread() == mThread);
+    // We can't pass an already_AddRefed through InitWithFuncCallback, so we
+    // should do the AddRef/Release manually.
+    this->AddRef();
 
-    // If the timer was already running, don't restart it here. Instead,
-    // wait until the previous call is done, then fire it one more time.
-    // This is also an optimization to prevent unnecessary
-    // cross-communication between threads.
-    if (mTimerPending) {
-        mShouldRunTimerAgain = true;
-        return;
-    }
-
-    const RefPtr<WatchdogTimerEvent> event = new WatchdogTimerEvent(this);
-    const uint32_t kDelayMS = 1000;
-    mTimer->InitWithCallback(event, kDelayMS, nsITimer::TYPE_ONE_SHOT);
-
-    mTimerPending = true;
+    mTimer->InitWithFuncCallback(StaticTimerCallback,
+                                 static_cast<void*>(this),
+                                 delayMS,
+                                 nsITimer::TYPE_ONE_SHOT);
 }
 
-////////////////////
+/*static*/ void
+WebGLContextLossHandler::StaticTimerCallback(nsITimer*, void* voidHandler)
+{
+    typedef WebGLContextLossHandler T;
+    T* handler = static_cast<T*>(voidHandler);
+
+    handler->TimerCallback();
+
+    // Release the AddRef from StartTimer.
+    handler->Release();
+}
 
 void
 WebGLContextLossHandler::TimerCallback()
 {
     MOZ_ASSERT(NS_GetCurrentThread() == mThread);
+    MOZ_ASSERT(mIsTimerRunning);
+    mIsTimerRunning = false;
 
-    mTimerPending = false;
+    if (mIsDisabled)
+        return;
 
-    const bool runOnceMore = mShouldRunTimerAgain;
-    mShouldRunTimerAgain = false;
+    // If we need to run the timer again, restart it immediately.
+    // Otherwise, the code we call into below might *also* try to
+    // restart it.
+    if (mShouldRunTimerAgain) {
+        RunTimer();
+        MOZ_ASSERT(mIsTimerRunning);
+    }
 
-    mWebGL->UpdateContextLossStatus();
-
-    if (runOnceMore && !mTimerPending) {
-        RunTimer();
+    if (mWeakWebGL) {
+        mWeakWebGL->UpdateContextLossStatus();
     }
 }
 
+void
+WebGLContextLossHandler::RunTimer()
+{
+    MOZ_ASSERT(!mIsDisabled);
+
+    // If the timer was already running, don't restart it here. Instead,
+    // wait until the previous call is done, then fire it one more time.
+    // This is an optimization to prevent unnecessary
+    // cross-communication between threads.
+    if (mIsTimerRunning) {
+        mShouldRunTimerAgain = true;
+        return;
+    }
+
+    if (!NS_IsMainThread()) {
+        dom::workers::WorkerPrivate* workerPrivate =
+            dom::workers::GetCurrentThreadWorkerPrivate();
+        nsCOMPtr<nsIEventTarget> target = workerPrivate->GetEventTarget();
+        mTimer->SetTarget(new ContextLossWorkerEventTarget(target));
+        if (!mWorkerHolderAdded) {
+            HoldWorker(workerPrivate);
+            mWorkerHolderAdded = true;
+        }
+    }
+
+    StartTimer(1000);
+
+    mIsTimerRunning = true;
+    mShouldRunTimerAgain = false;
+}
+
+void
+WebGLContextLossHandler::DisableTimer()
+{
+    if (mIsDisabled)
+        return;
+
+    mIsDisabled = true;
+
+    if (mWorkerHolderAdded) {
+        dom::workers::WorkerPrivate* workerPrivate =
+            dom::workers::GetCurrentThreadWorkerPrivate();
+        MOZ_RELEASE_ASSERT(workerPrivate, "GFX: No private worker created.");
+        ReleaseWorker();
+        mWorkerHolderAdded = false;
+    }
+
+    // We can't just Cancel() the timer, as sometimes we end up
+    // receiving a callback after calling Cancel(). This could cause us
+    // to receive the callback after object destruction.
+
+    // Instead, we let the timer finish, but ignore it.
+
+    if (!mIsTimerRunning)
+        return;
+
+    mTimer->SetDelay(0);
+}
+
+bool
+WebGLContextLossHandler::Notify(dom::workers::Status aStatus)
+{
+    bool isWorkerRunning = aStatus < dom::workers::Closing;
+    if (!isWorkerRunning && mIsTimerRunning) {
+        mIsTimerRunning = false;
+        this->Release();
+    }
+
+    return true;
+}
+
 } // namespace mozilla
--- a/dom/canvas/WebGLContextLossHandler.h
+++ b/dom/canvas/WebGLContextLossHandler.h
@@ -1,44 +1,52 @@
 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* 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 WEBGL_CONTEXT_LOSS_HANDLER_H_
 #define WEBGL_CONTEXT_LOSS_HANDLER_H_
 
+#include "mozilla/DebugOnly.h"
 #include "mozilla/WeakPtr.h"
 #include "nsCOMPtr.h"
+#include "nsISupportsImpl.h"
+#include "WorkerHolder.h"
 
 class nsIThread;
 class nsITimer;
 
 namespace mozilla {
 class WebGLContext;
 
-class WebGLContextLossHandler final : public SupportsWeakPtr<WebGLContextLossHandler>
+class WebGLContextLossHandler : public dom::workers::WorkerHolder
 {
-    WebGLContext* const mWebGL;
-    const nsCOMPtr<nsITimer> mTimer; // If we don't hold a ref to the timer, it will think
-    bool mTimerPending;              // that it's been discarded, and be canceled 'for our
-    bool mShouldRunTimerAgain;       // convenience'.
+    WeakPtr<WebGLContext> mWeakWebGL;
+    nsCOMPtr<nsITimer> mTimer;
+    bool mIsTimerRunning;
+    bool mShouldRunTimerAgain;
+    bool mIsDisabled;
+    bool mWorkerHolderAdded;
 #ifdef DEBUG
-    nsIThread* const mThread;
+    nsIThread* mThread;
 #endif
 
-    friend class WatchdogTimerEvent;
-
 public:
-    MOZ_DECLARE_WEAKREFERENCE_TYPENAME(WebGLContextLossHandler)
+    NS_INLINE_DECL_REFCOUNTING(WebGLContextLossHandler)
 
     explicit WebGLContextLossHandler(WebGLContext* webgl);
-    ~WebGLContextLossHandler();
 
     void RunTimer();
+    void DisableTimer();
+    bool Notify(dom::workers::Status aStatus) override;
 
-private:
+protected:
+    ~WebGLContextLossHandler();
+
+    void StartTimer(unsigned long delayMS);
+    static void StaticTimerCallback(nsITimer*, void* tempRefForTimer);
     void TimerCallback();
 };
 
 } // namespace mozilla
 
-#endif // WEBGL_CONTEXT_LOSS_HANDLER_H_
+#endif