1 /* 2 * Copyright (c) 2015, 2016, 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 jdk.internal.ref; 27 28 import jdk.internal.misc.InnocuousThread; 29 import jdk.internal.misc.JavaLangRefAccess; 30 import jdk.internal.misc.SharedSecrets; 31 import jdk.internal.vm.annotation.ReservedStackAccess; 32 33 import java.lang.ref.Cleaner; 34 import java.lang.ref.ReferenceQueue; 35 import java.security.AccessController; 36 import java.security.PrivilegedAction; 37 import java.util.Objects; 38 import java.util.concurrent.ThreadFactory; 39 import java.util.concurrent.atomic.AtomicInteger; 40 import java.util.concurrent.locks.StampedLock; 41 import java.util.function.BooleanSupplier; 42 43 /** 44 * CleanerImpl is the implementation of {@link Cleaner}. 45 */ 46 public class CleanerImpl implements Cleaner { 47 48 final Task task; 49 50 public CleanerImpl(ThreadFactory threadFactory) { 51 task = new Task(); 52 task.start(this, threadFactory); 53 } 54 55 @Override 56 public Cleanable register(Object obj, Runnable action) { 57 Objects.requireNonNull(obj, "obj"); 58 Objects.requireNonNull(action, "action"); 59 return new CleanerImpl.PhantomCleanableRef(obj, this, action); 60 } 61 62 /** 63 * CleanerImpl.ExtendedImpl is the implementation of {@link ExtendedCleaner}. 64 */ 65 static class ExtendedImpl extends CleanerImpl implements ExtendedCleaner { 66 67 ExtendedImpl(ThreadFactory threadFactory) { 68 super(threadFactory); 69 } 70 71 // A fair lock for threads that retry actions to queue after 72 // 1st optimistic try fails so that only a single thread at a time is 73 // retrying actions while helping the Cleaner execute Cleanable(s) 74 // and trigger new Reference discovery before finally giving up. 75 private final StampedLock helpingLock = new StampedLock(); 76 77 public boolean retryWhileHelpingClean(BooleanSupplier retriableAction) { 78 // 1st optimistic try - allow concurrent execution of actions 79 // until helping is necessary 80 long stamp = helpingLock.tryReadLock(); 81 if (stamp != 0) try { 82 if (retriableAction.getAsBoolean()) { 83 return true; 84 } 85 } finally { 86 helpingLock.unlockRead(stamp); 87 } 88 89 // retrials with helping are exclusive 90 stamp = helpingLock.writeLock(); 91 try { 92 // retry actions while executing enqueued Cleanable(s) until the 93 // queue drains out 94 do { 95 if (retriableAction.getAsBoolean()) { 96 return true; 97 } 98 } while (task.cleanNextEnqueued()); 99 100 // the queue is now empty but there may have been a long time 101 // since last Reference discovery has been performed. 102 JavaLangRefAccess jlra = SharedSecrets.getJavaLangRefAccess(); 103 // trigger Reference(s) discovery 104 int discoveryPhase = jlra.discoverReferences(); 105 // wait for newly discovered Reference(s) to be enqueued 106 boolean interrupted = false; 107 try { 108 while (true) { 109 try { 110 jlra.awaitReferencesEnqueued(discoveryPhase); 111 break; 112 } catch (InterruptedException e) { 113 // ignore interrupts but don't swallow them 114 interrupted = true; 115 } 116 } 117 } finally { 118 if (interrupted) { 119 Thread.currentThread().interrupt(); 120 } 121 } 122 123 // the queue is now hopefully filled with some freshly discovered 124 // Cleanable(s) so retry operation while executing enqueued Cleanable(s) 125 // until the queue drains out 126 do { 127 if (retriableAction.getAsBoolean()) { 128 return true; 129 } 130 } while (task.cleanNextEnqueued()); 131 132 // give up finally 133 return false; 134 } finally { 135 helpingLock.unlockWrite(stamp); 136 } 137 } 138 } 139 140 // package-private access to Task's state 141 PhantomCleanable<?> phantomCleanableList() { return task.phantomCleanableList; } 142 WeakCleanable<?> weakCleanableList() { return task.weakCleanableList; } 143 SoftCleanable<?> softCleanableList() { return task.softCleanableList; } 144 ReferenceQueue<Object> queue() { return task.queue; } 145 146 /** 147 * CleanerImpl.Task manages a set of object references and corresponding 148 * cleaning actions and executes them after they are enqueued. 149 */ 150 private static final class Task implements Runnable { 151 /** 152 * Heads of a CleanableList for each reference type. 153 */ 154 final PhantomCleanable<?> phantomCleanableList; 155 156 final WeakCleanable<?> weakCleanableList; 157 158 final SoftCleanable<?> softCleanableList; 159 160 // The ReferenceQueue of pending cleaning actions 161 final ReferenceQueue<Object> queue; 162 163 /** 164 * Constructor for Task. 165 */ 166 Task() { 167 queue = new ReferenceQueue<>(); 168 phantomCleanableList = new PhantomCleanableRef(); 169 weakCleanableList = new WeakCleanableRef(); 170 softCleanableList = new SoftCleanableRef(); 171 } 172 173 /** 174 * Starts the Cleaner implementation. 175 * Ensure this is the CleanerImpl for the Cleaner. 176 * When started waits for Cleanables to be queued. 177 * @param cleaner the cleaner 178 * @param threadFactory the thread factory 179 */ 180 void start(CleanerImpl cleaner, ThreadFactory threadFactory) { 181 if (cleaner.task != this) { 182 throw new AssertionError("wrong cleaner"); 183 } 184 // schedule a nop cleaning action for the cleaner, so the associated thread 185 // will continue to run at least until the cleaner is reclaimable. 186 new CleanerCleanable(cleaner); 187 188 if (threadFactory == null) { 189 threadFactory = CleanerImpl.InnocuousThreadFactory.factory(); 190 } 191 192 // now that there's at least one cleaning action, for the cleaner, 193 // we can start the associated thread, which runs until 194 // all cleaning actions have been run. 195 Thread thread = threadFactory.newThread(this); 196 thread.setDaemon(true); 197 thread.start(); 198 } 199 200 /** 201 * Process queued Cleanables as long as the cleanable lists are not empty. 202 * A Cleanable is in one of the lists for each Object and for the Cleaner 203 * itself. 204 * Terminates when the Cleaner is no longer reachable and 205 * has been cleaned and there are no more Cleanable instances 206 * for which the object is reachable. 207 * <p> 208 * If the thread is a ManagedLocalsThread, the threadlocals 209 * are erased before each cleanup 210 */ 211 @Override 212 public void run() { 213 Thread t = Thread.currentThread(); 214 InnocuousThread mlThread = (t instanceof InnocuousThread) 215 ? (InnocuousThread) t 216 : null; 217 while (!phantomCleanableList.isListEmpty() || 218 !weakCleanableList.isListEmpty() || 219 !softCleanableList.isListEmpty()) { 220 if (mlThread != null) { 221 // Clear the thread locals 222 mlThread.eraseThreadLocals(); 223 } 224 try { 225 // Wait for a Ref, with a timeout to avoid getting hung 226 // due to a race with clear/clean 227 Cleanable ref = (Cleanable) queue.remove(60 * 1000L); 228 if (ref != null) { 229 ref.clean(); 230 } 231 } catch (Throwable e) { 232 // ignore exceptions from the cleanup action 233 // (including interruption of cleanup thread) 234 } 235 } 236 } 237 238 /** 239 * Processes next Cleanable that has been waiting in the queue. 240 * 241 * @return {@code true} if a Cleanable was found in the queue and 242 * was processed or {@code false} if the queue was empty. 243 */ 244 @ReservedStackAccess 245 boolean cleanNextEnqueued() { 246 Cleanable ref = (Cleanable) queue.poll(); 247 if (ref != null) { 248 try { 249 ref.clean(); 250 } catch (Throwable t) { 251 // ignore exceptions from the cleanup action 252 } 253 return true; 254 } else { 255 return false; 256 } 257 } 258 } 259 260 /** 261 * Perform cleaning on an unreachable PhantomReference. 262 */ 263 public static final class PhantomCleanableRef extends PhantomCleanable<Object> { 264 private final Runnable action; 265 266 /** 267 * Constructor for a phantom cleanable reference. 268 * @param obj the object to monitor 269 * @param cleaner the cleaner 270 * @param action the action Runnable 271 */ 272 public PhantomCleanableRef(Object obj, Cleaner cleaner, Runnable action) { 273 super(obj, cleaner); 274 this.action = action; 275 } 276 277 /** 278 * Constructor used only for root of phantom cleanable list. 279 */ 280 PhantomCleanableRef() { 281 super(); 282 this.action = null; 283 } 284 285 @Override 286 protected void performCleanup() { 287 action.run(); 288 } 289 290 /** 291 * Prevent access to referent even when it is still alive. 292 * 293 * @throws UnsupportedOperationException always 294 */ 295 @Override 296 public Object get() { 297 throw new UnsupportedOperationException("get"); 298 } 299 300 /** 301 * Direct clearing of the referent is not supported. 302 * 303 * @throws UnsupportedOperationException always 304 */ 305 @Override 306 public void clear() { 307 throw new UnsupportedOperationException("clear"); 308 } 309 } 310 311 /** 312 * Perform cleaning on an unreachable WeakReference. 313 */ 314 public static final class WeakCleanableRef extends WeakCleanable<Object> { 315 private final Runnable action; 316 317 /** 318 * Constructor for a weak cleanable reference. 319 * @param obj the object to monitor 320 * @param cleaner the cleaner 321 * @param action the action Runnable 322 */ 323 WeakCleanableRef(Object obj, Cleaner cleaner, Runnable action) { 324 super(obj, cleaner); 325 this.action = action; 326 } 327 328 /** 329 * Constructor used only for root of weak cleanable list. 330 */ 331 WeakCleanableRef() { 332 super(); 333 this.action = null; 334 } 335 336 @Override 337 protected void performCleanup() { 338 action.run(); 339 } 340 341 /** 342 * Prevent access to referent even when it is still alive. 343 * 344 * @throws UnsupportedOperationException always 345 */ 346 @Override 347 public Object get() { 348 throw new UnsupportedOperationException("get"); 349 } 350 351 /** 352 * Direct clearing of the referent is not supported. 353 * 354 * @throws UnsupportedOperationException always 355 */ 356 @Override 357 public void clear() { 358 throw new UnsupportedOperationException("clear"); 359 } 360 } 361 362 /** 363 * Perform cleaning on an unreachable SoftReference. 364 */ 365 public static final class SoftCleanableRef extends SoftCleanable<Object> { 366 private final Runnable action; 367 368 /** 369 * Constructor for a soft cleanable reference. 370 * @param obj the object to monitor 371 * @param cleaner the cleaner 372 * @param action the action Runnable 373 */ 374 SoftCleanableRef(Object obj, Cleaner cleaner, Runnable action) { 375 super(obj, cleaner); 376 this.action = action; 377 } 378 379 /** 380 * Constructor used only for root of soft cleanable list. 381 */ 382 SoftCleanableRef() { 383 super(); 384 this.action = null; 385 } 386 387 @Override 388 protected void performCleanup() { 389 action.run(); 390 } 391 392 /** 393 * Prevent access to referent even when it is still alive. 394 * 395 * @throws UnsupportedOperationException always 396 */ 397 @Override 398 public Object get() { 399 throw new UnsupportedOperationException("get"); 400 } 401 402 /** 403 * Direct clearing of the referent is not supported. 404 * 405 * @throws UnsupportedOperationException always 406 */ 407 @Override 408 public void clear() { 409 throw new UnsupportedOperationException("clear"); 410 } 411 412 } 413 414 /** 415 * A ThreadFactory for InnocuousThreads. 416 * The factory is a singleton. 417 */ 418 static final class InnocuousThreadFactory implements ThreadFactory { 419 final static ThreadFactory factory = new InnocuousThreadFactory(); 420 421 static ThreadFactory factory() { 422 return factory; 423 } 424 425 final AtomicInteger cleanerThreadNumber = new AtomicInteger(); 426 427 public Thread newThread(Runnable r) { 428 return AccessController.doPrivileged(new PrivilegedAction<>() { 429 @Override 430 public Thread run() { 431 Thread t = InnocuousThread.newThread(r); 432 t.setPriority(Thread.MAX_PRIORITY - 2); 433 t.setName("Cleaner-" + cleanerThreadNumber.getAndIncrement()); 434 return t; 435 } 436 }); 437 } 438 } 439 440 /** 441 * A PhantomCleanable implementation for tracking the Cleaner itself. 442 */ 443 static final class CleanerCleanable extends PhantomCleanable<Cleaner> { 444 CleanerCleanable(Cleaner cleaner) { 445 super(cleaner, cleaner); 446 } 447 448 @Override 449 protected void performCleanup() { 450 // no action 451 } 452 } 453 }