/** * Starts the Cleaner implementation. * Ensure this is the CleanerImpl for the Cleaner. * When started waits for Cleanables to be queued. * @param cleaner the cleaner * @param threadFactory the thread factory */ public void start(Cleaner cleaner, ThreadFactory threadFactory) { if (getCleanerImpl(cleaner) != this) { throw new AssertionError("wrong cleaner"); } // schedule a nop cleaning action for the cleaner, so the associated thread // will continue to run at least until the cleaner is reclaimable. new CleanerCleanable(cleaner); if (threadFactory == null) { threadFactory = CleanerImpl.InnocuousThreadFactory.factory(); } // now that there's at least one cleaning action, for the cleaner, // we can start the associated thread, which runs until // all cleaning actions have been run. Thread thread = threadFactory.newThread(this); thread.setDaemon(true); thread.start(); }
/** * Test that sequences of the various actions on a Reference * and on the Cleanable instance have the desired result. * The test cases are generated for each of phantom, weak and soft * references. * The sequence of actions includes all permutations to an initial * list of actions including clearing the ref and resulting garbage * collection actions on the reference and explicitly performing * the cleaning action. */ @Test @SuppressWarnings("unchecked") void testCleanableActions() { Cleaner cleaner = Cleaner.create(); // Individually generateCases(cleaner, c -> c.clearRef()); generateCases(cleaner, c -> c.doClean()); // Pairs generateCases(cleaner, c -> c.doClean(), c -> c.clearRef()); CleanableCase s = setupPhantom(COMMON, cleaner); cleaner = null; checkCleaned(s.getSemaphore(), true, "Cleaner was cleaned:"); }
/** * Test the jdk.internal.misc APIs with sequences of the various actions * on a Reference and on the Cleanable instance have the desired result. * The test cases are generated for each of phantom, weak and soft * references. * The sequence of actions includes all permutations to an initial * list of actions including clearing the ref and resulting garbage * collection actions on the reference, explicitly performing * the cleanup and explicitly clearing the cleaning action. */ @Test @SuppressWarnings("unchecked") void testRefSubtypes() { Cleaner cleaner = Cleaner.create(); // Individually generateCasesInternal(cleaner, c -> c.clearRef()); generateCasesInternal(cleaner, c -> c.doClean()); generateCasesInternal(cleaner, c -> c.doClear()); // Pairs generateCasesInternal(cleaner, c -> c.doClear(), c -> c.doClean()); // Triplets generateCasesInternal(cleaner, c -> c.doClear(), c -> c.doClean(), c -> c.clearRef()); generateExceptionCasesInternal(cleaner); CleanableCase s = setupPhantom(COMMON, cleaner); cleaner = null; checkCleaned(s.getSemaphore(), true, "Cleaner was cleaned:"); }
/** * Test that releasing the reference to the Cleaner service allows it to be * be freed. */ @Test void testCleanerTermination() { ReferenceQueue<Object> queue = new ReferenceQueue<>(); Cleaner service = Cleaner.create(); PhantomReference<Object> ref = new PhantomReference<>(service, queue); System.gc(); // Clear the Reference to the cleaning service and force a gc. service = null; System.gc(); try { Reference<?> r = queue.remove(1000L); Assert.assertNotNull(r, "queue.remove timeout,"); Assert.assertEquals(r, ref, "Wrong Reference dequeued"); } catch (InterruptedException ie) { System.out.printf("queue.remove Interrupted%n"); } }
/** * Create a CleanableCase for a PhantomReference. * @param cleaner the cleaner to use * @param obj an object or null to create a new Object * @return a new CleanableCase preset with the object, cleanup, and semaphore */ static CleanableCase setupPhantomSubclassException(Cleaner cleaner, Object obj) { if (obj == null) { obj = new Object(); } Semaphore s1 = new Semaphore(0); Cleaner.Cleanable c1 = new PhantomCleanable<Object>(obj, cleaner) { protected void performCleanup() { s1.release(); throw new RuntimeException("Exception thrown to cleaner thread"); } }; return new CleanableCase(new PhantomReference<>(obj, null), c1, s1, true); }
/** * Create a CleanableCase for a WeakReference. * @param cleaner the cleaner to use * @param obj an object or null to create a new Object * @return a new CleanableCase preset with the object, cleanup, and semaphore */ static CleanableCase setupWeakSubclassException(Cleaner cleaner, Object obj) { if (obj == null) { obj = new Object(); } Semaphore s1 = new Semaphore(0); Cleaner.Cleanable c1 = new WeakCleanable<Object>(obj, cleaner) { protected void performCleanup() { s1.release(); throw new RuntimeException("Exception thrown to cleaner thread"); } }; return new CleanableCase(new WeakReference<>(obj, null), c1, s1, true); }
/** * Create a CleanableCase for a SoftReference. * @param cleaner the cleaner to use * @param obj an object or null to create a new Object * @return a new CleanableCase preset with the object, cleanup, and semaphore */ static CleanableCase setupSoftSubclassException(Cleaner cleaner, Object obj) { if (obj == null) { obj = new Object(); } Semaphore s1 = new Semaphore(0); Cleaner.Cleanable c1 = new SoftCleanable<Object>(obj, cleaner) { protected void performCleanup() { s1.release(); throw new RuntimeException("Exception thrown to cleaner thread"); } }; return new CleanableCase(new SoftReference<>(obj, null), c1, s1, true); }
/** * Example using a Cleaner to remove WeakKey references from a Map. */ @Test void testWeakKey() { ConcurrentHashMap<WeakKey<String>, String> map = new ConcurrentHashMap<>(); Cleaner cleaner = Cleaner.create(); String key = new String("foo"); // ensure it is not interned String data = "bar"; map.put(new WeakKey<>(key, cleaner, map), data); WeakKey<String> k2 = new WeakKey<>(key, cleaner, map); Assert.assertEquals(map.get(k2), data, "value should be found in the map"); key = null; System.gc(); Assert.assertNotEquals(map.get(k2), data, "value should not be found in the map"); final long CYCLE_MAX = Utils.adjustTimeout(30L); for (int i = 1; map.size() > 0 && i < CYCLE_MAX; i++) { map.forEach( (k, v) -> System.out.printf(" k: %s, v: %s%n", k, v)); try { Thread.sleep(10L); } catch (InterruptedException ie) {} } Assert.assertEquals(map.size(), 0, "Expected map to be empty;"); cleaner = null; }
public WeakKey(K key, Cleaner c, ConcurrentHashMap<WeakKey<K>, ?> map) { super(key); this.hash = key.hashCode(); this.map = map; cleanable = new WeakCleanable<Object>(key, c) { protected void performCleanup() { map.remove(WeakKey.this); } }; }
/** * Called by Cleaner static initialization to provide the function * to map from Cleaner to CleanerImpl. * @param access a function to map from Cleaner to CleanerImpl */ public static void setCleanerImplAccess(Function<Cleaner, CleanerImpl> access) { if (cleanerImplAccess == null) { cleanerImplAccess = access; } else { throw new InternalError("cleanerImplAccess"); } }
/** * Constructs new {@code PhantomCleanable} with * {@code non-null referent} and {@code non-null cleaner}. * The {@code cleaner} is not retained; it is only used to * register the newly constructed {@link Cleaner.Cleanable Cleanable}. * * @param referent the referent to track * @param cleaner the {@code Cleaner} to register with */ public PhantomCleanable(T referent, Cleaner cleaner) { super(Objects.requireNonNull(referent), CleanerImpl.getCleanerImpl(cleaner).queue); this.list = CleanerImpl.getCleanerImpl(cleaner).phantomCleanableList; insert(); // Ensure referent and cleaner remain accessible Reference.reachabilityFence(referent); Reference.reachabilityFence(cleaner); }
/** * Constructs new {@code WeakCleanableReference} with * {@code non-null referent} and {@code non-null cleaner}. * The {@code cleaner} is not retained by this reference; it is only used * to register the newly constructed {@link Cleaner.Cleanable Cleanable}. * * @param referent the referent to track * @param cleaner the {@code Cleaner} to register new reference with */ public WeakCleanable(T referent, Cleaner cleaner) { super(Objects.requireNonNull(referent), CleanerImpl.getCleanerImpl(cleaner).queue); list = CleanerImpl.getCleanerImpl(cleaner).weakCleanableList; insert(); // Ensure referent and cleaner remain accessible Reference.reachabilityFence(referent); Reference.reachabilityFence(cleaner); }
/** * Constructs new {@code SoftCleanableReference} with * {@code non-null referent} and {@code non-null cleaner}. * The {@code cleaner} is not retained by this reference; it is only used * to register the newly constructed {@link Cleaner.Cleanable Cleanable}. * * @param referent the referent to track * @param cleaner the {@code Cleaner} to register with */ public SoftCleanable(T referent, Cleaner cleaner) { super(Objects.requireNonNull(referent), CleanerImpl.getCleanerImpl(cleaner).queue); list = CleanerImpl.getCleanerImpl(cleaner).softCleanableList; insert(); // Ensure referent and cleaner remain accessible Reference.reachabilityFence(referent); Reference.reachabilityFence(cleaner); }
@SuppressWarnings("unchecked") void generateCasesInternal(Cleaner cleaner, Consumer<CleanableCase>... runnables) { generateCases(() -> setupPhantomSubclass(cleaner, null), runnables.length, runnables); generateCases(() -> setupWeakSubclass(cleaner, null), runnables.length, runnables); generateCases(() -> setupSoftSubclass(cleaner, null), runnables.length, runnables); }
@SuppressWarnings("unchecked") void generateExceptionCasesInternal(Cleaner cleaner) { generateCases(() -> setupPhantomSubclassException(cleaner, null), 1, c -> c.clearRef()); generateCases(() -> setupWeakSubclassException(cleaner, null), 1, c -> c.clearRef()); generateCases(() -> setupSoftSubclassException(cleaner, null), 1, c -> c.clearRef()); }
/** * Create a CleanableCase for a PhantomReference. * @param cleaner the cleaner to use * @param obj an object or null to create a new Object * @return a new CleanableCase preset with the object, cleanup, and semaphore */ static CleanableCase setupPhantom(Cleaner cleaner, Object obj) { if (obj == null) { obj = new Object(); } Semaphore s1 = new Semaphore(0); Cleaner.Cleanable c1 = cleaner.register(obj, () -> s1.release()); return new CleanableCase(new PhantomReference<>(obj, null), c1, s1); }
/** * Create a CleanableCase for a PhantomReference. * @param cleaner the cleaner to use * @param obj an object or null to create a new Object * @return a new CleanableCase preset with the object, cleanup, and semaphore */ static CleanableCase setupPhantomSubclass(Cleaner cleaner, Object obj) { if (obj == null) { obj = new Object(); } Semaphore s1 = new Semaphore(0); Cleaner.Cleanable c1 = new PhantomCleanable<Object>(obj, cleaner) { protected void performCleanup() { s1.release(); } }; return new CleanableCase(new PhantomReference<>(obj, null), c1, s1); }
/** * Create a CleanableCase for a WeakReference. * @param cleaner the cleaner to use * @param obj an object or null to create a new Object * @return a new CleanableCase preset with the object, cleanup, and semaphore */ static CleanableCase setupWeakSubclass(Cleaner cleaner, Object obj) { if (obj == null) { obj = new Object(); } Semaphore s1 = new Semaphore(0); Cleaner.Cleanable c1 = new WeakCleanable<Object>(obj, cleaner) { protected void performCleanup() { s1.release(); } }; return new CleanableCase(new WeakReference<>(obj, null), c1, s1); }
/** * Create a CleanableCase for a SoftReference. * @param cleaner the cleaner to use * @param obj an object or null to create a new Object * @return a new CleanableCase preset with the object, cleanup, and semaphore */ static CleanableCase setupSoftSubclass(Cleaner cleaner, Object obj) { if (obj == null) { obj = new Object(); } Semaphore s1 = new Semaphore(0); Cleaner.Cleanable c1 = new SoftCleanable<Object>(obj, cleaner) { protected void performCleanup() { s1.release(); } }; return new CleanableCase(new SoftReference<>(obj, null), c1, s1); }
CleanableCase(Reference<Object> ref, Cleaner.Cleanable cleanup, Semaphore semaphore) { this.ref = ref; this.cleanup = cleanup; this.semaphore = semaphore; this.throwsEx = false; this.events = new int[4]; this.eventNdx = 0; }
CleanableCase(Reference<Object> ref, Cleaner.Cleanable cleanup, Semaphore semaphore, boolean throwsEx) { this.ref = ref; this.cleanup = cleanup; this.semaphore = semaphore; this.throwsEx = throwsEx; this.events = new int[4]; this.eventNdx = 0; }
/** * Test the Cleaner from the CleanerFactory. */ @Test void testCleanerFactory() { Cleaner cleaner = CleanerFactory.cleaner(); Object obj = new Object(); CleanableCase s = setupPhantom(cleaner, obj); obj = null; checkCleaned(s.getSemaphore(), true, "Object was cleaned using CleanerFactor.cleaner():"); }