Free resources only from the GL context thread.
Bug #3179882

Resources were freed following garbage collections on a worker thread.
This worker thread had no EGL context, which would cause the renderer
to incorrectly assume that the memory was liberated.

Change-Id: Ifdb51f94ddf42641e8654522787bfac532976c7c
diff --git a/libs/hwui/PathCache.cpp b/libs/hwui/PathCache.cpp
index 7ff26dc..8740a64 100644
--- a/libs/hwui/PathCache.cpp
+++ b/libs/hwui/PathCache.cpp
@@ -53,7 +53,6 @@
 }
 
 PathCache::~PathCache() {
-    Mutex::Autolock _l(mLock);
     mCache.clear();
 }
 
@@ -72,17 +71,14 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 uint32_t PathCache::getSize() {
-    Mutex::Autolock _l(mLock);
     return mSize;
 }
 
 uint32_t PathCache::getMaxSize() {
-    Mutex::Autolock _l(mLock);
     return mMaxSize;
 }
 
 void PathCache::setMaxSize(uint32_t maxSize) {
-    Mutex::Autolock _l(mLock);
     mMaxSize = maxSize;
     while (mSize > mMaxSize) {
         mCache.removeOldest();
@@ -94,6 +90,14 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 void PathCache::operator()(PathCacheEntry& path, PathTexture*& texture) {
+    removeTexture(texture);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Caching
+///////////////////////////////////////////////////////////////////////////////
+
+void PathCache::removeTexture(PathTexture* texture) {
     if (texture) {
         const uint32_t size = texture->width * texture->height;
         mSize -= size;
@@ -109,39 +113,46 @@
     }
 }
 
-///////////////////////////////////////////////////////////////////////////////
-// Caching
-///////////////////////////////////////////////////////////////////////////////
-
 void PathCache::remove(SkPath* path) {
-    Mutex::Autolock _l(mLock);
-
     // TODO: Linear search...
     Vector<uint32_t> pathsToRemove;
     for (uint32_t i = 0; i < mCache.size(); i++) {
         if (mCache.getKeyAt(i).path == path) {
             pathsToRemove.push(i);
+            removeTexture(mCache.getValueAt(i));
         }
     }
 
+    mCache.setOnEntryRemovedListener(NULL);
     for (size_t i = 0; i < pathsToRemove.size(); i++) {
         mCache.removeAt(pathsToRemove.itemAt(i));
     }
+    mCache.setOnEntryRemovedListener(this);
+}
+
+void PathCache::removeDeferred(SkPath* path) {
+    Mutex::Autolock _l(mLock);
+    mGarbage.push(path);
+}
+
+void PathCache::clearGarbage() {
+    Mutex::Autolock _l(mLock);
+    size_t count = mGarbage.size();
+    for (size_t i = 0; i < count; i++) {
+        remove(mGarbage.itemAt(i));
+    }
+    mGarbage.clear();
 }
 
 PathTexture* PathCache::get(SkPath* path, SkPaint* paint) {
     PathCacheEntry entry(path, paint);
 
-    mLock.lock();
     PathTexture* texture = mCache.get(entry);
-    mLock.unlock();
 
     if (!texture) {
         texture = addTexture(entry, path, paint);
     } else if (path->getGenerationID() != texture->generation) {
-        mLock.lock();
         mCache.remove(entry);
-        mLock.unlock();
         texture = addTexture(entry, path, paint);
     }
 
@@ -167,11 +178,9 @@
     const uint32_t size = width * height;
     // Don't even try to cache a bitmap that's bigger than the cache
     if (size < mMaxSize) {
-        mLock.lock();
         while (mSize + size > mMaxSize) {
             mCache.removeOldest();
         }
-        mLock.unlock();
     }
 
     PathTexture* texture = new PathTexture;
@@ -200,7 +209,6 @@
     generateTexture(bitmap, texture);
 
     if (size < mMaxSize) {
-        mLock.lock();
         mSize += size;
         PATH_LOGD("PathCache::get: create path: name, size, mSize = %d, %d, %d",
                 texture->id, size, mSize);
@@ -208,7 +216,6 @@
             LOGD("Path created, size = %d", size);
         }
         mCache.put(entry, texture);
-        mLock.unlock();
     } else {
         texture->cleanup = true;
     }
@@ -217,7 +224,6 @@
 }
 
 void PathCache::clear() {
-    Mutex::Autolock _l(mLock);
     mCache.clear();
 }