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 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 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. 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 * Perform cleaning on an unreachable PhantomReference. 161 */ 162 public static final class PhantomCleanableRef extends PhantomCleanable<Object> { 163 private final Runnable action; 164 165 /** 166 * Constructor for a phantom cleanable reference. 167 * @param obj the object to monitor 168 * @param cleaner the cleaner 169 * @param action the action Runnable 170 */ 171 public PhantomCleanableRef(Object obj, Cleaner cleaner, Runnable action) { 172 super(obj, cleaner); 173 this.action = action; 174 } | 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. 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 } |