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.misc.JavaLangAccess;
29 import jdk.internal.misc.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      */
AbstractClassLoaderValue()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      */
key()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      */
sub(K key)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      */
isEqualOrDescendantOf(AbstractClassLoaderValue<?, V> clv)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      */
get(ClassLoader cl)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      */
putIfAbsent(ClassLoader cl, V v)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      */
remove(ClassLoader cl, Object v)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      */
computeIfAbsent(ClassLoader cl, BiFunction< ? super ClassLoader, ? super CLV, ? extends V > mappingFunction)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      */
removeAll(ClassLoader cl)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, ?>>
map(ClassLoader cl)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")
extractValue(Object memoizerOrValue)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 
Memoizer(ClassLoader cl, CLV clv, BiFunction<? super ClassLoader, ? super CLV, ? extends V> mappingFunction )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
get()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 
RecursiveInvocationException()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 
Sub(K key)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          */
parent()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
key()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
isEqualOrDescendantOf(AbstractClassLoaderValue<?, V> clv)412         public boolean isEqualOrDescendantOf(AbstractClassLoaderValue<?, V> clv) {
413             return equals(Objects.requireNonNull(clv)) ||
414                    parent().isEqualOrDescendantOf(clv);
415         }
416 
417         @Override
equals(Object o)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
hashCode()428         public int hashCode() {
429             return 31 * parent().hashCode() +
430                    Objects.hashCode(key);
431         }
432     }
433 }
434