1 /* 2 * Copyright (c) 2013, 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 package java.lang.reflect; 26 27 import java.lang.ref.ReferenceQueue; 28 import java.lang.ref.WeakReference; 29 import java.util.Objects; 30 import java.util.concurrent.ConcurrentHashMap; 31 import java.util.concurrent.ConcurrentMap; 32 import java.util.function.BiFunction; 33 import java.util.function.Supplier; 34 35 /** 36 * Cache mapping pairs of {@code (key, sub-key) -> value}. Keys and values are 37 * weakly but sub-keys are strongly referenced. Keys are passed directly to 38 * {@link #get} method which also takes a {@code parameter}. Sub-keys are 39 * calculated from keys and parameters using the {@code subKeyFactory} function 40 * passed to the constructor. Values are calculated from keys and parameters 41 * using the {@code valueFactory} function passed to the constructor. 42 * Keys can be {@code null} and are compared by identity while sub-keys returned by 43 * {@code subKeyFactory} or values returned by {@code valueFactory} 44 * can not be null. Sub-keys are compared using their {@link #equals} method. 45 * Entries are expunged from cache lazily on each invocation to {@link #get}, 46 * {@link #containsValue} or {@link #size} methods when the WeakReferences to 47 * keys are cleared. Cleared WeakReferences to individual values don't cause 48 * expunging, but such entries are logically treated as non-existent and 49 * trigger re-evaluation of {@code valueFactory} on request for their 50 * key/subKey. 51 * 52 * @author Peter Levart 53 * @param <K> type of keys 54 * @param <P> type of parameters 55 * @param <V> type of values 56 */ 57 final class WeakCache<K, P, V> { 58 59 private final ReferenceQueue<K> refQueue 60 = new ReferenceQueue<>(); 61 // the key type is Object for supporting null key 62 private final ConcurrentMap<Object, ConcurrentMap<Object, Supplier<V>>> map 63 = new ConcurrentHashMap<>(); 64 private final ConcurrentMap<Supplier<V>, Boolean> reverseMap 65 = new ConcurrentHashMap<>(); 66 private final BiFunction<K, P, ?> subKeyFactory; 67 private final BiFunction<K, P, V> valueFactory; 68 69 /** 70 * Construct an instance of {@code WeakCache} 71 * 72 * @param subKeyFactory a function mapping a pair of 73 * {@code (key, parameter) -> sub-key} 74 * @param valueFactory a function mapping a pair of 75 * {@code (key, parameter) -> value} 76 * @throws NullPointerException if {@code subKeyFactory} or 77 * {@code valueFactory} is null. 78 */ WeakCache(BiFunction<K, P, ?> subKeyFactory, BiFunction<K, P, V> valueFactory)79 public WeakCache(BiFunction<K, P, ?> subKeyFactory, 80 BiFunction<K, P, V> valueFactory) { 81 this.subKeyFactory = Objects.requireNonNull(subKeyFactory); 82 this.valueFactory = Objects.requireNonNull(valueFactory); 83 } 84 85 /** 86 * Look-up the value through the cache. This always evaluates the 87 * {@code subKeyFactory} function and optionally evaluates 88 * {@code valueFactory} function if there is no entry in the cache for given 89 * pair of (key, subKey) or the entry has already been cleared. 90 * 91 * @param key possibly null key 92 * @param parameter parameter used together with key to create sub-key and 93 * value (should not be null) 94 * @return the cached value (never null) 95 * @throws NullPointerException if {@code parameter} passed in or 96 * {@code sub-key} calculated by 97 * {@code subKeyFactory} or {@code value} 98 * calculated by {@code valueFactory} is null. 99 */ get(K key, P parameter)100 public V get(K key, P parameter) { 101 Objects.requireNonNull(parameter); 102 103 expungeStaleEntries(); 104 105 Object cacheKey = CacheKey.valueOf(key, refQueue); 106 107 // lazily install the 2nd level valuesMap for the particular cacheKey 108 ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey); 109 if (valuesMap == null) { 110 ConcurrentMap<Object, Supplier<V>> oldValuesMap 111 = map.putIfAbsent(cacheKey, 112 valuesMap = new ConcurrentHashMap<>()); 113 if (oldValuesMap != null) { 114 valuesMap = oldValuesMap; 115 } 116 } 117 118 // create subKey and retrieve the possible Supplier<V> stored by that 119 // subKey from valuesMap 120 Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter)); 121 Supplier<V> supplier = valuesMap.get(subKey); 122 Factory factory = null; 123 124 while (true) { 125 if (supplier != null) { 126 // supplier might be a Factory or a CacheValue<V> instance 127 V value = supplier.get(); 128 if (value != null) { 129 return value; 130 } 131 } 132 // else no supplier in cache 133 // or a supplier that returned null (could be a cleared CacheValue 134 // or a Factory that wasn't successful in installing the CacheValue) 135 136 // lazily construct a Factory 137 if (factory == null) { 138 factory = new Factory(key, parameter, subKey, valuesMap); 139 } 140 141 if (supplier == null) { 142 supplier = valuesMap.putIfAbsent(subKey, factory); 143 if (supplier == null) { 144 // successfully installed Factory 145 supplier = factory; 146 } 147 // else retry with winning supplier 148 } else { 149 if (valuesMap.replace(subKey, supplier, factory)) { 150 // successfully replaced 151 // cleared CacheEntry / unsuccessful Factory 152 // with our Factory 153 supplier = factory; 154 } else { 155 // retry with current supplier 156 supplier = valuesMap.get(subKey); 157 } 158 } 159 } 160 } 161 162 /** 163 * Checks whether the specified non-null value is already present in this 164 * {@code WeakCache}. The check is made using identity comparison regardless 165 * of whether value's class overrides {@link Object#equals} or not. 166 * 167 * @param value the non-null value to check 168 * @return true if given {@code value} is already cached 169 * @throws NullPointerException if value is null 170 */ containsValue(V value)171 public boolean containsValue(V value) { 172 Objects.requireNonNull(value); 173 174 expungeStaleEntries(); 175 return reverseMap.containsKey(new LookupValue<>(value)); 176 } 177 178 /** 179 * Returns the current number of cached entries that 180 * can decrease over time when keys/values are GC-ed. 181 */ size()182 public int size() { 183 expungeStaleEntries(); 184 return reverseMap.size(); 185 } 186 expungeStaleEntries()187 private void expungeStaleEntries() { 188 CacheKey<K> cacheKey; 189 while ((cacheKey = (CacheKey<K>)refQueue.poll()) != null) { 190 cacheKey.expungeFrom(map, reverseMap); 191 } 192 } 193 194 /** 195 * A factory {@link Supplier} that implements the lazy synchronized 196 * construction of the value and installment of it into the cache. 197 */ 198 private final class Factory implements Supplier<V> { 199 200 private final K key; 201 private final P parameter; 202 private final Object subKey; 203 private final ConcurrentMap<Object, Supplier<V>> valuesMap; 204 Factory(K key, P parameter, Object subKey, ConcurrentMap<Object, Supplier<V>> valuesMap)205 Factory(K key, P parameter, Object subKey, 206 ConcurrentMap<Object, Supplier<V>> valuesMap) { 207 this.key = key; 208 this.parameter = parameter; 209 this.subKey = subKey; 210 this.valuesMap = valuesMap; 211 } 212 213 @Override get()214 public synchronized V get() { // serialize access 215 // re-check 216 Supplier<V> supplier = valuesMap.get(subKey); 217 if (supplier != this) { 218 // something changed while we were waiting: 219 // might be that we were replaced by a CacheValue 220 // or were removed because of failure -> 221 // return null to signal WeakCache.get() to retry 222 // the loop 223 return null; 224 } 225 // else still us (supplier == this) 226 227 // create new value 228 V value = null; 229 try { 230 value = Objects.requireNonNull(valueFactory.apply(key, parameter)); 231 } finally { 232 if (value == null) { // remove us on failure 233 valuesMap.remove(subKey, this); 234 } 235 } 236 // the only path to reach here is with non-null value 237 assert value != null; 238 239 // wrap value with CacheValue (WeakReference) 240 CacheValue<V> cacheValue = new CacheValue<>(value); 241 242 // try replacing us with CacheValue (this should always succeed) 243 if (valuesMap.replace(subKey, this, cacheValue)) { 244 // put also in reverseMap 245 reverseMap.put(cacheValue, Boolean.TRUE); 246 } else { 247 throw new AssertionError("Should not reach here"); 248 } 249 250 // successfully replaced us with new CacheValue -> return the value 251 // wrapped by it 252 return value; 253 } 254 } 255 256 /** 257 * Common type of value suppliers that are holding a referent. 258 * The {@link #equals} and {@link #hashCode} of implementations is defined 259 * to compare the referent by identity. 260 */ 261 private interface Value<V> extends Supplier<V> {} 262 263 /** 264 * An optimized {@link Value} used to look-up the value in 265 * {@link WeakCache#containsValue} method so that we are not 266 * constructing the whole {@link CacheValue} just to look-up the referent. 267 */ 268 private static final class LookupValue<V> implements Value<V> { 269 private final V value; 270 LookupValue(V value)271 LookupValue(V value) { 272 this.value = value; 273 } 274 275 @Override get()276 public V get() { 277 return value; 278 } 279 280 @Override hashCode()281 public int hashCode() { 282 return System.identityHashCode(value); // compare by identity 283 } 284 285 @Override equals(Object obj)286 public boolean equals(Object obj) { 287 return obj == this || 288 obj instanceof Value && 289 this.value == ((Value<?>) obj).get(); // compare by identity 290 } 291 } 292 293 /** 294 * A {@link Value} that weakly references the referent. 295 */ 296 private static final class CacheValue<V> 297 extends WeakReference<V> implements Value<V> 298 { 299 private final int hash; 300 CacheValue(V value)301 CacheValue(V value) { 302 super(value); 303 this.hash = System.identityHashCode(value); // compare by identity 304 } 305 306 @Override hashCode()307 public int hashCode() { 308 return hash; 309 } 310 311 @Override equals(Object obj)312 public boolean equals(Object obj) { 313 V value; 314 return obj == this || 315 obj instanceof Value && 316 // cleared CacheValue is only equal to itself 317 (value = get()) != null && 318 value == ((Value<?>) obj).get(); // compare by identity 319 } 320 } 321 322 /** 323 * CacheKey containing a weakly referenced {@code key}. It registers 324 * itself with the {@code refQueue} so that it can be used to expunge 325 * the entry when the {@link WeakReference} is cleared. 326 */ 327 private static final class CacheKey<K> extends WeakReference<K> { 328 329 // a replacement for null keys 330 private static final Object NULL_KEY = new Object(); 331 valueOf(K key, ReferenceQueue<K> refQueue)332 static <K> Object valueOf(K key, ReferenceQueue<K> refQueue) { 333 return key == null 334 // null key means we can't weakly reference it, 335 // so we use a NULL_KEY singleton as cache key 336 ? NULL_KEY 337 // non-null key requires wrapping with a WeakReference 338 : new CacheKey<>(key, refQueue); 339 } 340 341 private final int hash; 342 CacheKey(K key, ReferenceQueue<K> refQueue)343 private CacheKey(K key, ReferenceQueue<K> refQueue) { 344 super(key, refQueue); 345 this.hash = System.identityHashCode(key); // compare by identity 346 } 347 348 @Override hashCode()349 public int hashCode() { 350 return hash; 351 } 352 353 @Override equals(Object obj)354 public boolean equals(Object obj) { 355 K key; 356 return obj == this || 357 obj != null && 358 obj.getClass() == this.getClass() && 359 // cleared CacheKey is only equal to itself 360 (key = this.get()) != null && 361 // compare key by identity 362 key == ((CacheKey<K>) obj).get(); 363 } 364 expungeFrom(ConcurrentMap<?, ? extends ConcurrentMap<?, ?>> map, ConcurrentMap<?, Boolean> reverseMap)365 void expungeFrom(ConcurrentMap<?, ? extends ConcurrentMap<?, ?>> map, 366 ConcurrentMap<?, Boolean> reverseMap) { 367 // removing just by key is always safe here because after a CacheKey 368 // is cleared and enqueue-ed it is only equal to itself 369 // (see equals method)... 370 ConcurrentMap<?, ?> valuesMap = map.remove(this); 371 // remove also from reverseMap if needed 372 if (valuesMap != null) { 373 for (Object cacheValue : valuesMap.values()) { 374 reverseMap.remove(cacheValue); 375 } 376 } 377 } 378 } 379 } 380