| Index: content/public/android/java/src/org/chromium/content/common/CleanupReference.java
|
| diff --git a/content/public/android/java/src/org/chromium/content/common/CleanupReference.java b/content/public/android/java/src/org/chromium/content/common/CleanupReference.java
|
| index cc53c018466ba147c7112936bbd52c6222292d78..695e587787c5a01254eef43f5d549fc264341774 100644
|
| --- a/content/public/android/java/src/org/chromium/content/common/CleanupReference.java
|
| +++ b/content/public/android/java/src/org/chromium/content/common/CleanupReference.java
|
| @@ -7,54 +7,68 @@ package org.chromium.content.common;
|
| import android.os.Handler;
|
| import android.os.Looper;
|
| import android.os.Message;
|
| +import android.util.Log;
|
|
|
| -import java.lang.ref.PhantomReference;
|
| import java.lang.ref.ReferenceQueue;
|
| +import java.lang.ref.WeakReference;
|
| import java.util.HashSet;
|
| import java.util.Set;
|
|
|
| /**
|
| - * Handles running cleanup tasks after an object has been GC'd. Cleanup tasks
|
| + * Handles running cleanup tasks when an object becomes eligible for GC. Cleanup tasks
|
| * are always executed on the main thread. In general, classes should not have
|
| * finalizers and likewise should not use this class for the same reasons. The
|
| * exception is where public APIs exist that require native side resources to be
|
| * cleaned up in response to java side GC of API objects. (Private/internal
|
| * interfaces should always favor explicit resource releases / destroy()
|
| * protocol for this rather than depend on GC to trigger native cleanup).
|
| + * NOTE this uses WeakReference rather than PhantomReference, to avoid delaying the
|
| + * cleanup processing until after finalizers (if any) have run. In general usage of
|
| + * this class indicates the client does NOT use finalizers anyway (Good), so this should
|
| + * not be a visible difference in practice.
|
| */
|
| -public class CleanupReference extends PhantomReference<Object> {
|
| +public class CleanupReference extends WeakReference<Object> {
|
| + private static final String TAG = "CleanupReference";
|
|
|
| - static {
|
| - // Bootstrap the cleanup trigger.
|
| - createGarbageCollectionDetector();
|
| - }
|
| + private static final boolean DEBUG = false; // Always check in as false!
|
|
|
| - /**
|
| - * Create a scratch object with a finalizer that triggers cleanup.
|
| - */
|
| - private static void createGarbageCollectionDetector() {
|
| - new Object() {
|
| - @Override
|
| - protected void finalize() throws Throwable {
|
| + // The VM will enqueue CleanupReference instance onto sGcQueue when it becomes eligible for
|
| + // garbage collection (i.e. when all references to the underlying object are nullified).
|
| + // |sReaperThread| processes this queue by forwarding the references on to the UI thread
|
| + // (via REMOVE_REF message) to perform cleanup.
|
| + private static ReferenceQueue<Object> sGcQueue = new ReferenceQueue<Object>();
|
| + private static Object sCleanupMonitor = new Object();
|
| +
|
| + static private final Thread sReaperThread = new Thread(TAG) {
|
| + public void run() {
|
| + while (true) {
|
| try {
|
| - Message.obtain(sHandler, CLEANUP_REFS).sendToTarget();
|
| - // Create a new detector since this one has now been GC'd.
|
| - createGarbageCollectionDetector();
|
| - } finally {
|
| - super.finalize();
|
| + CleanupReference ref = (CleanupReference) sGcQueue.remove();
|
| + if (DEBUG) Log.d(TAG, "removed one ref from GC queue");
|
| + synchronized (sCleanupMonitor) {
|
| + Message.obtain(sHandler, REMOVE_REF, ref).sendToTarget();
|
| + // Give the UI thread chance to run cleanup before looping around and
|
| + // taking the next item from the queue, to avoid Message bombing it.
|
| + sCleanupMonitor.wait(500);
|
| + }
|
| + } catch (Exception e) {
|
| + Log.e(TAG, "Queue remove exception:", e);
|
| }
|
| }
|
| - };
|
| + }
|
| + };
|
| +
|
| + static {
|
| + sReaperThread.setDaemon(true);
|
| + sReaperThread.start();
|
| }
|
|
|
| // Message's sent in the |what| field to |sHandler|.
|
|
|
| // Add a new reference to sRefs. |msg.obj| is the CleanupReference to add.
|
| - static final int ADD_REF = 1;
|
| + private static final int ADD_REF = 1;
|
| // Remove reference from sRefs. |msg.obj| is the CleanupReference to remove.
|
| - static final int REMOVE_REF = 2;
|
| - // Flush out pending items on the reference queue (i.e. run the finalizer actions).
|
| - static final int CLEANUP_REFS = 3;
|
| + private static final int REMOVE_REF = 2;
|
|
|
| /**
|
| * This {@link Handler} polls {@link #sRefs}, looking for cleanup tasks that
|
| @@ -62,7 +76,7 @@ public class CleanupReference extends PhantomReference<Object> {
|
| */
|
| private static Handler sHandler = new Handler(Looper.getMainLooper()) {
|
| @Override
|
| - public void handleMessage(android.os.Message msg) {
|
| + public void handleMessage(Message msg) {
|
| TraceEvent.begin();
|
| CleanupReference ref = (CleanupReference) msg.obj;
|
| switch (msg.what) {
|
| @@ -72,21 +86,25 @@ public class CleanupReference extends PhantomReference<Object> {
|
| case REMOVE_REF:
|
| ref.runCleanupTaskInternal();
|
| break;
|
| - case CLEANUP_REFS:
|
| - break; // Handled below
|
| + default:
|
| + Log.e(TAG, "Bad message=" + msg.what);
|
| + break;
|
| }
|
|
|
| - // Always run the cleanup loop here even when adding or removing refs, to avoid
|
| - // falling behind on rapid allocation/GC inner loops.
|
| - while ((ref = (CleanupReference) sQueue.poll()) != null) {
|
| - ref.runCleanupTaskInternal();
|
| + if (DEBUG) Log.d(TAG, "will try and cleanup; max = " + sRefs.size());
|
| +
|
| + synchronized (sCleanupMonitor) {
|
| + // Always run the cleanup loop here even when adding or removing refs, to avoid
|
| + // falling behind on rapid garbage allocation inner loops.
|
| + while ((ref = (CleanupReference) sGcQueue.poll()) != null) {
|
| + ref.runCleanupTaskInternal();
|
| + }
|
| + sCleanupMonitor.notifyAll();
|
| }
|
| TraceEvent.end();
|
| }
|
| };
|
|
|
| - private static ReferenceQueue<Object> sQueue = new ReferenceQueue<Object>();
|
| -
|
| /**
|
| * Keep a strong reference to {@link CleanupReference} so that it will
|
| * actually get enqueued.
|
| @@ -102,7 +120,8 @@ public class CleanupReference extends PhantomReference<Object> {
|
| * @param cleanupTask the task to run once obj loses reachability.
|
| */
|
| public CleanupReference(Object obj, Runnable cleanupTask) {
|
| - super(obj, sQueue);
|
| + super(obj, sGcQueue);
|
| + if (DEBUG) Log.d(TAG, "+++ CREATED ONE REF");
|
| mCleanupTask = cleanupTask;
|
| handleOnUiThread(ADD_REF);
|
| }
|
| @@ -119,14 +138,17 @@ public class CleanupReference extends PhantomReference<Object> {
|
| Message msg = Message.obtain(sHandler, what, this);
|
| if (Looper.myLooper() == sHandler.getLooper()) {
|
| sHandler.handleMessage(msg);
|
| + msg.recycle();
|
| } else {
|
| msg.sendToTarget();
|
| }
|
| }
|
|
|
| private void runCleanupTaskInternal() {
|
| + if (DEBUG) Log.d(TAG, "runCleanupTaskInternal");
|
| sRefs.remove(this);
|
| if (mCleanupTask != null) {
|
| + if (DEBUG) Log.i(TAG, "--- CLEANING ONE REF");
|
| mCleanupTask.run();
|
| mCleanupTask = null;
|
| }
|
|
|