Bug 1057061 - Handle timer lifetime outlasting WebGLContext. - r=kamidphish on a CLOSED TREE FIREFOX_BETA_32_END
authorJeff Gilbert <jgilbert@mozilla.com>
Mon, 25 Aug 2014 14:13:29 -0700
changeset 209471 ebd0ee3e97dc2756d979261b29f173638fe6aeb6
parent 209466 f3195212d160e37bc8892f9e2778c94af915d3cd
child 209472 5b8de9e4c2ea94334f37992e90ded50aadddd478
child 209509 7fd4acf361a3fdd6030e85290111693750f9d850
child 217763 fe017d6de6b54b807dc05c201b908c54bb183811
push id495
push userryanvm@gmail.com
push dateMon, 25 Aug 2014 22:35:54 +0000
treeherdermozilla-release@5b8de9e4c2ea [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskamidphish
bugs1057061
milestone32.0
Bug 1057061 - Handle timer lifetime outlasting WebGLContext. - r=kamidphish on a CLOSED TREE
content/canvas/src/WebGLContext.cpp
content/canvas/src/WebGLContext.h
content/canvas/src/WebGLContextLossHandler.cpp
content/canvas/src/WebGLContextLossHandler.h
content/canvas/src/WebGLContextLossTimer.cpp
content/canvas/src/moz.build
--- a/content/canvas/src/WebGLContext.cpp
+++ b/content/canvas/src/WebGLContext.cpp
@@ -1,14 +1,16 @@
 /* -*- 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 "WebGLContext.h"
+
+#include "WebGLContextLossHandler.h"
 #include "WebGL1Context.h"
 #include "WebGLObjectModel.h"
 #include "WebGLExtensions.h"
 #include "WebGLContextUtils.h"
 #include "WebGLBuffer.h"
 #include "WebGLVertexAttribData.h"
 #include "WebGLMemoryTracker.h"
 #include "WebGLFramebuffer.h"
@@ -168,19 +170,17 @@ WebGLContext::WebGLContext()
     // See OpenGL ES 2.0.25 spec, 6.2 State Tables, table 6.13
     mPixelStorePackAlignment = 4;
     mPixelStoreUnpackAlignment = 4;
 
     WebGLMemoryTracker::AddWebGLContext(this);
 
     mAllowContextRestore = true;
     mLastLossWasSimulated = false;
-    mContextLossTimerRunning = false;
-    mRunContextLossTimerAgain = false;
-    mContextRestorer = do_CreateInstance("@mozilla.org/timer;1");
+    mContextLossHandler = new WebGLContextLossHandler(this);
     mContextStatus = ContextNotLost;
     mLoseContextOnHeapMinimize = false;
     mCanLoseContextInForeground = true;
 
     mAlreadyGeneratedWarnings = 0;
     mAlreadyWarnedAboutFakeVertexAttrib0 = false;
     mAlreadyWarnedAboutViewportLargerThanDest = false;
     mMaxWarnings = Preferences::GetInt("webgl.max-warnings-per-context", 32);
@@ -200,18 +200,19 @@ WebGLContext::WebGLContext()
 
     mDrawCallsSinceLastFlush = 0;
 }
 
 WebGLContext::~WebGLContext()
 {
     DestroyResourcesAndContext();
     WebGLMemoryTracker::RemoveWebGLContext(this);
-    TerminateContextLossTimer();
-    mContextRestorer = nullptr;
+
+    mContextLossHandler->DisableTimer();
+    mContextLossHandler = nullptr;
 }
 
 void
 WebGLContext::DestroyResourcesAndContext()
 {
     if (mMemoryPressureObserver) {
         nsCOMPtr<nsIObserverService> observerService
             = mozilla::services::GetObserverService();
@@ -1176,16 +1177,22 @@ bool
 WebGLContext::TryToRestoreContext()
 {
     if (NS_FAILED(SetDimensions(mWidth, mHeight)))
         return false;
 
     return true;
 }
 
+void
+WebGLContext::RunContextLossTimer()
+{
+    mContextLossHandler->RunTimer();
+}
+
 class UpdateContextLossStatusTask : public nsRunnable
 {
     nsRefPtr<WebGLContext> mContext;
 
 public:
     UpdateContextLossStatusTask(WebGLContext* context)
         : mContext(context)
     {
@@ -1292,17 +1299,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.
-            RunContextLossTimer();
+            mContextLossHandler->RunTimer();
             return;
         }
 
         // Revival!
         mContextStatus = ContextNotLost;
         nsContentUtils::DispatchTrustedEvent(mCanvasElement->OwnerDoc(),
                                              static_cast<nsIDOMHTMLCanvasElement*>(mCanvasElement),
                                              NS_LITERAL_STRING("webglcontextrestored"),
--- a/content/canvas/src/WebGLContext.h
+++ b/content/canvas/src/WebGLContext.h
@@ -2,16 +2,17 @@
 /* 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 WEBGLCONTEXT_H_
 #define WEBGLCONTEXT_H_
 
 #include "mozilla/Attributes.h"
+#include "mozilla/WeakPtr.h"
 #include "GLDefs.h"
 #include "WebGLActiveInfo.h"
 #include "WebGLObjectModel.h"
 #include "WebGLRenderbuffer.h"
 #include <stdarg.h>
 
 #include "nsTArray.h"
 #include "nsCycleCollectionNoteChild.h"
@@ -55,16 +56,17 @@ class nsIDocShell;
 #define MINVALUE_GL_MAX_VARYING_VECTORS               8     // Page 164
 #define MINVALUE_GL_MAX_TEXTURE_IMAGE_UNITS           8     // Page 164
 #define MINVALUE_GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS    0     // Page 164
 #define MINVALUE_GL_MAX_RENDERBUFFER_SIZE             1024  // Different from the spec, which sets it to 1 on page 164
 #define MINVALUE_GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS  8     // Page 164
 
 namespace mozilla {
 
+class WebGLContextLossHandler;
 class WebGLMemoryPressureObserver;
 class WebGLContextBoundObject;
 class WebGLActiveInfo;
 class WebGLExtensionBase;
 class WebGLBuffer;
 class WebGLVertexAttribData;
 class WebGLShader;
 class WebGLProgram;
@@ -130,17 +132,18 @@ IsTextureBinding(GLenum binding)
 }
 #endif
 
 class WebGLContext :
     public nsIDOMWebGLRenderingContext,
     public nsICanvasRenderingContextInternal,
     public nsSupportsWeakReference,
     public WebGLRectangleObject,
-    public nsWrapperCache
+    public nsWrapperCache,
+    public SupportsWeakPtr<WebGLContext>
 {
     friend class WebGLContextUserData;
     friend class WebGLExtensionCompressedTextureATC;
     friend class WebGLExtensionCompressedTextureETC1;
     friend class WebGLExtensionCompressedTexturePVRTC;
     friend class WebGLExtensionCompressedTextureS3TC;
     friend class WebGLExtensionDepthTexture;
     friend class WebGLExtensionDrawBuffers;
@@ -155,16 +158,18 @@ class WebGLContext :
         CONTEXT_LOST_WEBGL = 0x9242,
         UNPACK_COLORSPACE_CONVERSION_WEBGL = 0x9243,
         BROWSER_DEFAULT_WEBGL = 0x9244,
         UNMASKED_VENDOR_WEBGL = 0x9245,
         UNMASKED_RENDERER_WEBGL = 0x9246
     };
 
 public:
+    MOZ_DECLARE_REFCOUNTED_TYPENAME(WebGLContext)
+
     WebGLContext();
     virtual ~WebGLContext();
 
     NS_DECL_CYCLE_COLLECTING_ISUPPORTS
 
     NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(WebGLContext,
                                                            nsIDOMWebGLRenderingContext)
 
@@ -261,21 +266,19 @@ public:
     void ForceClearFramebufferWithDefaultValues(GLbitfield mask, const bool colorAttachmentsMask[kMaxColorAttachments]);
 
     // Calls ForceClearFramebufferWithDefaultValues() for the Context's 'screen'.
     void ClearScreen();
     void ClearBackbufferIfNeeded();
 
     bool MinCapabilityMode() const { return mMinCapability; }
 
+    void RunContextLossTimer();
     void UpdateContextLossStatus();
     void EnqueueUpdateContextLossStatus();
-    static void ContextLossCallbackStatic(nsITimer* timer, void* thisPointer);
-    void RunContextLossTimer();
-    void TerminateContextLossTimer();
 
     bool TryToRestoreContext();
 
     void AssertCachedBindings();
     void AssertCachedState();
 
     // WebIDL WebGLRenderingContext API
     dom::HTMLCanvasElement* GetCanvas() const { return mCanvasElement; }
@@ -1231,21 +1234,19 @@ protected:
     GLfloat mDepthClearValue;
 
     GLint mViewportX;
     GLint mViewportY;
     GLsizei mViewportWidth;
     GLsizei mViewportHeight;
     bool mAlreadyWarnedAboutViewportLargerThanDest;
 
-    nsCOMPtr<nsITimer> mContextRestorer;
+    RefPtr<WebGLContextLossHandler> mContextLossHandler;
     bool mAllowContextRestore;
     bool mLastLossWasSimulated;
-    bool mContextLossTimerRunning;
-    bool mRunContextLossTimerAgain;
     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;
 
     int mAlreadyGeneratedWarnings;
new file mode 100644
--- /dev/null
+++ b/content/canvas/src/WebGLContextLossHandler.cpp
@@ -0,0 +1,121 @@
+/* -*- 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 "nsITimer.h"
+#include "nsThreadUtils.h"
+#include "WebGLContext.h"
+
+namespace mozilla {
+
+WebGLContextLossHandler::WebGLContextLossHandler(WebGLContext* webgl)
+    : mWeakWebGL(webgl->asWeakPtr())
+    , mTimer(do_CreateInstance(NS_TIMER_CONTRACTID))
+    , mIsTimerRunning(false)
+    , mShouldRunTimerAgain(false)
+    , mIsDisabled(false)
+#ifdef DEBUG
+    , mThread(NS_GetCurrentThread())
+#endif
+{
+}
+
+WebGLContextLossHandler::~WebGLContextLossHandler()
+{
+    MOZ_ASSERT(!mIsTimerRunning);
+}
+
+void
+WebGLContextLossHandler::StartTimer(unsigned long delayMS)
+{
+    // We can't pass a TemporaryRef through InitWithFuncCallback, so we
+    // should do the AddRef/Release manually.
+    this->AddRef();
+
+    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);
+
+    if (mIsDisabled)
+        return;
+
+    MOZ_ASSERT(mIsTimerRunning);
+    mIsTimerRunning = 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);
+    }
+
+    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;
+    }
+
+    StartTimer(1000);
+
+    mIsTimerRunning = true;
+    mShouldRunTimerAgain = false;
+}
+
+void
+WebGLContextLossHandler::DisableTimer()
+{
+    if (!mIsDisabled)
+        return;
+
+    mIsDisabled = true;
+
+    // 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);
+}
+
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/content/canvas/src/WebGLContextLossHandler.h
@@ -0,0 +1,47 @@
+/* -*- 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/RefPtr.h"
+#include "mozilla/WeakPtr.h"
+#include "nsCOMPtr.h"
+
+class nsIThread;
+class nsITimer;
+
+namespace mozilla {
+class WebGLContext;
+
+class WebGLContextLossHandler
+    : public RefCounted<WebGLContextLossHandler>
+{
+    WeakPtr<WebGLContext> mWeakWebGL;
+    nsCOMPtr<nsITimer> mTimer;
+    bool mIsTimerRunning;
+    bool mShouldRunTimerAgain;
+    bool mIsDisabled;
+    DebugOnly<nsIThread*> mThread;
+
+public:
+    MOZ_DECLARE_REFCOUNTED_TYPENAME(WebGLContextLossHandler)
+
+    WebGLContextLossHandler(WebGLContext* webgl);
+    ~WebGLContextLossHandler();
+
+    void RunTimer();
+    void DisableTimer();
+
+protected:
+    void StartTimer(unsigned long delayMS);
+    static void StaticTimerCallback(nsITimer*, void* tempRefForTimer);
+    void TimerCallback();
+};
+
+} // namespace mozilla
+
+#endif
deleted file mode 100644
--- a/content/canvas/src/WebGLContextLossTimer.cpp
+++ /dev/null
@@ -1,52 +0,0 @@
-/* -*- 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 "WebGLContext.h"
-
-using namespace mozilla;
-
-/* static */ void
-WebGLContext::ContextLossCallbackStatic(nsITimer* timer, void* thisPointer)
-{
-    (void)timer;
-    WebGLContext* context = static_cast<WebGLContext*>(thisPointer);
-
-    context->TerminateContextLossTimer();
-
-    context->UpdateContextLossStatus();
-}
-
-void
-WebGLContext::RunContextLossTimer()
-{
-    // 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 (mContextLossTimerRunning) {
-        mRunContextLossTimerAgain = true;
-        return;
-    }
-    mContextRestorer->InitWithFuncCallback(ContextLossCallbackStatic,
-                                           static_cast<void*>(this),
-                                           1000,
-                                           nsITimer::TYPE_ONE_SHOT);
-    mContextLossTimerRunning = true;
-    mRunContextLossTimerAgain = false;
-}
-
-void
-WebGLContext::TerminateContextLossTimer()
-{
-    if (!mContextLossTimerRunning)
-        return;
-
-    mContextRestorer->Cancel();
-    mContextLossTimerRunning = false;
-
-    if (mRunContextLossTimerAgain) {
-        RunContextLossTimer();
-    }
-}
--- a/content/canvas/src/moz.build
+++ b/content/canvas/src/moz.build
@@ -31,17 +31,17 @@ if CONFIG['MOZ_WEBGL']:
         'WebGLBuffer.cpp',
         'WebGLContext.cpp',
         'WebGLContextAsyncQueries.cpp',
         'WebGLContextBuffers.cpp',
         'WebGLContextDraw.cpp',
         'WebGLContextExtensions.cpp',
         'WebGLContextFramebufferOperations.cpp',
         'WebGLContextGL.cpp',
-        'WebGLContextLossTimer.cpp',
+        'WebGLContextLossHandler.cpp',
         'WebGLContextReporter.cpp',
         'WebGLContextState.cpp',
         'WebGLContextUtils.cpp',
         'WebGLContextValidate.cpp',
         'WebGLContextVertexArray.cpp',
         'WebGLContextVertices.cpp',
         'WebGLElementArrayCache.cpp',
         'WebGLExtensionBase.cpp',