1 /*
  2  * Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved.
  3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  4  *
  5  * This code is free software; you can redistribute it and/or modify it
  6  * under the terms of the GNU General Public License version 2 only, as
  7  * published by the Free Software Foundation.  Oracle designates this
  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.util;
 27 
 28 import java.lang.ref.Reference;
 29 import java.lang.ref.ReferenceQueue;
 30 import java.lang.ref.SoftReference;
 31 import java.lang.ref.WeakReference;
 32 import java.util.*;
 33 import java.util.concurrent.ConcurrentHashMap;
 34 import java.util.function.Supplier;
 35 import java.util.function.UnaryOperator;
 36 
 37 /**
 38  * This class provides management of {@link Set set} where it is desirable to
 39  * remove elements automatically when the element is garbage collected. This is
 40  * accomplished by using a backing map where the keys and values are either a
 41  * {@link WeakReference} or a {@link SoftReference}.
 42  * <p>
 43  * To create a {@link ReferencedKeySet} the user must provide a {@link Supplier}
 44  * of the backing map and whether {@link WeakReference} or
 45  * {@link SoftReference} is to be used.
 46  * {@snippet :
 47  * Set<Long> set;
 48  *
 49  * set = ReferencedKeySet.create(false, HashMap::new);
 50  * set.add(30_000_000L);
 51  * set.add(30_000_001L);
 52  * set.add(30_000_002L);
 53  * set.add(30_000_003L);
 54  * set.add(30_000_004L);
 55  *
 56  * set = ReferencedKeySet.create(true, ConcurrentHashMap::new);
 57  * set.add(40_000_000L);
 58  * set.add(40_000_001L);
 59  * set.add(40_000_002L);
 60  * set.add(40_000_003L);
 61  * set.add(40_000_004L);
 62  * }
 63  *
 64  * @implNote Care must be given that the backing map does replacement by
 65  * replacing the value in the map entry instead of deleting the old entry and
 66  * adding a new entry, otherwise replaced entries may end up with a strongly
 67  * referenced key. {@link HashMap} and {@link ConcurrentHashMap} are known
 68  * to be safe.
 69  *
 70  * @param <T> the type of elements maintained by this set
 71  */
 72 public final class ReferencedKeySet<T> extends AbstractSet<T> {
 73     /**
 74      * Backing {@link ReferencedKeySet} map.
 75      */
 76     final ReferencedKeyMap<T, ReferenceKey<T>> map;
 77 
 78     /**
 79      * @return a supplier to create a {@code ConcurrentHashMap} appropriate for use in the
 80      *         create methods.
 81      * @param <E> the type of elements maintained by this set
 82      */
 83     public static <E> Supplier<Map<ReferenceKey<E>, ReferenceKey<E>>> concurrentHashMapSupplier() {
 84         return ReferencedKeyMap.concurrentHashMapSupplier();
 85     }
 86 
 87     /**
 88      * Private constructor.
 89      *
 90      * @param map     backing map
 91      */
 92     private ReferencedKeySet(ReferencedKeyMap<T, ReferenceKey<T>> map) {
 93         this.map = map;
 94     }
 95 
 96     /**
 97      * Create a new {@link ReferencedKeySet} elements.
 98      *
 99      * @param isSoft          true if {@link SoftReference} elements are to
100      *                        be used, {@link WeakReference} otherwise.
101      * @param supplier        {@link Supplier} of the backing map
102      *
103      * @return a new set with {@link Reference} elements
104      *
105      * @param <E> the type of elements maintained by this set
106      */
107     public static <E> ReferencedKeySet<E>
108     create(boolean isSoft, Supplier<Map<ReferenceKey<E>, ReferenceKey<E>>> supplier) {
109         return new ReferencedKeySet<>(ReferencedKeyMap.create(isSoft, supplier));
110     }
111 
112     /**
113      * Removes enqueued weak references from set.
114      */
115     public void removeStaleReferences() {
116         map.removeStaleReferences();
117     }
118 
119     @Override
120     public Iterator<T> iterator() {
121         return map.keySet().iterator();
122     }
123 
124     @Override
125     public int size() {
126         return map.size();
127     }
128 
129     @Override
130     public boolean isEmpty() {
131         return map.isEmpty();
132     }
133 
134     @Override
135     @SuppressWarnings("unchecked")
136     public boolean contains(Object o) {
137         return map.containsKey((T)o);
138     }
139 
140     @Override
141     public boolean add(T e) {
142         return ReferencedKeyMap.internAddKey(map, e);
143     }
144 
145     @Override
146     public boolean remove(Object o) {
147         return map.remove(o) != null;
148     }
149 
150     @Override
151     public void clear() {
152         map.clear();
153     }
154 
155     /**
156      * Gets an existing element from the set, returning null if not present or
157      * the old element if previously added.
158      *
159      * @param e  element to get
160      *
161      * @return the old element if present, otherwise, null
162      */
163     public T get(T e) {
164         ReferenceKey<T> key = map.get(e);
165 
166         return key == null ? null : key.get();
167     }
168 
169     /**
170      * Intern an element to the set, returning the element if newly added or the
171      * old element if previously added.
172      *
173      * @param e  element to add
174      *
175      * @return the old element if present, otherwise, the new element
176      */
177     public T intern(T e) {
178         return ReferencedKeyMap.intern(map, e);
179     }
180 
181     /**
182      * Intern an element to the set, returning the element if newly added or the
183      * old element if previously added.
184      *
185      * @param e         element to add
186      * @param interner  operation to apply to key before adding to set
187      *
188      * @return the old element if present, otherwise, the new element
189      *
190      * @implNote This version of intern should not be called during phase1
191      * using a lambda. Use an UnaryOperator instance instead.
192      */
193     public T intern(T e, UnaryOperator<T> interner) {
194         return ReferencedKeyMap.intern(map, e, interner);
195     }
196 }