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 32 import java.lang.ref.Cleaner; 33 import java.lang.ref.ReferenceQueue; 34 import java.security.AccessController; 35 import java.security.PrivilegedAction; 36 import java.util.Objects; 37 import java.util.concurrent.ThreadFactory; 38 import java.util.concurrent.atomic.AtomicInteger; 39 import java.util.concurrent.locks.StampedLock; 40 import java.util.function.BooleanSupplier; 41 42 /** 43 * CleanerImpl is the implementation of {@link Cleaner}. 44 */ 45 public class CleanerImpl implements Cleaner { 46 47 final Task task; 48 49 public CleanerImpl(ThreadFactory threadFactory) { 50 task = new Task(); 51 task.start(this, threadFactory); 52 } 53 54 @Override 55 public Cleanable register(Object obj, Runnable action) { 56 Objects.requireNonNull(obj, "obj"); 57 Objects.requireNonNull(action, "action"); 58 return new CleanerImpl.PhantomCleanableRef(obj, this, action); 59 } 60 61 /** 62 * CleanerImpl.ExtendedImpl is the implementation of {@link ExtendedCleaner}. 63 */ 64 static class ExtendedImpl extends CleanerImpl implements ExtendedCleaner { 65 66 ExtendedImpl(ThreadFactory threadFactory) { 67 super(threadFactory); 68 } 69 70 // A fair lock for threads that retry operations to queue after 71 // 1st optimistic try fails so that only a single thread at a time is 72 // retrying operations while helping the Cleaner execute Cleanable(s) 73 // and trigger new Reference discovery before finally giving up. 74 private final StampedLock helpingLock = new StampedLock(); 75 76 public boolean retryWhileHelpingClean(BooleanSupplier retriableOperation) { 77 // 1st optimistic try - allow concurrent execution of operations 78 // until helping is necessary 79 long stamp = helpingLock.tryReadLock(); 80 if (stamp != 0) try { 81 if (retriableOperation.getAsBoolean()) { 82 return true; 83 } 84 } finally { 85 helpingLock.unlockRead(stamp); 86 } 87 88 // retrials with helping is exclusive 89 stamp = helpingLock.writeLock(); 90 try { 91 // retry operation while executing enqueued Cleanable(s) until the 92 // queue drains out 93 do { 94 if (retriableOperation.getAsBoolean()) { 95 return true; 96 } 97 } while (task.cleanNextEnqueued()); 98 99 JavaLangRefAccess jlra = SharedSecrets.getJavaLangRefAccess(); 100 // trigger Reference(s) discovery 101 int discoveryPhase = jlra.discoverReferences(); 102 // wait for newly discovered Reference(s) to be enqueued 103 boolean interrupted = false; 104 try { 105 while (true) { 106 try { 107 jlra.awaitReferencesEnqueued(discoveryPhase); 108 break; 109 } catch (InterruptedException e) { 110 // ignore interrupts but don't swallow them 111 interrupted = true; 112 } 113 } 114 } finally { 115 if (interrupted) { 116 Thread.currentThread().interrupt(); 117 } 118 } 119 120 // the queue is now hopefully filled with new pending Cleanable(s) 121 // so retry operation while executing enqueued Cleanable(s) until the 122 // queue drains out 123 do { 124 if (retriableOperation.getAsBoolean()) { 125 return true; 126 } 127 } while (task.cleanNextEnqueued()); 128 129 // give up finally 130 return false; 131 } finally { 132 helpingLock.unlockWrite(stamp); 133 } 134 } 135 } 136 137 // package-private access to Task's state 138 PhantomCleanable<?> phantomCleanableList() { return task.phantomCleanableList; } 139 WeakCleanable<?> weakCleanableList() { return task.weakCleanableList; } 140 SoftCleanable<?> softCleanableList() { return task.softCleanableList; } 141 ReferenceQueue<Object> queue() { return task.queue; } 142 143 /** 144 * CleanerImpl.Task manages a set of object references and corresponding 145 * cleaning actions and executes them after they are enqueued. 146 */ 147 private static final class Task implements Runnable { 148 /** 149 * Heads of a CleanableList for each reference type. 150 */ 151 final PhantomCleanable<?> phantomCleanableList; 152 153 final WeakCleanable<?> weakCleanableList; 154 155 final SoftCleanable<?> softCleanableList; 156 157 // The ReferenceQueue of pending cleaning actions 158 final ReferenceQueue<Object> queue; 159 160 /** 161 * Constructor for Task. 162 */ 163 Task() { 164 queue = new ReferenceQueue<>(); 165 phantomCleanableList = new PhantomCleanableRef(); 166 weakCleanableList = new WeakCleanableRef(); 167 softCleanableList = new SoftCleanableRef(); 168 } 169 170 /** 171 * Starts the Cleaner implementation. 172 * Ensure this is the CleanerImpl for the Cleaner. 173 * When started waits for Cleanables to be queued. 174 * @param cleaner the cleaner 175 * @param threadFactory the thread factory 176 */ 177 void start(CleanerImpl cleaner, ThreadFactory threadFactory) { 178 if (cleaner.task != this) { 179 throw new AssertionError("wrong cleaner"); 180 } 181 // schedule a nop cleaning action for the cleaner, so the associated thread 182 // will continue to run at least until the cleaner is reclaimable. 183 new CleanerCleanable(cleaner); 184 185 if (threadFactory == null) { 186 threadFactory = CleanerImpl.InnocuousThreadFactory.factory(); 187 } 188 189 // now that there's at least one cleaning action, for the cleaner, 190 // we can start the associated thread, which runs until 191 // all cleaning actions have been run. 192 Thread thread = threadFactory.newThread(this); 193 thread.setDaemon(true); 194 thread.start(); 195 } 196 197 /** 198 * Process queued Cleanables as long as the cleanable lists are not empty. 211 InnocuousThread mlThread = (t instanceof InnocuousThread) 212 ? (InnocuousThread) t 213 : null; 214 while (!phantomCleanableList.isListEmpty() || 215 !weakCleanableList.isListEmpty() || 216 !softCleanableList.isListEmpty()) { 217 if (mlThread != null) { 218 // Clear the thread locals 219 mlThread.eraseThreadLocals(); 220 } 221 try { 222 // Wait for a Ref, with a timeout to avoid getting hung 223 // due to a race with clear/clean 224 Cleanable ref = (Cleanable) queue.remove(60 * 1000L); 225 if (ref != null) { 226 ref.clean(); 227 } 228 } catch (Throwable e) { 229 // ignore exceptions from the cleanup action 230 // (including interruption of cleanup thread) 231 } 232 } 233 } 234 235 /** 236 * Processes next Cleanable that has been waiting in the queue. 237 * 238 * @return {@code true} if a Cleanable was found in the queue and 239 * was processed or {@code false} if the queue was empty. 240 */ 241 boolean cleanNextEnqueued() { 242 Cleanable ref = (Cleanable) queue.poll(); 243 if (ref != null) { 244 try { 245 ref.clean(); 246 } catch (Throwable t) { 247 // ignore exceptions from the cleanup action 248 } 249 return true; 250 } else { 251 return false; 252 } 253 } 254 } 255 256 /** 257 * Perform cleaning on an unreachable PhantomReference. 258 */ 259 public static final class PhantomCleanableRef extends PhantomCleanable<Object> { 260 private final Runnable action; 261 262 /** 263 * Constructor for a phantom cleanable reference. 264 * @param obj the object to monitor 265 * @param cleaner the cleaner 266 * @param action the action Runnable 267 */ 268 public PhantomCleanableRef(Object obj, Cleaner cleaner, Runnable action) { 269 super(obj, cleaner); 270 this.action = action; 271 } |