1 /* 2 * Copyright (c) 2016 Mockito contributors 3 * This program is made available under the terms of the MIT License. 4 */ 5 package org.mockito.internal.util.concurrent; 6 7 import java.util.Iterator; 8 import java.util.Map; 9 10 /** 11 * <p> 12 * 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. 13 * </p> 14 * This class does not implement the {@link java.util.Set} interface because this implementation is incompatible 15 * with the set contract. While iterating over a set's entries, any value that has not passed iteration is referenced non-weakly. 16 */ 17 public class WeakConcurrentSet<V> implements Runnable, Iterable<V> { 18 19 final WeakConcurrentMap<V, Boolean> target; 20 21 public WeakConcurrentSet(Cleaner cleaner) { 22 switch (cleaner) { 23 case INLINE: 24 target = new WeakConcurrentMap.WithInlinedExpunction<V, Boolean>(); 25 break; 26 case THREAD: 27 case MANUAL: 28 target = new WeakConcurrentMap<V, Boolean>(cleaner == Cleaner.THREAD); 29 break; 30 default: 31 throw new AssertionError(); 32 } 33 } 34 35 /** 36 * @param value The value to add to the set. 37 * @return {@code true} if the value was added to the set and was not contained before. 38 */ 39 public boolean add(V value) { 40 return target.put(value, Boolean.TRUE) == null; // is null or Boolean.TRUE 41 } 42 43 /** 44 * @param value The value to check if it is contained in the set. 45 * @return {@code true} if the set contains the value. 46 */ 47 public boolean contains(V value) { 48 return target.containsKey(value); 49 } 50 51 /** 52 * @param value The value to remove from the set. 53 * @return {@code true} if the value is contained in the set. 54 */ 55 public boolean remove(V value) { 56 return target.remove(value); 57 } 58 59 /** 60 * Clears the set. 61 */ 62 public void clear() { 63 target.clear(); 64 } 65 66 /** 67 * Returns the approximate size of this set where the returned number is at least as big as the actual number of entries. 68 * 69 * @return The minimum size of this set. 70 */ 71 public int approximateSize() { 72 return target.approximateSize(); 73 } 74 75 @Override 76 public void run() { 77 target.run(); 78 } 79 80 /** 81 * Determines the cleaning format. A reference is removed either by an explicitly started cleaner thread 82 * associated with this instance ({@link Cleaner#THREAD}), as a result of interacting with this thread local 83 * from any thread ({@link Cleaner#INLINE} or manually by submitting the detached thread local to a thread 84 * ({@link Cleaner#MANUAL}). 85 */ 86 public enum Cleaner { 87 THREAD, INLINE, MANUAL 88 } 89 90 /** 91 * Cleans all unused references. 92 */ 93 public void expungeStaleEntries() { 94 target.expungeStaleEntries(); 95 } 96 97 /** 98 * @return The cleaner thread or {@code null} if no such thread was set. 99 */ 100 public Thread getCleanerThread() { 101 return target.getCleanerThread(); 102 } 103 104 @Override 105 public Iterator<V> iterator() { 106 return new ReducingIterator<V>(target.iterator()); 107 } 108 109 private static class ReducingIterator<V> implements Iterator<V> { 110 111 private final Iterator<Map.Entry<V, Boolean>> iterator; 112 113 private ReducingIterator(Iterator<Map.Entry<V, Boolean>> iterator) { 114 this.iterator = iterator; 115 } 116 117 @Override 118 public void remove() { 119 iterator.remove(); 120 } 121 122 @Override 123 public V next() { 124 return iterator.next().getKey(); 125 } 126 127 @Override 128 public boolean hasNext() { 129 return iterator.hasNext(); 130 } 131 } 132 } 133