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

Side by Side 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 unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 package org.chromium.content.common; 5 package org.chromium.content.common;
6 6
7 import android.os.Handler; 7 import android.os.Handler;
8 import android.os.Looper; 8 import android.os.Looper;
9 import android.os.Message; 9 import android.os.Message;
10 import android.util.Log;
10 11
11 import java.lang.ref.PhantomReference;
12 import java.lang.ref.ReferenceQueue; 12 import java.lang.ref.ReferenceQueue;
13 import java.lang.ref.WeakReference;
13 import java.util.HashSet; 14 import java.util.HashSet;
14 import java.util.Set; 15 import java.util.Set;
15 16
16 /** 17 /**
17 * Handles running cleanup tasks after an object has been GC'd. Cleanup tasks 18 * Handles running cleanup tasks when an object becomes eligible for GC. Cleanup tasks
18 * are always executed on the main thread. In general, classes should not have 19 * are always executed on the main thread. In general, classes should not have
19 * finalizers and likewise should not use this class for the same reasons. The 20 * finalizers and likewise should not use this class for the same reasons. The
20 * exception is where public APIs exist that require native side resources to be 21 * exception is where public APIs exist that require native side resources to be
21 * cleaned up in response to java side GC of API objects. (Private/internal 22 * cleaned up in response to java side GC of API objects. (Private/internal
22 * interfaces should always favor explicit resource releases / destroy() 23 * interfaces should always favor explicit resource releases / destroy()
23 * protocol for this rather than depend on GC to trigger native cleanup). 24 * protocol for this rather than depend on GC to trigger native cleanup).
25 * NOTE this uses WeakReference rather than PhantomReference, to avoid delaying the
26 * cleanup processing until after finalizers (if any) have run. In general usage of
27 * this class indicates the client does NOT use finalizers anyway (Good), so thi s should
28 * not be a visible difference in practice.
24 */ 29 */
25 public class CleanupReference extends PhantomReference<Object> { 30 public class CleanupReference extends WeakReference<Object> {
31 private static final String TAG = "CleanupReference";
32
33 private static final boolean DEBUG = false; // Always check in as false!
34
35 // The VM will enqueue CleanupReference instance onto sGcQueue when it becom es eligible for
36 // garbage collection (i.e. when all references to the underlying object are nullified).
37 // |sReaperThread| processes this queue by forwarding the references on to t he UI thread
38 // (via REMOVE_REF message) to perform cleanup.
39 private static ReferenceQueue<Object> sGcQueue = new ReferenceQueue<Object>( );
40 private static Object sCleanupMonitor = new Object();
41
42 static private final Thread sReaperThread = new Thread(TAG) {
43 public void run() {
44 while (true) {
45 try {
46 CleanupReference ref = (CleanupReference) sGcQueue.remove();
47 if (DEBUG) Log.d(TAG, "removed one ref from GC queue");
48 synchronized (sCleanupMonitor) {
49 Message.obtain(sHandler, REMOVE_REF, ref).sendToTarget() ;
50 // Give the UI thread chance to run cleanup before loopi ng around and
51 // taking the next item from the queue, to avoid Message bombing it.
52 sCleanupMonitor.wait(500);
53 }
54 } catch (Exception e) {
55 Log.e(TAG, "Queue remove exception:", e);
56 }
57 }
58 }
59 };
26 60
27 static { 61 static {
28 // Bootstrap the cleanup trigger. 62 sReaperThread.setDaemon(true);
29 createGarbageCollectionDetector(); 63 sReaperThread.start();
30 }
31
32 /**
33 * Create a scratch object with a finalizer that triggers cleanup.
34 */
35 private static void createGarbageCollectionDetector() {
36 new Object() {
37 @Override
38 protected void finalize() throws Throwable {
39 try {
40 Message.obtain(sHandler, CLEANUP_REFS).sendToTarget();
41 // Create a new detector since this one has now been GC'd.
42 createGarbageCollectionDetector();
43 } finally {
44 super.finalize();
45 }
46 }
47 };
48 } 64 }
49 65
50 // Message's sent in the |what| field to |sHandler|. 66 // Message's sent in the |what| field to |sHandler|.
51 67
52 // Add a new reference to sRefs. |msg.obj| is the CleanupReference to add. 68 // Add a new reference to sRefs. |msg.obj| is the CleanupReference to add.
53 static final int ADD_REF = 1; 69 private static final int ADD_REF = 1;
54 // Remove reference from sRefs. |msg.obj| is the CleanupReference to remove. 70 // Remove reference from sRefs. |msg.obj| is the CleanupReference to remove.
55 static final int REMOVE_REF = 2; 71 private static final int REMOVE_REF = 2;
56 // Flush out pending items on the reference queue (i.e. run the finalizer ac tions).
57 static final int CLEANUP_REFS = 3;
58 72
59 /** 73 /**
60 * This {@link Handler} polls {@link #sRefs}, looking for cleanup tasks that 74 * This {@link Handler} polls {@link #sRefs}, looking for cleanup tasks that
61 * are ready to run. 75 * are ready to run.
62 */ 76 */
63 private static Handler sHandler = new Handler(Looper.getMainLooper()) { 77 private static Handler sHandler = new Handler(Looper.getMainLooper()) {
64 @Override 78 @Override
65 public void handleMessage(android.os.Message msg) { 79 public void handleMessage(Message msg) {
66 TraceEvent.begin(); 80 TraceEvent.begin();
67 CleanupReference ref = (CleanupReference) msg.obj; 81 CleanupReference ref = (CleanupReference) msg.obj;
68 switch (msg.what) { 82 switch (msg.what) {
69 case ADD_REF: 83 case ADD_REF:
70 sRefs.add(ref); 84 sRefs.add(ref);
71 break; 85 break;
72 case REMOVE_REF: 86 case REMOVE_REF:
73 ref.runCleanupTaskInternal(); 87 ref.runCleanupTaskInternal();
74 break; 88 break;
75 case CLEANUP_REFS: 89 default:
76 break; // Handled below 90 Log.e(TAG, "Bad message=" + msg.what);
91 break;
77 } 92 }
78 93
79 // Always run the cleanup loop here even when adding or removing ref s, to avoid 94 if (DEBUG) Log.d(TAG, "will try and cleanup; max = " + sRefs.size()) ;
80 // falling behind on rapid allocation/GC inner loops. 95
81 while ((ref = (CleanupReference) sQueue.poll()) != null) { 96 synchronized (sCleanupMonitor) {
82 ref.runCleanupTaskInternal(); 97 // Always run the cleanup loop here even when adding or removing refs, to avoid
98 // falling behind on rapid garbage allocation inner loops.
99 while ((ref = (CleanupReference) sGcQueue.poll()) != null) {
100 ref.runCleanupTaskInternal();
101 }
102 sCleanupMonitor.notifyAll();
83 } 103 }
84 TraceEvent.end(); 104 TraceEvent.end();
85 } 105 }
86 }; 106 };
87 107
88 private static ReferenceQueue<Object> sQueue = new ReferenceQueue<Object>();
89
90 /** 108 /**
91 * Keep a strong reference to {@link CleanupReference} so that it will 109 * Keep a strong reference to {@link CleanupReference} so that it will
92 * actually get enqueued. 110 * actually get enqueued.
93 * Only accessed on the UI thread. 111 * Only accessed on the UI thread.
94 */ 112 */
95 private static Set<CleanupReference> sRefs = new HashSet<CleanupReference>() ; 113 private static Set<CleanupReference> sRefs = new HashSet<CleanupReference>() ;
96 114
97 private Runnable mCleanupTask; 115 private Runnable mCleanupTask;
98 116
99 /** 117 /**
100 * @param obj the object whose loss of reachability should trigger the 118 * @param obj the object whose loss of reachability should trigger the
101 * cleanup task. 119 * cleanup task.
102 * @param cleanupTask the task to run once obj loses reachability. 120 * @param cleanupTask the task to run once obj loses reachability.
103 */ 121 */
104 public CleanupReference(Object obj, Runnable cleanupTask) { 122 public CleanupReference(Object obj, Runnable cleanupTask) {
105 super(obj, sQueue); 123 super(obj, sGcQueue);
124 if (DEBUG) Log.d(TAG, "+++ CREATED ONE REF");
106 mCleanupTask = cleanupTask; 125 mCleanupTask = cleanupTask;
107 handleOnUiThread(ADD_REF); 126 handleOnUiThread(ADD_REF);
108 } 127 }
109 128
110 /** 129 /**
111 * Clear the cleanup task {@link Runnable} so that nothing will be done 130 * Clear the cleanup task {@link Runnable} so that nothing will be done
112 * after garbage collection. 131 * after garbage collection.
113 */ 132 */
114 public void cleanupNow() { 133 public void cleanupNow() {
115 handleOnUiThread(REMOVE_REF); 134 handleOnUiThread(REMOVE_REF);
116 } 135 }
117 136
118 private void handleOnUiThread(int what) { 137 private void handleOnUiThread(int what) {
119 Message msg = Message.obtain(sHandler, what, this); 138 Message msg = Message.obtain(sHandler, what, this);
120 if (Looper.myLooper() == sHandler.getLooper()) { 139 if (Looper.myLooper() == sHandler.getLooper()) {
121 sHandler.handleMessage(msg); 140 sHandler.handleMessage(msg);
141 msg.recycle();
122 } else { 142 } else {
123 msg.sendToTarget(); 143 msg.sendToTarget();
124 } 144 }
125 } 145 }
126 146
127 private void runCleanupTaskInternal() { 147 private void runCleanupTaskInternal() {
148 if (DEBUG) Log.d(TAG, "runCleanupTaskInternal");
128 sRefs.remove(this); 149 sRefs.remove(this);
129 if (mCleanupTask != null) { 150 if (mCleanupTask != null) {
151 if (DEBUG) Log.i(TAG, "--- CLEANING ONE REF");
130 mCleanupTask.run(); 152 mCleanupTask.run();
131 mCleanupTask = null; 153 mCleanupTask = null;
132 } 154 }
133 clear(); 155 clear();
134 } 156 }
135 } 157 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698