1 /*
  2  * Copyright (c) 2023, 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 create(isSoft, false, supplier);
110     }
111 
112     /**
113      * Create a new {@link ReferencedKeySet} elements.
114      *
115      * @param isSoft          true if {@link SoftReference} elements are to
116      *                        be used, {@link WeakReference} otherwise.
117      * @param useNativeQueue  true if uses NativeReferenceQueue
118      *                        otherwise use {@link ReferenceQueue}.
119      * @param supplier        {@link Supplier} of the backing map
120      *
121      * @return a new set with {@link Reference} elements
122      *
123      * @param <E> the type of elements maintained by this set
124      */
125     public static <E> ReferencedKeySet<E>
126     create(boolean isSoft, boolean useNativeQueue, Supplier<Map<ReferenceKey<E>, ReferenceKey<E>>> supplier) {
127          return new ReferencedKeySet<>(ReferencedKeyMap.create(isSoft, useNativeQueue, supplier));
128     }
129 
130     /**
131      * Removes enqueued weak references from set.
132      */
133     public void removeStaleReferences() {
134         map.removeStaleReferences();
135     }
136 
137     @Override
138     public Iterator<T> iterator() {
139         return map.keySet().iterator();
140     }
141 
142     @Override
143     public int size() {
144         return map.size();
145     }
146 
147     @Override
148     public boolean isEmpty() {
149         return map.isEmpty();
150     }
151 
152     @Override
153     @SuppressWarnings("unchecked")
154     public boolean contains(Object o) {
155         return map.containsKey((T)o);
156     }
157 
158     @Override
159     public boolean add(T e) {
160         return ReferencedKeyMap.internAddKey(map, e);
161     }
162 
163     @Override
164     public boolean remove(Object o) {
165         return map.remove(o) != null;
166     }
167 
168     @Override
169     public void clear() {
170         map.clear();
171     }
172 
173     /**
174      * Gets an existing element from the set, returning null if not present or
175      * the old element if previously added.
176      *
177      * @param e  element to get
178      *
179      * @return the old element if present, otherwise, null
180      */
181     public T get(T e) {
182         ReferenceKey<T> key = map.get(e);
183 
184         return key == null ? null : key.get();
185     }
186 
187     /**
188      * Intern an element to the set, returning the element if newly added or the
189      * old element if previously added.
190      *
191      * @param e  element to add
192      *
193      * @return the old element if present, otherwise, the new element
194      */
195     public T intern(T e) {
196         return ReferencedKeyMap.intern(map, e);
197     }
198 
199     /**
200      * Intern an element to the set, returning the element if newly added or the
201      * old element if previously added.
202      *
203      * @param e         element to add
204      * @param interner  operation to apply to key before adding to set
205      *
206      * @return the old element if present, otherwise, the new element
207      *
208      * @implNote This version of intern should not be called during phase1
209      * using a lambda. Use an UnaryOperator instance instead.
210      */
211     public T intern(T e, UnaryOperator<T> interner) {
212         return ReferencedKeyMap.intern(map, e, interner);
213     }
214 }