Fix destruction issues relating to AllocationAdapter.

bug 13170046

Change-Id: I922b19c086b675949c6fae66c6dcb4c8af3b715f
diff --git a/graphics/java/android/renderscript/BaseObj.java b/graphics/java/android/renderscript/BaseObj.java
index e17d79a..6f2726e 100644
--- a/graphics/java/android/renderscript/BaseObj.java
+++ b/graphics/java/android/renderscript/BaseObj.java
@@ -16,7 +16,7 @@
 
 package android.renderscript;
 
-import android.util.Log;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
 
 /**
  * BaseObj is the base class for all RenderScript objects owned by a RS context.
@@ -109,17 +109,30 @@
         return mName;
     }
 
-    protected void finalize() throws Throwable {
-        if (!mDestroyed) {
-            if(mID != 0 && mRS.isAlive()) {
+    private void helpDestroy() {
+        boolean shouldDestroy = false;
+        synchronized(this) {
+            if (!mDestroyed) {
+                shouldDestroy = true;
+                mDestroyed = true;
+            }
+        }
+
+        if (shouldDestroy) {
+            // must include nObjDestroy in the critical section
+            ReentrantReadWriteLock.ReadLock rlock = mRS.mRWLock.readLock();
+            rlock.lock();
+            if(mRS.isAlive()) {
                 mRS.nObjDestroy(mID);
             }
+            rlock.unlock();
             mRS = null;
             mID = 0;
-            mDestroyed = true;
-            //Log.v(RenderScript.LOG_TAG, getClass() +
-            // " auto finalizing object without having released the RS reference.");
         }
+    }
+
+    protected void finalize() throws Throwable {
+        helpDestroy();
         super.finalize();
     }
 
@@ -128,12 +141,11 @@
      * primary use is to force immediate cleanup of resources when it is
      * believed the GC will not respond quickly enough.
      */
-    synchronized public void destroy() {
+    public void destroy() {
         if(mDestroyed) {
             throw new RSInvalidStateException("Object already destroyed.");
         }
-        mDestroyed = true;
-        mRS.nObjDestroy(mID);
+        helpDestroy();
     }
 
     /**
diff --git a/graphics/java/android/renderscript/RenderScript.java b/graphics/java/android/renderscript/RenderScript.java
index 7d4a5c4..4f59af8 100644
--- a/graphics/java/android/renderscript/RenderScript.java
+++ b/graphics/java/android/renderscript/RenderScript.java
@@ -19,6 +19,7 @@
 import java.io.File;
 import java.lang.reflect.Field;
 import java.lang.reflect.Method;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
 
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
@@ -151,6 +152,7 @@
     }
 
     ContextType mContextType;
+    ReentrantReadWriteLock mRWLock;
 
     // Methods below are wrapped to protect the non-threadsafe
     // lockless fifo.
@@ -178,7 +180,18 @@
     native void rsnContextDestroy(int con);
     synchronized void nContextDestroy() {
         validate();
-        rsnContextDestroy(mContext);
+
+        // take teardown lock
+        // teardown lock can only be taken when no objects are being destroyed
+        ReentrantReadWriteLock.WriteLock wlock = mRWLock.writeLock();
+        wlock.lock();
+
+        int curCon = mContext;
+        // context is considered dead as of this point
+        mContext = 0;
+
+        wlock.unlock();
+        rsnContextDestroy(curCon);
     }
     native void rsnContextSetSurface(int con, int w, int h, Surface sur);
     synchronized void nContextSetSurface(int w, int h, Surface sur) {
@@ -263,8 +276,9 @@
         validate();
         return rsnGetName(mContext, obj);
     }
+    // nObjDestroy is explicitly _not_ synchronous to prevent crashes in finalizers
     native void rsnObjDestroy(int con, int id);
-    synchronized void nObjDestroy(int id) {
+    void nObjDestroy(int id) {
         // There is a race condition here.  The calling code may be run
         // by the gc while teardown is occuring.  This protects againts
         // deleting dead objects.
@@ -1136,6 +1150,7 @@
         if (ctx != null) {
             mApplicationContext = ctx.getApplicationContext();
         }
+        mRWLock = new ReentrantReadWriteLock();
     }
 
     /**
@@ -1230,6 +1245,8 @@
      */
     public void destroy() {
         validate();
+        nContextFinish();
+
         nContextDeinitToClient(mContext);
         mMessageThread.mRun = false;
         try {
@@ -1238,7 +1255,6 @@
         }
 
         nContextDestroy();
-        mContext = 0;
 
         nDeviceDestroy(mDev);
         mDev = 0;