OLD | NEW |
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 } |
OLD | NEW |