1 /*
   2  * Copyright (c) 2016, 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.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 package jdk.internal.loader;
  25 
  26 import jdk.internal.misc.JavaLangAccess;
  27 import jdk.internal.misc.SharedSecrets;
  28 
  29 import java.lang.reflect.UndeclaredThrowableException;
  30 import java.util.Iterator;
  31 import java.util.Objects;
  32 import java.util.concurrent.ConcurrentHashMap;
  33 import java.util.function.BiFunction;
  34 import java.util.function.Supplier;
  35 
  36 /**
  37  * AbstractClassLoaderValue is a superclass of root-{@link ClassLoaderValue}
  38  * and {@link Sub sub}-ClassLoaderValue.
  39  *
  40  * @param <CLV> the type of concrete ClassLoaderValue (this type)
  41  * @param <V>   the type of values associated with ClassLoaderValue
  42  */
  43 public abstract class AbstractClassLoaderValue<CLV extends AbstractClassLoaderValue<CLV, V>, V> {
  44 
  45     /**
  46      * Sole constructor.
  47      */
  48     AbstractClassLoaderValue() {}
  49 
  50     /**
  51      * Returns the key component of this ClassLoaderValue. The key component of
  52      * the root-{@link ClassLoaderValue} is the ClassLoaderValue itself,
  53      * while the key component of a {@link #sub(Object) sub}-ClassLoaderValue
  54      * is what was given to construct it.
  55      *
  56      * @return the key component of this ClassLoaderValue.
  57      */
  58     public abstract Object key();
  59 
  60     /**
  61      * Constructs new sub-ClassLoaderValue of this ClassLoaderValue with given
  62      * key component.
  63      *
  64      * @param key the key component of the sub-ClassLoaderValue.
  65      * @param <K> the type of the key component.
  66      * @return a sub-ClassLoaderValue of this ClassLoaderValue for given key
  67      */
  68     public <K> Sub<K> sub(K key) {
  69         return new Sub<>(key);
  70     }
  71 
  72     /**
  73      * Returns {@code true} if this ClassLoaderValue is equal to given {@code clv}
  74      * or if this ClassLoaderValue was derived from given {@code clv} by a chain
  75      * of {@link #sub(Object)} invocations.
  76      *
  77      * @param clv the ClassLoaderValue to test this against
  78      * @return if this ClassLoaderValue is equal to given {@code clv} or
  79      * its descendant
  80      */
  81     public abstract boolean isEqualOrDescendantOf(AbstractClassLoaderValue<?, V> clv);
  82 
  83     /**
  84      * Returns the value associated with this ClassLoaderValue and given ClassLoader
  85      * or {@code null} if there is none.
  86      *
  87      * @param cl the ClassLoader for the associated value
  88      * @return the value associated with this ClassLoaderValue and given ClassLoader
  89      * or {@code null} if there is none.
  90      */
  91     public V get(ClassLoader cl) {
  92         Object val = AbstractClassLoaderValue.<CLV>map(cl).get(this);
  93         try {
  94             return extractValue(val);
  95         } catch (Memoizer.RecursiveInvocationException e) {
  96             // propagate recursive get() for the same key that is just
  97             // being calculated in computeIfAbsent()
  98             throw e;
  99         } catch (Throwable t) {
 100             // don't propagate exceptions thrown from Memoizer - pretend
 101             // that there was no entry
 102             // (computeIfAbsent invocation will try to remove it anyway)
 103             return null;
 104         }
 105     }
 106 
 107     /**
 108      * Associates given value {@code v} with this ClassLoaderValue and given
 109      * ClassLoader and returns {@code null} if there was no previously associated
 110      * value or does nothing and returns previously associated value if there
 111      * was one.
 112      *
 113      * @param cl the ClassLoader for the associated value
 114      * @param v  the value to associate
 115      * @return previously associated value or null if there was none
 116      */
 117     public V putIfAbsent(ClassLoader cl, V v) {
 118         ConcurrentHashMap<CLV, Object> map = map(cl);
 119         @SuppressWarnings("unchecked")
 120         CLV clv = (CLV) this;
 121         while (true) {
 122             try {
 123                 Object val = map.putIfAbsent(clv, v);
 124                 return extractValue(val);
 125             } catch (Memoizer.RecursiveInvocationException e) {
 126                 // propagate RecursiveInvocationException for the same key that
 127                 // is just being calculated in computeIfAbsent
 128                 throw e;
 129             } catch (Throwable t) {
 130                 // don't propagate exceptions thrown from foreign Memoizer -
 131                 // pretend that there was no entry and retry
 132                 // (foreign computeIfAbsent invocation will try to remove it anyway)
 133             }
 134             // TODO:
 135             // Thread.onSpinLoop(); // when available
 136         }
 137     }
 138 
 139     /**
 140      * Removes the value associated with this ClassLoaderValue and given
 141      * ClassLoader if the associated value is equal to given value {@code v} and
 142      * returns {@code true} or does nothing and returns {@code false} if there is
 143      * no currently associated value or it is not equal to given value {@code v}.
 144      *
 145      * @param cl the ClassLoader for the associated value
 146      * @param v  the value to compare with currently associated value
 147      * @return {@code true} if the association was removed or {@code false} if not
 148      */
 149     public boolean remove(ClassLoader cl, Object v) {
 150         return AbstractClassLoaderValue.<CLV>map(cl).remove(this, v);
 151     }
 152 
 153     /**
 154      * Returns the value associated with this ClassLoaderValue and given
 155      * ClassLoader if there is one or computes the value by invoking given
 156      * {@code mappingFunction}, associates it and returns it.
 157      * <p>
 158      * Computation and association of the computed value is performed atomically
 159      * by the 1st thread that requests a particular association while holding a
 160      * lock associated with this ClassLoaderValue and given ClassLoader.
 161      * Nested calls from the {@code mappingFunction} to {@link #get},
 162      * {@link #putIfAbsent} or {@link #computeIfAbsent} for the same association
 163      * are not allowed and throw {@link IllegalStateException}. Nested call to
 164      * {@link #remove} for the same association is allowed but will always return
 165      * {@code false} regardless of passed-in comparison value. Nested calls for
 166      * other association(s) are allowed, but care should be taken to avoid
 167      * deadlocks. When two threads perform nested computations of the overlapping
 168      * set of associations they should always request them in the same order.
 169      *
 170      * @param cl              the ClassLoader for the associated value
 171      * @param mappingFunction the function to compute the value
 172      * @return the value associated with this ClassLoaderValue and given
 173      * ClassLoader.
 174      * @throws IllegalStateException if a direct or indirect invocation from
 175      *                               within given {@code mappingFunction} that
 176      *                               computes the value of a particular association
 177      *                               to {@link #get}, {@link #putIfAbsent} or
 178      *                               {@link #computeIfAbsent}
 179      *                               for the same association is attempted.
 180      */
 181     public V computeIfAbsent(ClassLoader cl,
 182                              BiFunction<
 183                                  ? super ClassLoader,
 184                                  ? super CLV,
 185                                  ? extends V
 186                                  > mappingFunction) throws IllegalStateException {
 187         ConcurrentHashMap<CLV, Object> map = map(cl);
 188         @SuppressWarnings("unchecked")
 189         CLV clv = (CLV) this;
 190         Memoizer<CLV, V> mv = null;
 191         while (true) {
 192             Object val = (mv == null) ? map.get(clv) : map.putIfAbsent(clv, mv);
 193             if (val == null) {
 194                 if (mv == null) {
 195                     // create Memoizer lazily when 1st needed and restart loop
 196                     mv = new Memoizer<>(cl, clv, mappingFunction);
 197                     continue;
 198                 }
 199                 // mv != null, therefore sv == null was a result of successful
 200                 // putIfAbsent
 201                 try {
 202                     // trigger Memoizer to compute the value
 203                     V v = mv.get();
 204                     // attempt to replace our Memoizer with the value
 205                     map.replace(clv, mv, v);
 206                     // return computed value
 207                     return v;
 208                 } catch (Throwable t) {
 209                     // our Memoizer has thrown, attempt to remove it
 210                     map.remove(clv, mv);
 211                     // propagate exception because it's from our Memoizer
 212                     throw t;
 213                 }
 214             } else {
 215                 try {
 216                     return extractValue(val);
 217                 } catch (Memoizer.RecursiveInvocationException e) {
 218                     // propagate recursive attempts to calculate the same
 219                     // value as being calculated at the moment
 220                     throw e;
 221                 } catch (Throwable t) {
 222                     // don't propagate exceptions thrown from foreign Memoizer -
 223                     // pretend that there was no entry and retry
 224                     // (foreign computeIfAbsent invocation will try to remove it anyway)
 225                 }
 226             }
 227             // TODO:
 228             // Thread.onSpinLoop(); // when available
 229         }
 230     }
 231 
 232     /**
 233      * Removes all values associated with given ClassLoader {@code cl} and
 234      * {@link #isEqualOrDescendantOf(AbstractClassLoaderValue) this or descendants}
 235      * of this ClassLoaderValue.
 236      * This is not an atomic operation. Other threads may see some associations
 237      * be already removed and others still present while this method is executing.
 238      * <p>
 239      * The sole intention of this method is to cleanup after a unit test that
 240      * tests ClassLoaderValue directly. It is not intended for use in
 241      * actual algorithms.
 242      *
 243      * @param cl the associated ClassLoader of the values to be removed
 244      */
 245     public void removeAll(ClassLoader cl) {
 246         ConcurrentHashMap<CLV, Object> map = map(cl);
 247         for (Iterator<CLV> i = map.keySet().iterator(); i.hasNext(); ) {
 248             if (i.next().isEqualOrDescendantOf(this)) {
 249                 i.remove();
 250             }
 251         }
 252     }
 253 
 254     private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess();
 255 
 256     /**
 257      * @return a ConcurrentHashMap for given ClassLoader
 258      */
 259     @SuppressWarnings("unchecked")
 260     private static <CLV extends AbstractClassLoaderValue<CLV, ?>>
 261     ConcurrentHashMap<CLV, Object> map(ClassLoader cl) {
 262         return (ConcurrentHashMap<CLV, Object>)
 263             (cl == null ? BootLoader.getClassLoaderValueMap()
 264                         : JLA.createOrGetClassLoaderValueMap(cl));
 265     }
 266 
 267     /**
 268      * @return value extracted from the {@link Memoizer} if given
 269      * {@code memoizerOrValue} parameter is a {@code Memoizer} or
 270      * just return given parameter.
 271      */
 272     @SuppressWarnings("unchecked")
 273     private V extractValue(Object memoizerOrValue) {
 274         if (memoizerOrValue instanceof Memoizer) {
 275             return ((Memoizer<?, V>) memoizerOrValue).get();
 276         } else {
 277             return (V) memoizerOrValue;
 278         }
 279     }
 280 
 281     /**
 282      * A memoized supplier that invokes given {@code mappingFunction} just once
 283      * and remembers the result or thrown exception for subsequent calls.
 284      * If given mappingFunction returns null, it is converted to NullPointerException,
 285      * thrown from the Memoizer's {@link #get()} method and remembered.
 286      * If the Memoizer is invoked recursively from the given {@code mappingFunction},
 287      * {@link RecursiveInvocationException} is thrown, but it is not remembered.
 288      * The in-flight call to the {@link #get()} can still complete successfully if
 289      * such exception is handled by the mappingFunction.
 290      */
 291     private static final class Memoizer<CLV extends AbstractClassLoaderValue<CLV, V>, V>
 292         implements Supplier<V> {
 293 
 294         private final ClassLoader cl;
 295         private final CLV clv;
 296         private final BiFunction<? super ClassLoader, ? super CLV, ? extends V>
 297             mappingFunction;
 298 
 299         private volatile V v;
 300         private volatile Throwable t;
 301         private boolean inCall;
 302 
 303         Memoizer(ClassLoader cl,
 304                  CLV clv,
 305                  BiFunction<? super ClassLoader, ? super CLV, ? extends V>
 306                      mappingFunction
 307         ) {
 308             this.cl = cl;
 309             this.clv = clv;
 310             this.mappingFunction = mappingFunction;
 311         }
 312 
 313         @Override
 314         public V get() throws RecursiveInvocationException {
 315             V v = this.v;
 316             if (v != null) return v;
 317             Throwable t = this.t;
 318             if (t == null) {
 319                 synchronized (this) {
 320                     if ((v = this.v) == null && (t = this.t) == null) {
 321                         if (inCall) {
 322                             throw new RecursiveInvocationException();
 323                         }
 324                         inCall = true;
 325                         try {
 326                             this.v = v = Objects.requireNonNull(
 327                                 mappingFunction.apply(cl, clv));
 328                         } catch (Throwable x) {
 329                             this.t = t = x;
 330                         } finally {
 331                             inCall = false;
 332                         }
 333                     }
 334                 }
 335             }
 336             if (v != null) return v;
 337             if (t instanceof Error) {
 338                 throw (Error) t;
 339             } else if (t instanceof RuntimeException) {
 340                 throw (RuntimeException) t;
 341             } else {
 342                 throw new UndeclaredThrowableException(t);
 343             }
 344         }
 345 
 346         static class RecursiveInvocationException extends IllegalStateException {
 347             private static final long serialVersionUID = 1L;
 348 
 349             RecursiveInvocationException() {
 350                 super("Recursive call");
 351             }
 352         }
 353     }
 354 
 355     /**
 356      * sub-ClassLoaderValue is an inner class of {@link AbstractClassLoaderValue}
 357      * and also a subclass of it. It can therefore be instantiated as an inner
 358      * class of either an instance of root-{@link ClassLoaderValue} or another
 359      * instance of itself. This enables composing type-safe compound keys of
 360      * arbitrary length:
 361      * <pre>{@code
 362      * ClassLoaderValue<V> clv = new ClassLoaderValue<>();
 363      * ClassLoaderValue<V>.Sub<K1>.Sub<K2>.Sub<K3> clv_k123 =
 364      *     clv.sub(k1).sub(k2).sub(k3);
 365      * }</pre>
 366      * From which individual components are accessible in a type-safe way:
 367      * <pre>{@code
 368      * K1 k1 = clv_k123.parent().parent().key();
 369      * K2 k2 = clv_k123.parent().key();
 370      * K3 k3 = clv_k123.key();
 371      * }</pre>
 372      * This allows specifying non-capturing lambdas for the mapping function of
 373      * {@link #computeIfAbsent(ClassLoader, BiFunction)} operation that can
 374      * access individual key components from passed-in
 375      * sub-[sub-...]ClassLoaderValue instance in a type-safe way.
 376      *
 377      * @param <K> the type of {@link #key()} component contained in the
 378      *            sub-ClassLoaderValue.
 379      */
 380     public final class Sub<K> extends AbstractClassLoaderValue<Sub<K>, V> {
 381 
 382         private final K key;
 383 
 384         Sub(K key) {
 385             this.key = key;
 386         }
 387 
 388         /**
 389          * @return the parent ClassLoaderValue this sub-ClassLoaderValue
 390          * has been {@link #sub(Object) derived} from.
 391          */
 392         public AbstractClassLoaderValue<CLV, V> parent() {
 393             return AbstractClassLoaderValue.this;
 394         }
 395 
 396         /**
 397          * @return the key component of this sub-ClassLoaderValue.
 398          */
 399         @Override
 400         public K key() {
 401             return key;
 402         }
 403 
 404         /**
 405          * sub-ClassLoaderValue is a descendant of given {@code clv} if it is
 406          * either equal to it or if its {@link #parent() parent} is a
 407          * descendant of given {@code clv}.
 408          */
 409         @Override
 410         public boolean isEqualOrDescendantOf(AbstractClassLoaderValue<?, V> clv) {
 411             return equals(Objects.requireNonNull(clv)) ||
 412                    parent().isEqualOrDescendantOf(clv);
 413         }
 414 
 415         @Override
 416         public boolean equals(Object o) {
 417             if (this == o) return true;
 418             if (!(o instanceof Sub)) return false;
 419             @SuppressWarnings("unchecked")
 420             Sub<?> that = (Sub<?>) o;
 421             return this.parent().equals(that.parent()) &&
 422                    Objects.equals(this.key, that.key);
 423         }
 424 
 425         @Override
 426         public int hashCode() {
 427             return 31 * parent().hashCode() +
 428                    Objects.hashCode(key);
 429         }
 430     }
 431 }