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 java.lang.reflect; |
|
25 |
|
26 import jdk.internal.loader.BootLoader; |
|
27 import jdk.internal.misc.JavaLangAccess; |
|
28 import jdk.internal.misc.SharedSecrets; |
|
29 |
|
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 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 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 } |
|