Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(552)

Unified Diff: content/public/android/java/src/org/chromium/content/common/CleanupReference.java

Issue 12658010: Improve CleanupReference & add test (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: <3 findbugs Created 7 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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;
}

Powered by Google App Engine
This is Rietveld 408576698