1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * Copyright (c) 1997, 2005, Oracle and/or its affiliates. All rights reserved. 4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 5 * 6 * This code is free software; you can redistribute it and/or modify it 7 * under the terms of the GNU General Public License version 2 only, as 8 * published by the Free Software Foundation. Oracle designates this 9 * particular file as subject to the "Classpath" exception as provided 10 * by Oracle in the LICENSE file that accompanied this code. 11 * 12 * This code is distributed in the hope that it will be useful, but WITHOUT 13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 15 * version 2 for more details (a copy is included in the LICENSE file that 16 * accompanied this code). 17 * 18 * You should have received a copy of the GNU General Public License version 19 * 2 along with this work; if not, write to the Free Software Foundation, 20 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 21 * 22 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 23 * or visit www.oracle.com if you need additional information or have any 24 * questions. 25 */ 26 27 package java.lang.ref; 28 29 import sun.misc.Cleaner; 30 31 /** 32 * Reference queues, to which registered reference objects are appended by the 33 * garbage collector after the appropriate reachability changes are detected. 34 * 35 * @author Mark Reinhold 36 * @since 1.2 37 */ 38 public class ReferenceQueue<T> { 39 40 // Reference.queueNext will be set to sQueueNextUnenqueued to indicate 41 // when a reference has been enqueued and removed from its queue. 42 private static final Reference sQueueNextUnenqueued = new PhantomReference(null, null); 43 44 // NOTE: This implementation of ReferenceQueue is FIFO (queue-like) whereas 45 // the OpenJdk implementation is LIFO (stack-like). 46 private Reference<? extends T> head = null; 47 private Reference<? extends T> tail = null; 48 49 private final Object lock = new Object(); 50 51 /** 52 * Constructs a new reference-object queue. 53 */ 54 public ReferenceQueue() { } 55 56 /** 57 * Enqueue the given reference onto this queue. 58 * The caller is responsible for ensuring the lock is held on this queue, 59 * and for calling notifyAll on this queue after the reference has been 60 * enqueued. Returns true if the reference was enqueued successfully, 61 * false if the reference had already been enqueued. 62 * @GuardedBy("lock") 63 */ 64 private boolean enqueueLocked(Reference<? extends T> r) { 65 // Verify the reference has not already been enqueued. 66 if (r.queueNext != null) { 67 return false; 68 } 69 70 if (r instanceof Cleaner) { 71 // If this reference is a Cleaner, then simply invoke the clean method instead 72 // of enqueueing it in the queue. Cleaners are associated with dummy queues that 73 // are never polled and objects are never enqueued on them. 74 Cleaner cl = (sun.misc.Cleaner) r; 75 cl.clean(); 76 77 // Update queueNext to indicate that the reference has been 78 // enqueued, but is now removed from the queue. 79 r.queueNext = sQueueNextUnenqueued; 80 return true; 81 } 82 83 if (tail == null) { 84 head = r; 85 } else { 86 tail.queueNext = r; 87 } 88 tail = r; 89 tail.queueNext = r; 90 return true; 91 } 92 93 /** 94 * Test if the given reference object has been enqueued but not yet 95 * removed from the queue, assuming this is the reference object's queue. 96 */ 97 boolean isEnqueued(Reference<? extends T> reference) { 98 synchronized (lock) { 99 return reference.queueNext != null && reference.queueNext != sQueueNextUnenqueued; 100 } 101 } 102 103 /** 104 * Enqueue the reference object on the receiver. 105 * 106 * @param reference 107 * reference object to be enqueued. 108 * @return true if the reference was enqueued. 109 */ 110 boolean enqueue(Reference<? extends T> reference) { 111 synchronized (lock) { 112 if (enqueueLocked(reference)) { 113 lock.notifyAll(); 114 return true; 115 } 116 return false; 117 } 118 } 119 120 // @GuardedBy("lock") 121 private Reference<? extends T> reallyPollLocked() { 122 if (head != null) { 123 Reference<? extends T> r = head; 124 if (head == tail) { 125 tail = null; 126 head = null; 127 } else { 128 head = head.queueNext; 129 } 130 131 // Update queueNext to indicate that the reference has been 132 // enqueued, but is now removed from the queue. 133 r.queueNext = sQueueNextUnenqueued; 134 return r; 135 } 136 137 return null; 138 } 139 140 /** 141 * Polls this queue to see if a reference object is available. If one is 142 * available without further delay then it is removed from the queue and 143 * returned. Otherwise this method immediately returns <tt>null</tt>. 144 * 145 * @return A reference object, if one was immediately available, 146 * otherwise <code>null</code> 147 */ 148 public Reference<? extends T> poll() { 149 synchronized (lock) { 150 if (head == null) 151 return null; 152 153 return reallyPollLocked(); 154 } 155 } 156 157 /** 158 * Removes the next reference object in this queue, blocking until either 159 * one becomes available or the given timeout period expires. 160 * 161 * <p> This method does not offer real-time guarantees: It schedules the 162 * timeout as if by invoking the {@link Object#wait(long)} method. 163 * 164 * @param timeout If positive, block for up to <code>timeout</code> 165 * milliseconds while waiting for a reference to be 166 * added to this queue. If zero, block indefinitely. 167 * 168 * @return A reference object, if one was available within the specified 169 * timeout period, otherwise <code>null</code> 170 * 171 * @throws IllegalArgumentException 172 * If the value of the timeout argument is negative 173 * 174 * @throws InterruptedException 175 * If the timeout wait is interrupted 176 */ 177 public Reference<? extends T> remove(long timeout) 178 throws IllegalArgumentException, InterruptedException 179 { 180 if (timeout < 0) { 181 throw new IllegalArgumentException("Negative timeout value"); 182 } 183 synchronized (lock) { 184 Reference<? extends T> r = reallyPollLocked(); 185 if (r != null) return r; 186 long start = (timeout == 0) ? 0 : System.nanoTime(); 187 for (;;) { 188 lock.wait(timeout); 189 r = reallyPollLocked(); 190 if (r != null) return r; 191 if (timeout != 0) { 192 long end = System.nanoTime(); 193 timeout -= (end - start) / 1000_000; 194 if (timeout <= 0) return null; 195 start = end; 196 } 197 } 198 } 199 } 200 201 /** 202 * Removes the next reference object in this queue, blocking until one 203 * becomes available. 204 * 205 * @return A reference object, blocking until one becomes available 206 * @throws InterruptedException If the wait is interrupted 207 */ 208 public Reference<? extends T> remove() throws InterruptedException { 209 return remove(0); 210 } 211 212 /** 213 * Enqueue the given list of currently pending (unenqueued) references. 214 * 215 * @hide 216 */ 217 public static void enqueuePending(Reference<?> list) { 218 Reference<?> start = list; 219 do { 220 ReferenceQueue queue = list.queue; 221 if (queue == null) { 222 Reference<?> next = list.pendingNext; 223 224 // Make pendingNext a self-loop to preserve the invariant that 225 // once enqueued, pendingNext is non-null -- without leaking 226 // the object pendingNext was previously pointing to. 227 list.pendingNext = list; 228 list = next; 229 } else { 230 // To improve performance, we try to avoid repeated 231 // synchronization on the same queue by batching enqueue of 232 // consecutive references in the list that have the same 233 // queue. 234 synchronized (queue.lock) { 235 do { 236 Reference<?> next = list.pendingNext; 237 238 // Make pendingNext a self-loop to preserve the 239 // invariant that once enqueued, pendingNext is 240 // non-null -- without leaking the object pendingNext 241 // was previously pointing to. 242 list.pendingNext = list; 243 queue.enqueueLocked(list); 244 list = next; 245 } while (list != start && list.queue == queue); 246 queue.lock.notifyAll(); 247 } 248 } 249 } while (list != start); 250 } 251 252 /** 253 * List of references that the GC says need to be enqueued. 254 * Protected by ReferenceQueue.class lock. 255 * @hide 256 */ 257 public static Reference<?> unenqueued = null; 258 259 static void add(Reference<?> list) { 260 synchronized (ReferenceQueue.class) { 261 if (unenqueued == null) { 262 unenqueued = list; 263 } else { 264 // Find the last element in unenqueued. 265 Reference<?> last = unenqueued; 266 while (last.pendingNext != unenqueued) { 267 last = last.pendingNext; 268 } 269 // Add our list to the end. Update the pendingNext to point back to enqueued. 270 last.pendingNext = list; 271 last = list; 272 while (last.pendingNext != list) { 273 last = last.pendingNext; 274 } 275 last.pendingNext = unenqueued; 276 } 277 ReferenceQueue.class.notifyAll(); 278 } 279 } 280 } 281