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 java.lang.ref.Cleaner; 29 import java.lang.ref.Cleaner.Cleanable; 30 import java.lang.ref.ReferenceQueue; 31 import java.security.AccessController; 32 import java.security.PrivilegedAction; 33 import java.util.concurrent.ThreadFactory; 34 import java.util.function.Function; 35 36 import jdk.internal.misc.InnocuousThread; 37 38 /** 39 * CleanerImpl manages a set of object references and corresponding cleaning actions. 40 * CleanerImpl provides the functionality of {@link java.lang.ref.Cleaner}. 41 */ 42 public final class CleanerImpl { 43 44 /** 45 * An object to access the CleanerImpl from a Cleaner; set by Cleaner init. 46 */ 47 private static Function<Cleaner, CleanerImpl> cleanerImplAccess = null; 48 49 /** 50 * Heads of a CleanableList for each reference type. 51 */ 52 final PhantomCleanable<?> phantomCleanableList; 53 54 final WeakCleanable<?> weakCleanableList; 55 56 final SoftCleanable<?> softCleanableList; 57 58 // The ReferenceQueue of pending cleaning actions 59 final ReferenceQueue<Object> queue; 60 61 /** 62 * Called by Cleaner static initialization to provide the function 63 * to map from Cleaner to CleanerImpl. 64 * @param access a function to map from Cleaner to CleanerImpl 65 */ 66 public static void setCleanerImplAccess(Function<Cleaner, CleanerImpl> access) { 67 if (cleanerImplAccess == null) { 68 cleanerImplAccess = access; 69 } else { 70 throw new InternalError("cleanerImplAccess"); 71 } 72 } 73 74 /** 75 * Called to get the CleanerImpl for a Cleaner. 76 * @param cleaner the cleaner 77 * @return the corresponding CleanerImpl 78 */ 79 static CleanerImpl getCleanerImpl(Cleaner cleaner) { 80 return cleanerImplAccess.apply(cleaner); 81 } 82 83 /** 84 * Constructor for CleanerImpl. 85 */ 86 public CleanerImpl() { 87 queue = new ReferenceQueue<>(); 88 phantomCleanableList = new PhantomCleanableRef(); 89 weakCleanableList = new WeakCleanableRef(); 90 softCleanableList = new SoftCleanableRef(); 91 } 92 93 /** 94 * Starts the Cleaner implementation. 95 * Ensure this is the CleanerImpl for the Cleaner. 96 * When started waits for Cleanables to be queued. 97 * @param cleaner the cleaner 98 * @param threadFactory the thread factory 99 */ 100 public void start(Cleaner cleaner, ThreadFactory threadFactory) { 101 if (getCleanerImpl(cleaner) != this) { 102 throw new AssertionError("wrong cleaner"); 103 } 104 // schedule a nop cleaning action for the cleaner, so the associated thread 105 // will continue to run at least until the cleaner is reclaimable. 106 new PhantomCleanableRef(cleaner, cleaner, () -> {}); 107 108 if (threadFactory == null) { 109 threadFactory = CleanerImpl.InnocuousThreadFactory.factory(); 110 } 111 112 // now that there's at least one cleaning action, for the cleaner, 113 // we can start the associated thread, which runs until 114 // all cleaning actions have been run. 115 Thread thread = threadFactory.newThread(this::run); 116 thread.setDaemon(true); 117 thread.start(); 118 } 119 120 /** 121 * Process queued Cleanables as long as the cleanable lists are not empty. 122 * A Cleanable is in one of the lists for each Object and for the Cleaner 123 * itself. 124 * Terminates when the Cleaner is no longer reachable and 125 * has been cleaned and there are no more Cleanable instances 126 * for which the object is reachable. 127 * <p> 128 * If the thread is a ManagedLocalsThread, the threadlocals 129 * are erased before each cleanup 130 */ 131 private void run() { 132 Thread t = Thread.currentThread(); 133 InnocuousThread mlThread = (t instanceof InnocuousThread) 134 ? (InnocuousThread) t 135 : null; 136 while (!phantomCleanableList.isListEmpty() || 137 !weakCleanableList.isListEmpty() || 138 !softCleanableList.isListEmpty()) { 139 if (mlThread != null) { 140 // Clear the thread locals 141 mlThread.eraseThreadLocals(); 142 } 143 try { 144 // Wait for a Ref, with a timeout to avoid getting hung 145 // due to a race with clear/clean 146 Cleanable ref = (Cleanable) queue.remove(60 * 1000L); 147 if (ref != null) { 148 ref.clean(); 149 } 150 } catch (InterruptedException i) { 151 continue; // ignore the interruption 152 } catch (Throwable e) { 153 // ignore exceptions from the cleanup action 154 } 155 } 156 } 157 158 /** 159 * Perform cleaning on an unreachable PhantomReference. 160 */ 161 public static final class PhantomCleanableRef extends PhantomCleanable<Object> { 162 private final Runnable action; 163 164 /** 165 * Constructor for a phantom cleanable reference. 166 * @param obj the object to monitor 167 * @param cleaner the cleaner 168 * @param action the action Runnable 169 */ 170 public PhantomCleanableRef(Object obj, Cleaner cleaner, Runnable action) { 171 super(obj, cleaner); 172 this.action = action; 173 } 174 175 /** 303 * @throws UnsupportedOperationException always 304 */ 305 @Override 306 public void clear() { 307 throw new UnsupportedOperationException("clear"); 308 } 309 310 } 311 312 /** 313 * A ThreadFactory for InnocuousThreads. 314 * The factory is a singleton. 315 */ 316 static final class InnocuousThreadFactory implements ThreadFactory { 317 final static ThreadFactory factory = new InnocuousThreadFactory(); 318 319 static ThreadFactory factory() { 320 return factory; 321 } 322 323 public Thread newThread(Runnable r) { 324 return AccessController.doPrivileged((PrivilegedAction<Thread>) () -> { 325 Thread t = new InnocuousThread(r); 326 t.setPriority(Thread.MAX_PRIORITY - 2); 327 t.setName("Cleaner-" + t.getId()); 328 return t; 329 }); 330 } 331 } 332 333 } | 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 java.lang.ref.Cleaner; 29 import java.lang.ref.Cleaner.Cleanable; 30 import java.lang.ref.ReferenceQueue; 31 import java.security.AccessController; 32 import java.security.PrivilegedAction; 33 import java.util.concurrent.ThreadFactory; 34 import java.util.concurrent.atomic.AtomicInteger; 35 import java.util.function.Function; 36 37 import jdk.internal.misc.InnocuousThread; 38 39 /** 40 * CleanerImpl manages a set of object references and corresponding cleaning actions. 41 * CleanerImpl provides the functionality of {@link java.lang.ref.Cleaner}. 42 */ 43 public final class CleanerImpl implements Runnable { 44 45 /** 46 * An object to access the CleanerImpl from a Cleaner; set by Cleaner init. 47 */ 48 private static Function<Cleaner, CleanerImpl> cleanerImplAccess = null; 49 50 /** 51 * Heads of a CleanableList for each reference type. 52 */ 53 final PhantomCleanable<?> phantomCleanableList; 54 55 final WeakCleanable<?> weakCleanableList; 56 57 final SoftCleanable<?> softCleanableList; 58 59 // The ReferenceQueue of pending cleaning actions 60 final ReferenceQueue<Object> queue; 61 62 /** 63 * Called by Cleaner static initialization to provide the function 64 * to map from Cleaner to CleanerImpl. 65 * @param access a function to map from Cleaner to CleanerImpl 66 */ 67 public static void setCleanerImplAccess(Function<Cleaner, CleanerImpl> access) { 68 if (cleanerImplAccess == null) { 69 cleanerImplAccess = access; 70 } else { 71 throw new InternalError("cleanerImplAccess"); 72 } 73 } 74 75 /** 76 * Called to get the CleanerImpl for a Cleaner. 77 * @param cleaner the cleaner 78 * @return the corresponding CleanerImpl 79 */ 80 public static CleanerImpl getCleanerImpl(Cleaner cleaner) { 81 return cleanerImplAccess.apply(cleaner); 82 } 83 84 /** 85 * Constructor for CleanerImpl. 86 */ 87 public CleanerImpl() { 88 queue = new ReferenceQueue<>(); 89 phantomCleanableList = new PhantomCleanableRef(); 90 weakCleanableList = new WeakCleanableRef(); 91 softCleanableList = new SoftCleanableRef(); 92 } 93 94 /** 95 * Starts the Cleaner implementation. 96 * Ensure this is the CleanerImpl for the Cleaner. 97 * When started waits for Cleanables to be queued. 98 * @param cleaner the cleaner 99 * @param threadFactory the thread factory 100 */ 101 public void start(Cleaner cleaner, ThreadFactory threadFactory) { 102 if (getCleanerImpl(cleaner) != this) { 103 throw new AssertionError("wrong cleaner"); 104 } 105 // schedule a nop cleaning action for the cleaner, so the associated thread 106 // will continue to run at least until the cleaner is reclaimable. 107 new CleanerCleanable(cleaner); 108 109 if (threadFactory == null) { 110 threadFactory = CleanerImpl.InnocuousThreadFactory.factory(); 111 } 112 113 // now that there's at least one cleaning action, for the cleaner, 114 // we can start the associated thread, which runs until 115 // all cleaning actions have been run. 116 Thread thread = threadFactory.newThread(this); 117 thread.setDaemon(true); 118 thread.start(); 119 } 120 121 /** 122 * Process queued Cleanables as long as the cleanable lists are not empty. 123 * A Cleanable is in one of the lists for each Object and for the Cleaner 124 * itself. 125 * Terminates when the Cleaner is no longer reachable and 126 * has been cleaned and there are no more Cleanable instances 127 * for which the object is reachable. 128 * <p> 129 * If the thread is a ManagedLocalsThread, the threadlocals 130 * are erased before each cleanup 131 */ 132 @Override 133 public void run() { 134 Thread t = Thread.currentThread(); 135 InnocuousThread mlThread = (t instanceof InnocuousThread) 136 ? (InnocuousThread) t 137 : null; 138 while (!phantomCleanableList.isListEmpty() || 139 !weakCleanableList.isListEmpty() || 140 !softCleanableList.isListEmpty()) { 141 if (mlThread != null) { 142 // Clear the thread locals 143 mlThread.eraseThreadLocals(); 144 } 145 try { 146 // Wait for a Ref, with a timeout to avoid getting hung 147 // due to a race with clear/clean 148 Cleanable ref = (Cleanable) queue.remove(60 * 1000L); 149 if (ref != null) { 150 ref.clean(); 151 } 152 } catch (Throwable e) { 153 // ignore exceptions from the cleanup action 154 // (including interruption of cleanup thread) 155 } 156 } 157 } 158 159 /** 160 * Processes all Cleanable(s) that have been waiting in the queue. 161 * 162 * @return {@code true} if any Cleanable was found in the queue and 163 * was processed or {@code false} if the queue was empty. 164 */ 165 public boolean drainQueue() { 166 boolean cleaned = false; 167 Cleanable ref; 168 while ((ref = (Cleanable) queue.poll()) != null) { 169 try { 170 ref.clean(); 171 } catch (Throwable t) { 172 // ignore exceptions from the cleanup action 173 } 174 cleaned = true; 175 } 176 return cleaned; 177 } 178 179 /** 180 * Perform cleaning on an unreachable PhantomReference. 181 */ 182 public static final class PhantomCleanableRef extends PhantomCleanable<Object> { 183 private final Runnable action; 184 185 /** 186 * Constructor for a phantom cleanable reference. 187 * @param obj the object to monitor 188 * @param cleaner the cleaner 189 * @param action the action Runnable 190 */ 191 public PhantomCleanableRef(Object obj, Cleaner cleaner, Runnable action) { 192 super(obj, cleaner); 193 this.action = action; 194 } 195 196 /** 324 * @throws UnsupportedOperationException always 325 */ 326 @Override 327 public void clear() { 328 throw new UnsupportedOperationException("clear"); 329 } 330 331 } 332 333 /** 334 * A ThreadFactory for InnocuousThreads. 335 * The factory is a singleton. 336 */ 337 static final class InnocuousThreadFactory implements ThreadFactory { 338 final static ThreadFactory factory = new InnocuousThreadFactory(); 339 340 static ThreadFactory factory() { 341 return factory; 342 } 343 344 final AtomicInteger cleanerThreadNumber = new AtomicInteger(); 345 346 public Thread newThread(Runnable r) { 347 return AccessController.doPrivileged(new PrivilegedAction<Thread>() { 348 @Override 349 public Thread run() { 350 Thread t = new InnocuousThread(r); 351 t.setPriority(Thread.MAX_PRIORITY - 2); 352 t.setName("Cleaner-" + cleanerThreadNumber.getAndIncrement()); 353 return t; 354 } 355 }); 356 } 357 } 358 359 /** 360 * A PhantomCleanable implementation for tracking the Cleaner itself. 361 */ 362 static final class CleanerCleanable extends PhantomCleanable<Cleaner> { 363 CleanerCleanable(Cleaner cleaner) { 364 super(cleaner, cleaner); 365 } 366 367 @Override 368 protected void performCleanup() { 369 // no action 370 } 371 } 372 } |