1 /* 2 * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package java.lang.ref; 27 28 import java.util.function.Consumer; 29 import jdk.internal.misc.VM; 30 import jdk.internal.vm.Continuation; 31 import jdk.internal.vm.ContinuationSupport; 32 33 /** 34 * Reference queues, to which registered reference objects are appended by the 35 * garbage collector after the appropriate reachability changes are detected. 36 * 37 * <p>{@linkplain java.lang.ref##MemoryConsistency Memory consistency effects}: 38 * The enqueueing of a reference to a queue (by the garbage collector, or by a 39 * successful call to {@link Reference#enqueue}) 40 * <a href="{@docRoot}/java.base/java/util/concurrent/package-summary.html#MemoryVisibility"><i>happens-before</i></a> 41 * the reference is removed from the queue by {@link ReferenceQueue#poll} or 42 * {@link ReferenceQueue#remove}. 43 * 44 * @param <T> the type of the reference object 45 * 46 * @author Mark Reinhold 47 * @since 1.2 48 */ 49 50 public class ReferenceQueue<T> { 51 private static class Null extends ReferenceQueue<Object> { 52 @Override 53 boolean enqueue(Reference<?> r) { 54 return false; 55 } 56 } 57 58 static final ReferenceQueue<Object> NULL = new Null(); 59 static final ReferenceQueue<Object> ENQUEUED = new Null(); 60 61 private volatile Reference<? extends T> head; 62 private long queueLength = 0; 63 64 private static class Lock { }; 65 private final Lock lock = new Lock(); 66 67 /** 68 * Constructs a new reference-object queue. 69 */ 70 public ReferenceQueue() { 71 } 72 73 private boolean enqueue0(Reference<? extends T> r) { // must hold lock 74 // Check that since getting the lock this reference hasn't already been 75 // enqueued (and even then removed) 76 ReferenceQueue<?> queue = r.queue; 77 if ((queue == NULL) || (queue == ENQUEUED)) { 78 return false; 79 } 80 assert queue == this; 81 // Self-loop end, so if a FinalReference it remains inactive. 82 r.next = (head == null) ? r : head; 83 head = r; 84 queueLength++; 85 // Update r.queue *after* adding to list, to avoid race 86 // with concurrent enqueued checks and fast-path poll(). 87 // Volatiles ensure ordering. 88 r.queue = ENQUEUED; 89 if (r instanceof FinalReference) { 90 VM.addFinalRefCount(1); 91 } 92 lock.notifyAll(); 93 return true; 94 } 95 96 private Reference<? extends T> poll0() { // must hold lock 97 Reference<? extends T> r = head; 98 if (r != null) { 99 r.queue = NULL; 100 // Update r.queue *before* removing from list, to avoid 101 // race with concurrent enqueued checks and fast-path 102 // poll(). Volatiles ensure ordering. 103 @SuppressWarnings("unchecked") 104 Reference<? extends T> rn = r.next; 105 // Handle self-looped next as end of list designator. 106 head = (rn == r) ? null : rn; 107 // Self-loop next rather than setting to null, so if a 108 // FinalReference it remains inactive. 109 r.next = r; 110 queueLength--; 111 if (r instanceof FinalReference) { 112 VM.addFinalRefCount(-1); 113 } 114 return r; 115 } 116 return null; 117 } 118 119 private Reference<? extends T> remove0(long timeout) throws InterruptedException { // must hold lock 120 Reference<? extends T> r = poll0(); 121 if (r != null) return r; 122 long start = System.nanoTime(); 123 for (;;) { 124 lock.wait(timeout); 125 r = poll0(); 126 if (r != null) return r; 127 long end = System.nanoTime(); 128 timeout -= (end - start) / 1000_000; 129 if (timeout <= 0) return null; 130 start = end; 131 } 132 } 133 134 private Reference<? extends T> remove0() throws InterruptedException { // must hold lock 135 for (;;) { 136 var r = poll0(); 137 if (r != null) return r; 138 lock.wait(); 139 } 140 } 141 142 boolean enqueue(Reference<? extends T> r) { /* Called only by Reference class */ 143 synchronized (lock) { 144 return enqueue0(r); 145 } 146 } 147 148 private boolean tryDisablePreempt() { 149 if (Thread.currentThread().isVirtual() && ContinuationSupport.isSupported()) { 150 Continuation.pin(); 151 return true; 152 } else { 153 return false; 154 } 155 } 156 157 private void enablePreempt() { 158 Continuation.unpin(); 159 } 160 161 /** 162 * Polls this queue to see if a reference object is available. If one is 163 * available without further delay then it is removed from the queue and 164 * returned. Otherwise this method immediately returns {@code null}. 165 * 166 * @return A reference object, if one was immediately available, 167 * otherwise {@code null} 168 * @see java.lang.ref.Reference#enqueue() 169 */ 170 public Reference<? extends T> poll() { 171 if (head == null) 172 return null; 173 174 // Prevent a virtual thread from being preempted as this could potentially 175 // deadlock with a carrier that is polling the same reference queue. 176 boolean disabled = tryDisablePreempt(); 177 try { 178 synchronized (lock) { 179 return poll0(); 180 } 181 } finally { 182 if (disabled) enablePreempt(); 183 } 184 } 185 186 /** 187 * Removes the next reference object in this queue, blocking until either 188 * one becomes available or the given timeout period expires. 189 * 190 * <p> This method does not offer real-time guarantees: It schedules the 191 * timeout as if by invoking the {@link Object#wait(long)} method. 192 * 193 * @param timeout If positive, block for up to {@code timeout} 194 * milliseconds while waiting for a reference to be 195 * added to this queue. If zero, block indefinitely. 196 * 197 * @return A reference object, if one was available within the specified 198 * timeout period, otherwise {@code null} 199 * 200 * @throws IllegalArgumentException 201 * If the value of the timeout argument is negative 202 * 203 * @throws InterruptedException 204 * If the timeout wait is interrupted 205 * 206 * @see java.lang.ref.Reference#enqueue() 207 */ 208 public Reference<? extends T> remove(long timeout) throws InterruptedException { 209 if (timeout < 0) 210 throw new IllegalArgumentException("Negative timeout value"); 211 if (timeout == 0) 212 return remove(); 213 214 synchronized (lock) { 215 return remove0(timeout); 216 } 217 } 218 219 /** 220 * Removes the next reference object in this queue, blocking until one 221 * becomes available. 222 * 223 * @return A reference object, blocking until one becomes available 224 * @throws InterruptedException If the wait is interrupted 225 * @see java.lang.ref.Reference#enqueue() 226 */ 227 public Reference<? extends T> remove() throws InterruptedException { 228 synchronized (lock) { 229 return remove0(); 230 } 231 } 232 233 /** 234 * Iterate queue and invoke given action with each Reference. 235 * Suitable for diagnostic purposes. 236 * WARNING: any use of this method should make sure to not 237 * retain the referents of iterated references (in case of 238 * FinalReference(s)) so that their life is not prolonged more 239 * than necessary. 240 */ 241 void forEach(Consumer<? super Reference<? extends T>> action) { 242 for (Reference<? extends T> r = head; r != null;) { 243 action.accept(r); 244 @SuppressWarnings("unchecked") 245 Reference<? extends T> rn = r.next; 246 if (rn == r) { 247 if (r.queue == ENQUEUED) { 248 // still enqueued -> we reached end of chain 249 r = null; 250 } else { 251 // already dequeued: r.queue == NULL; -> 252 // restart from head when overtaken by queue poller(s) 253 r = head; 254 } 255 } else { 256 // next in chain 257 r = rn; 258 } 259 } 260 } 261 }