Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/src/java.base/share/classes/jdk/internal/loader/AbstractClassLoaderValue.java
41159 views
1
/*
2
* Copyright (c) 2016, 2019, 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
@java.io.Serial
350
private static final long serialVersionUID = 1L;
351
352
RecursiveInvocationException() {
353
super("Recursive call");
354
}
355
}
356
}
357
358
/**
359
* sub-ClassLoaderValue is an inner class of {@link AbstractClassLoaderValue}
360
* and also a subclass of it. It can therefore be instantiated as an inner
361
* class of either an instance of root-{@link ClassLoaderValue} or another
362
* instance of itself. This enables composing type-safe compound keys of
363
* arbitrary length:
364
* <pre>{@code
365
* ClassLoaderValue<V> clv = new ClassLoaderValue<>();
366
* ClassLoaderValue<V>.Sub<K1>.Sub<K2>.Sub<K3> clv_k123 =
367
* clv.sub(k1).sub(k2).sub(k3);
368
* }</pre>
369
* From which individual components are accessible in a type-safe way:
370
* <pre>{@code
371
* K1 k1 = clv_k123.parent().parent().key();
372
* K2 k2 = clv_k123.parent().key();
373
* K3 k3 = clv_k123.key();
374
* }</pre>
375
* This allows specifying non-capturing lambdas for the mapping function of
376
* {@link #computeIfAbsent(ClassLoader, BiFunction)} operation that can
377
* access individual key components from passed-in
378
* sub-[sub-...]ClassLoaderValue instance in a type-safe way.
379
*
380
* @param <K> the type of {@link #key()} component contained in the
381
* sub-ClassLoaderValue.
382
*/
383
public final class Sub<K> extends AbstractClassLoaderValue<Sub<K>, V> {
384
385
private final K key;
386
387
Sub(K key) {
388
this.key = key;
389
}
390
391
/**
392
* @return the parent ClassLoaderValue this sub-ClassLoaderValue
393
* has been {@link #sub(Object) derived} from.
394
*/
395
public AbstractClassLoaderValue<CLV, V> parent() {
396
return AbstractClassLoaderValue.this;
397
}
398
399
/**
400
* @return the key component of this sub-ClassLoaderValue.
401
*/
402
@Override
403
public K key() {
404
return key;
405
}
406
407
/**
408
* sub-ClassLoaderValue is a descendant of given {@code clv} if it is
409
* either equal to it or if its {@link #parent() parent} is a
410
* descendant of given {@code clv}.
411
*/
412
@Override
413
public boolean isEqualOrDescendantOf(AbstractClassLoaderValue<?, V> clv) {
414
return equals(Objects.requireNonNull(clv)) ||
415
parent().isEqualOrDescendantOf(clv);
416
}
417
418
@Override
419
public boolean equals(Object o) {
420
if (this == o) return true;
421
if (!(o instanceof Sub)) return false;
422
@SuppressWarnings("unchecked")
423
Sub<?> that = (Sub<?>) o;
424
return this.parent().equals(that.parent()) &&
425
Objects.equals(this.key, that.key);
426
}
427
428
@Override
429
public int hashCode() {
430
return 31 * parent().hashCode() +
431
Objects.hashCode(key);
432
}
433
}
434
}
435
436