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