Bug 741319 - Delete textures using the same context used to create them, on the thread that owns that context. r=joe
authorAli Juma <ajuma@mozilla.com>
Tue, 10 Apr 2012 16:20:02 -0400
changeset 94668 b1421c3cd5c88682da29e17fb2a43a76b3ce2e85
parent 94667 b5c80ac325c1d2c68af68ccfea7f08f604ba5fc1
child 94669 3bc6b34d23daf6df7f38f2fe48c6388d408454ea
push id886
push userlsblakk@mozilla.com
push dateMon, 04 Jun 2012 19:57:52 +0000
treeherdermozilla-beta@bbd8d5efd6d1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjoe
bugs741319
milestone14.0a1
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
Bug 741319 - Delete textures using the same context used to create them, on the thread that owns that context. r=joe
gfx/gl/GLContext.cpp
gfx/gl/GLContext.h
gfx/gl/GLContextProviderEGL.cpp
gfx/layers/opengl/ImageLayerOGL.cpp
--- a/gfx/gl/GLContext.cpp
+++ b/gfx/gl/GLContext.cpp
@@ -761,17 +761,17 @@ void GLContext::ApplyFilterToBoundTextur
         fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MIN_FILTER, LOCAL_GL_LINEAR);
        fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MAG_FILTER, LOCAL_GL_LINEAR);
     }
 }
 
 BasicTextureImage::~BasicTextureImage()
 {
     GLContext *ctx = mGLContext;
-    if (ctx->IsDestroyed() || !NS_IsMainThread()) {
+    if (ctx->IsDestroyed() || !ctx->IsOwningThreadCurrent()) {
         ctx = ctx->GetSharedContext();
     }
 
     // If we have a context, then we need to delete the texture;
     // if we don't have a context (either real or shared),
     // then they went away when the contex was deleted, because it
     // was the only one that had access to it.
     if (ctx && !ctx->IsDestroyed()) {
--- a/gfx/gl/GLContext.h
+++ b/gfx/gl/GLContext.h
@@ -549,16 +549,17 @@ public:
         mMaxTextureImageSize(0),
         mMaxRenderbufferSize(0),
         mWorkAroundDriverBugs(true)
 #ifdef DEBUG
         , mGLError(LOCAL_GL_NO_ERROR)
 #endif
     {
         mUserData.Init();
+        mOwningThread = NS_GetCurrentThread();
     }
 
     virtual ~GLContext() {
         NS_ASSERTION(IsDestroyed(), "GLContext implementation must call MarkDestroyed in destructor!");
 #ifdef DEBUG
         if (mSharedContext) {
             GLContext *tip = mSharedContext;
             while (tip->mSharedContext)
@@ -635,16 +636,33 @@ public:
     };
 
     virtual void *GetNativeData(NativeDataType aType) { return NULL; }
     GLContext *GetSharedContext() { return mSharedContext; }
 
     bool IsGlobalSharedContext() { return mIsGlobalSharedContext; }
     void SetIsGlobalSharedContext(bool aIsOne) { mIsGlobalSharedContext = aIsOne; }
 
+    /**
+     * Returns true if the thread on which this context was created is the currently
+     * executing thread.
+     */
+    bool IsOwningThreadCurrent() { return NS_GetCurrentThread() == mOwningThread; }
+
+    void DispatchToOwningThread(nsIRunnable *event) {
+        // Before dispatching, we need to ensure we're not in the middle of
+        // shutting down. Dispatching runnables in the middle of shutdown
+        // (that is, when the main thread is no longer get-able) can cause them
+        // to leak. See Bug 741319, and Bug 744115.
+        nsCOMPtr<nsIThread> mainThread;
+        if (NS_SUCCEEDED(NS_GetMainThread(getter_AddRefs(mainThread)))) {
+            mOwningThread->Dispatch(event, NS_DISPATCH_NORMAL);
+        }
+    }
+
     const ContextFormat& CreationFormat() { return mCreationFormat; }
     const ContextFormat& ActualFormat() { return mActualFormat; }
 
     /**
      * If this GL context has a D3D texture share handle, returns non-null.
      */
     virtual void *GetD3DShareHandle() { return nsnull; }
 
@@ -1576,16 +1594,19 @@ public:
 #endif
     }
 
 protected:
 
     ContextFormat mCreationFormat;
     nsRefPtr<GLContext> mSharedContext;
 
+    // The thread on which this context was created.
+    nsCOMPtr<nsIThread> mOwningThread;
+
     GLContextSymbols mSymbols;
 
 #ifdef DEBUG
     // GLDebugMode will check that we don't send call
     // to a GLContext that isn't current on the current
     // thread.
     // Store the current context when binding to thread local
     // storage to support DebugMode on an arbitrary thread.
--- a/gfx/gl/GLContextProviderEGL.cpp
+++ b/gfx/gl/GLContextProviderEGL.cpp
@@ -869,17 +869,17 @@ public:
                 mShaderType = BGRALayerProgramType;
             }
         }
     }
 
     virtual ~TextureImageEGL()
     {
         GLContext *ctx = mGLContext;
-        if (ctx->IsDestroyed() || !NS_IsMainThread()) {
+        if (ctx->IsDestroyed() || !ctx->IsOwningThreadCurrent()) {
             ctx = ctx->GetSharedContext();
         }
 
         // If we have a context, then we need to delete the texture;
         // if we don't have a context (either real or shared),
         // then they went away when the contex was deleted, because it
         // was the only one that had access to it.
         if (ctx && !ctx->IsDestroyed()) {
--- a/gfx/layers/opengl/ImageLayerOGL.cpp
+++ b/gfx/layers/opengl/ImageLayerOGL.cpp
@@ -78,18 +78,18 @@ public:
 
   nsRefPtr<GLContext> mContext;
   GLuint mTexture;
 };
 
 void
 GLTexture::Allocate(GLContext *aContext)
 {
-  NS_ASSERTION(aContext->IsGlobalSharedContext() ||
-               NS_IsMainThread(), "Can only allocate texture on main thread or with cx sharing");
+  NS_ASSERTION(aContext->IsGlobalSharedContext() || aContext->IsOwningThreadCurrent(),
+               "Can only allocate texture on context's owning thread or with cx sharing");
 
   Release();
 
   mContext = aContext;
 
   mContext->MakeCurrent();
   mContext->fGenTextures(1, &mTexture);
 }
@@ -117,23 +117,24 @@ GLTexture::Release()
     if (!mContext) {
       NS_ASSERTION(!mTexture, 
                    "Context has been destroyed and couldn't find a shared context!");
       return;
     }
   }
 
   if (mTexture) {
-    if (NS_IsMainThread() || mContext->IsGlobalSharedContext()) {
+    if (mContext->IsOwningThreadCurrent() || mContext->IsGlobalSharedContext()) {
       mContext->MakeCurrent();
       mContext->fDeleteTextures(1, &mTexture);
     } else {
       nsCOMPtr<nsIRunnable> runnable =
-        new TextureDeleter(mContext.forget(), mTexture);
-      NS_DispatchToMainThread(runnable);
+        new TextureDeleter(mContext.get(), mTexture);
+      mContext->DispatchToOwningThread(runnable);
+      mContext.forget();
     }
 
     mTexture = 0;
   }
 
   mContext = nsnull;
 }