/* * Copyright (c) 2016 Mockito contributors * This program is made available under the terms of the MIT License. */ package org.mockito.internal.util.concurrent; import java.util.Iterator; import java.util.Map; /** * <p> * A thread-safe set with weak values. Entries are based on a key's system hash code and keys are considered equal only by reference equality. * </p> * This class does not implement the {@link java.util.Set} interface because this implementation is incompatible * with the set contract. While iterating over a set's entries, any value that has not passed iteration is referenced non-weakly. */ public class WeakConcurrentSet<V> implements Runnable, Iterable<V> { final WeakConcurrentMap<V, Boolean> target; public WeakConcurrentSet(Cleaner cleaner) { switch (cleaner) { case INLINE: target = new WeakConcurrentMap.WithInlinedExpunction<V, Boolean>(); break; case THREAD: case MANUAL: target = new WeakConcurrentMap<V, Boolean>(cleaner == Cleaner.THREAD); break; default: throw new AssertionError(); } } /** * @param value The value to add to the set. * @return {@code true} if the value was added to the set and was not contained before. */ public boolean add(V value) { return target.put(value, Boolean.TRUE) == null; // is null or Boolean.TRUE } /** * @param value The value to check if it is contained in the set. * @return {@code true} if the set contains the value. */ public boolean contains(V value) { return target.containsKey(value); } /** * @param value The value to remove from the set. * @return {@code true} if the value is contained in the set. */ public boolean remove(V value) { return target.remove(value); } /** * Clears the set. */ public void clear() { target.clear(); } /** * Returns the approximate size of this set where the returned number is at least as big as the actual number of entries. * * @return The minimum size of this set. */ public int approximateSize() { return target.approximateSize(); } @Override public void run() { target.run(); } /** * Determines the cleaning format. A reference is removed either by an explicitly started cleaner thread * associated with this instance ({@link Cleaner#THREAD}), as a result of interacting with this thread local * from any thread ({@link Cleaner#INLINE} or manually by submitting the detached thread local to a thread * ({@link Cleaner#MANUAL}). */ public enum Cleaner { THREAD, INLINE, MANUAL } /** * Cleans all unused references. */ public void expungeStaleEntries() { target.expungeStaleEntries(); } /** * @return The cleaner thread or {@code null} if no such thread was set. */ public Thread getCleanerThread() { return target.getCleanerThread(); } @Override public Iterator<V> iterator() { return new ReducingIterator<V>(target.iterator()); } private static class ReducingIterator<V> implements Iterator<V> { private final Iterator<Map.Entry<V, Boolean>> iterator; private ReducingIterator(Iterator<Map.Entry<V, Boolean>> iterator) { this.iterator = iterator; } @Override public void remove() { iterator.remove(); } @Override public V next() { return iterator.next().getKey(); } @Override public boolean hasNext() { return iterator.hasNext(); } } }