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