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