1 |
/* |
2 |
* Written by Doug Lea with assistance from members of JCP JSR-166 |
3 |
* Expert Group and released to the public domain, as explained at |
4 |
* http://creativecommons.org/publicdomain/zero/1.0/ |
5 |
*/ |
6 |
|
7 |
package java.util.concurrent.atomic; |
8 |
|
9 |
import sun.misc.Unsafe; |
10 |
import java.lang.reflect.Field; |
11 |
import java.lang.reflect.Modifier; |
12 |
import java.security.AccessController; |
13 |
import java.security.PrivilegedExceptionAction; |
14 |
import java.security.PrivilegedActionException; |
15 |
|
16 |
/** |
17 |
* A reflection-based utility that enables atomic updates to |
18 |
* designated {@code volatile long} fields of designated classes. |
19 |
* This class is designed for use in atomic data structures in which |
20 |
* several fields of the same node are independently subject to atomic |
21 |
* updates. |
22 |
* |
23 |
* <p>Note that the guarantees of the {@code compareAndSet} |
24 |
* method in this class are weaker than in other atomic classes. |
25 |
* Because this class cannot ensure that all uses of the field |
26 |
* are appropriate for purposes of atomic access, it can |
27 |
* guarantee atomicity only with respect to other invocations of |
28 |
* {@code compareAndSet} and {@code set} on the same updater. |
29 |
* |
30 |
* @since 1.5 |
31 |
* @author Doug Lea |
32 |
* @param <T> The type of the object holding the updatable field |
33 |
*/ |
34 |
public abstract class AtomicLongFieldUpdater<T> { |
35 |
/** |
36 |
* Creates and returns an updater for objects with the given field. |
37 |
* The Class argument is needed to check that reflective types and |
38 |
* generic types match. |
39 |
* |
40 |
* @param tclass the class of the objects holding the field |
41 |
* @param fieldName the name of the field to be updated |
42 |
* @param <U> the type of instances of tclass |
43 |
* @return the updater |
44 |
* @throws IllegalArgumentException if the field is not a |
45 |
* volatile long type |
46 |
* @throws RuntimeException with a nested reflection-based |
47 |
* exception if the class does not hold field or is the wrong type, |
48 |
* or the field is inaccessible to the caller according to Java language |
49 |
* access control |
50 |
*/ |
51 |
public static <U> AtomicLongFieldUpdater<U> newUpdater(Class<U> tclass, String fieldName) { |
52 |
if (AtomicLong.VM_SUPPORTS_LONG_CAS) |
53 |
return new CASUpdater<U>(tclass, fieldName); |
54 |
else |
55 |
return new LockedUpdater<U>(tclass, fieldName); |
56 |
} |
57 |
|
58 |
/** |
59 |
* Protected do-nothing constructor for use by subclasses. |
60 |
*/ |
61 |
protected AtomicLongFieldUpdater() { |
62 |
} |
63 |
|
64 |
/** |
65 |
* Atomically sets the field of the given object managed by this updater |
66 |
* to the given updated value if the current value {@code ==} the |
67 |
* expected value. This method is guaranteed to be atomic with respect to |
68 |
* other calls to {@code compareAndSet} and {@code set}, but not |
69 |
* necessarily with respect to other changes in the field. |
70 |
* |
71 |
* @param obj An object whose field to conditionally set |
72 |
* @param expect the expected value |
73 |
* @param update the new value |
74 |
* @return true if successful |
75 |
* @throws ClassCastException if {@code obj} is not an instance |
76 |
* of the class possessing the field established in the constructor |
77 |
*/ |
78 |
public abstract boolean compareAndSet(T obj, long expect, long update); |
79 |
|
80 |
/** |
81 |
* Atomically sets the field of the given object managed by this updater |
82 |
* to the given updated value if the current value {@code ==} the |
83 |
* expected value. This method is guaranteed to be atomic with respect to |
84 |
* other calls to {@code compareAndSet} and {@code set}, but not |
85 |
* necessarily with respect to other changes in the field. |
86 |
* |
87 |
* <p><a href="package-summary.html#weakCompareAndSet">May fail |
88 |
* spuriously and does not provide ordering guarantees</a>, so is |
89 |
* only rarely an appropriate alternative to {@code compareAndSet}. |
90 |
* |
91 |
* @param obj An object whose field to conditionally set |
92 |
* @param expect the expected value |
93 |
* @param update the new value |
94 |
* @return true if successful |
95 |
* @throws ClassCastException if {@code obj} is not an instance |
96 |
* of the class possessing the field established in the constructor |
97 |
*/ |
98 |
public abstract boolean weakCompareAndSet(T obj, long expect, long update); |
99 |
|
100 |
/** |
101 |
* Sets the field of the given object managed by this updater to the |
102 |
* given updated value. This operation is guaranteed to act as a volatile |
103 |
* store with respect to subsequent invocations of {@code compareAndSet}. |
104 |
* |
105 |
* @param obj An object whose field to set |
106 |
* @param newValue the new value |
107 |
*/ |
108 |
public abstract void set(T obj, long newValue); |
109 |
|
110 |
/** |
111 |
* Eventually sets the field of the given object managed by this |
112 |
* updater to the given updated value. |
113 |
* |
114 |
* @param obj An object whose field to set |
115 |
* @param newValue the new value |
116 |
* @since 1.6 |
117 |
*/ |
118 |
public abstract void lazySet(T obj, long newValue); |
119 |
|
120 |
/** |
121 |
* Gets the current value held in the field of the given object managed |
122 |
* by this updater. |
123 |
* |
124 |
* @param obj An object whose field to get |
125 |
* @return the current value |
126 |
*/ |
127 |
public abstract long get(T obj); |
128 |
|
129 |
/** |
130 |
* Atomically sets the field of the given object managed by this updater |
131 |
* to the given value and returns the old value. |
132 |
* |
133 |
* @param obj An object whose field to get and set |
134 |
* @param newValue the new value |
135 |
* @return the previous value |
136 |
*/ |
137 |
public long getAndSet(T obj, long newValue) { |
138 |
for (;;) { |
139 |
long current = get(obj); |
140 |
if (compareAndSet(obj, current, newValue)) |
141 |
return current; |
142 |
} |
143 |
} |
144 |
|
145 |
/** |
146 |
* Atomically increments by one the current value of the field of the |
147 |
* given object managed by this updater. |
148 |
* |
149 |
* @param obj An object whose field to get and set |
150 |
* @return the previous value |
151 |
*/ |
152 |
public long getAndIncrement(T obj) { |
153 |
for (;;) { |
154 |
long current = get(obj); |
155 |
long next = current + 1; |
156 |
if (compareAndSet(obj, current, next)) |
157 |
return current; |
158 |
} |
159 |
} |
160 |
|
161 |
/** |
162 |
* Atomically decrements by one the current value of the field of the |
163 |
* given object managed by this updater. |
164 |
* |
165 |
* @param obj An object whose field to get and set |
166 |
* @return the previous value |
167 |
*/ |
168 |
public long getAndDecrement(T obj) { |
169 |
for (;;) { |
170 |
long current = get(obj); |
171 |
long next = current - 1; |
172 |
if (compareAndSet(obj, current, next)) |
173 |
return current; |
174 |
} |
175 |
} |
176 |
|
177 |
/** |
178 |
* Atomically adds the given value to the current value of the field of |
179 |
* the given object managed by this updater. |
180 |
* |
181 |
* @param obj An object whose field to get and set |
182 |
* @param delta the value to add |
183 |
* @return the previous value |
184 |
*/ |
185 |
public long getAndAdd(T obj, long delta) { |
186 |
for (;;) { |
187 |
long current = get(obj); |
188 |
long next = current + delta; |
189 |
if (compareAndSet(obj, current, next)) |
190 |
return current; |
191 |
} |
192 |
} |
193 |
|
194 |
/** |
195 |
* Atomically increments by one the current value of the field of the |
196 |
* given object managed by this updater. |
197 |
* |
198 |
* @param obj An object whose field to get and set |
199 |
* @return the updated value |
200 |
*/ |
201 |
public long incrementAndGet(T obj) { |
202 |
for (;;) { |
203 |
long current = get(obj); |
204 |
long next = current + 1; |
205 |
if (compareAndSet(obj, current, next)) |
206 |
return next; |
207 |
} |
208 |
} |
209 |
|
210 |
/** |
211 |
* Atomically decrements by one the current value of the field of the |
212 |
* given object managed by this updater. |
213 |
* |
214 |
* @param obj An object whose field to get and set |
215 |
* @return the updated value |
216 |
*/ |
217 |
public long decrementAndGet(T obj) { |
218 |
for (;;) { |
219 |
long current = get(obj); |
220 |
long next = current - 1; |
221 |
if (compareAndSet(obj, current, next)) |
222 |
return next; |
223 |
} |
224 |
} |
225 |
|
226 |
/** |
227 |
* Atomically adds the given value to the current value of the field of |
228 |
* the given object managed by this updater. |
229 |
* |
230 |
* @param obj An object whose field to get and set |
231 |
* @param delta the value to add |
232 |
* @return the updated value |
233 |
*/ |
234 |
public long addAndGet(T obj, long delta) { |
235 |
for (;;) { |
236 |
long current = get(obj); |
237 |
long next = current + delta; |
238 |
if (compareAndSet(obj, current, next)) |
239 |
return next; |
240 |
} |
241 |
} |
242 |
|
243 |
private static class CASUpdater<T> extends AtomicLongFieldUpdater<T> { |
244 |
private static final Unsafe unsafe = Unsafe.getUnsafe(); |
245 |
private final long offset; |
246 |
private final Class<T> tclass; |
247 |
private final Class<?> cclass; |
248 |
|
249 |
CASUpdater(final Class<T> tclass, final String fieldName) { |
250 |
final Field field; |
251 |
final Class<?> caller; |
252 |
final int modifiers; |
253 |
try { |
254 |
field = AccessController.doPrivileged( |
255 |
new PrivilegedExceptionAction<Field>() { |
256 |
public Field run() throws NoSuchFieldException { |
257 |
return tclass.getDeclaredField(fieldName); |
258 |
} |
259 |
}); |
260 |
caller = sun.reflect.Reflection.getCallerClass(3); |
261 |
modifiers = field.getModifiers(); |
262 |
sun.reflect.misc.ReflectUtil.ensureMemberAccess( |
263 |
caller, tclass, null, modifiers); |
264 |
ClassLoader cl = tclass.getClassLoader(); |
265 |
ClassLoader ccl = caller.getClassLoader(); |
266 |
if ((ccl != null) && (ccl != cl) && |
267 |
((cl == null) || !isAncestor(cl, ccl))) { |
268 |
sun.reflect.misc.ReflectUtil.checkPackageAccess(tclass); |
269 |
} |
270 |
} catch (PrivilegedActionException pae) { |
271 |
throw new RuntimeException(pae.getException()); |
272 |
} catch (Exception ex) { |
273 |
throw new RuntimeException(ex); |
274 |
} |
275 |
|
276 |
Class<?> fieldt = field.getType(); |
277 |
if (fieldt != long.class) |
278 |
throw new IllegalArgumentException("Must be long type"); |
279 |
|
280 |
if (!Modifier.isVolatile(modifiers)) |
281 |
throw new IllegalArgumentException("Must be volatile type"); |
282 |
|
283 |
this.cclass = (Modifier.isProtected(modifiers) && |
284 |
caller != tclass) ? caller : null; |
285 |
this.tclass = tclass; |
286 |
offset = unsafe.objectFieldOffset(field); |
287 |
} |
288 |
|
289 |
private void fullCheck(T obj) { |
290 |
if (!tclass.isInstance(obj)) |
291 |
throw new ClassCastException(); |
292 |
if (cclass != null) |
293 |
ensureProtectedAccess(obj); |
294 |
} |
295 |
|
296 |
public boolean compareAndSet(T obj, long expect, long update) { |
297 |
if (obj == null || obj.getClass() != tclass || cclass != null) fullCheck(obj); |
298 |
return unsafe.compareAndSwapLong(obj, offset, expect, update); |
299 |
} |
300 |
|
301 |
public boolean weakCompareAndSet(T obj, long expect, long update) { |
302 |
if (obj == null || obj.getClass() != tclass || cclass != null) fullCheck(obj); |
303 |
return unsafe.compareAndSwapLong(obj, offset, expect, update); |
304 |
} |
305 |
|
306 |
public void set(T obj, long newValue) { |
307 |
if (obj == null || obj.getClass() != tclass || cclass != null) fullCheck(obj); |
308 |
unsafe.putLongVolatile(obj, offset, newValue); |
309 |
} |
310 |
|
311 |
public void lazySet(T obj, long newValue) { |
312 |
if (obj == null || obj.getClass() != tclass || cclass != null) fullCheck(obj); |
313 |
unsafe.putOrderedLong(obj, offset, newValue); |
314 |
} |
315 |
|
316 |
public long get(T obj) { |
317 |
if (obj == null || obj.getClass() != tclass || cclass != null) fullCheck(obj); |
318 |
return unsafe.getLongVolatile(obj, offset); |
319 |
} |
320 |
|
321 |
private void ensureProtectedAccess(T obj) { |
322 |
if (cclass.isInstance(obj)) { |
323 |
return; |
324 |
} |
325 |
throw new RuntimeException( |
326 |
new IllegalAccessException("Class " + |
327 |
cclass.getName() + |
328 |
" can not access a protected member of class " + |
329 |
tclass.getName() + |
330 |
" using an instance of " + |
331 |
obj.getClass().getName() |
332 |
) |
333 |
); |
334 |
} |
335 |
} |
336 |
|
337 |
|
338 |
private static class LockedUpdater<T> extends AtomicLongFieldUpdater<T> { |
339 |
private static final Unsafe unsafe = Unsafe.getUnsafe(); |
340 |
private final long offset; |
341 |
private final Class<T> tclass; |
342 |
private final Class<?> cclass; |
343 |
|
344 |
LockedUpdater(final Class<T> tclass, final String fieldName) { |
345 |
Field field = null; |
346 |
Class<?> caller = null; |
347 |
int modifiers = 0; |
348 |
try { |
349 |
field = AccessController.doPrivileged( |
350 |
new PrivilegedExceptionAction<Field>() { |
351 |
public Field run() throws NoSuchFieldException { |
352 |
return tclass.getDeclaredField(fieldName); |
353 |
} |
354 |
}); |
355 |
caller = sun.reflect.Reflection.getCallerClass(3); |
356 |
modifiers = field.getModifiers(); |
357 |
sun.reflect.misc.ReflectUtil.ensureMemberAccess( |
358 |
caller, tclass, null, modifiers); |
359 |
ClassLoader cl = tclass.getClassLoader(); |
360 |
ClassLoader ccl = caller.getClassLoader(); |
361 |
if ((ccl != null) && (ccl != cl) && |
362 |
((cl == null) || !isAncestor(cl, ccl))) { |
363 |
sun.reflect.misc.ReflectUtil.checkPackageAccess(tclass); |
364 |
} |
365 |
} catch (PrivilegedActionException pae) { |
366 |
throw new RuntimeException(pae.getException()); |
367 |
} catch (Exception ex) { |
368 |
throw new RuntimeException(ex); |
369 |
} |
370 |
|
371 |
Class<?> fieldt = field.getType(); |
372 |
if (fieldt != long.class) |
373 |
throw new IllegalArgumentException("Must be long type"); |
374 |
|
375 |
if (!Modifier.isVolatile(modifiers)) |
376 |
throw new IllegalArgumentException("Must be volatile type"); |
377 |
|
378 |
this.cclass = (Modifier.isProtected(modifiers) && |
379 |
caller != tclass) ? caller : null; |
380 |
this.tclass = tclass; |
381 |
offset = unsafe.objectFieldOffset(field); |
382 |
} |
383 |
|
384 |
private void fullCheck(T obj) { |
385 |
if (!tclass.isInstance(obj)) |
386 |
throw new ClassCastException(); |
387 |
if (cclass != null) |
388 |
ensureProtectedAccess(obj); |
389 |
} |
390 |
|
391 |
public boolean compareAndSet(T obj, long expect, long update) { |
392 |
if (obj == null || obj.getClass() != tclass || cclass != null) fullCheck(obj); |
393 |
synchronized (this) { |
394 |
long v = unsafe.getLong(obj, offset); |
395 |
if (v != expect) |
396 |
return false; |
397 |
unsafe.putLong(obj, offset, update); |
398 |
return true; |
399 |
} |
400 |
} |
401 |
|
402 |
public boolean weakCompareAndSet(T obj, long expect, long update) { |
403 |
return compareAndSet(obj, expect, update); |
404 |
} |
405 |
|
406 |
public void set(T obj, long newValue) { |
407 |
if (obj == null || obj.getClass() != tclass || cclass != null) fullCheck(obj); |
408 |
synchronized (this) { |
409 |
unsafe.putLong(obj, offset, newValue); |
410 |
} |
411 |
} |
412 |
|
413 |
public void lazySet(T obj, long newValue) { |
414 |
set(obj, newValue); |
415 |
} |
416 |
|
417 |
public long get(T obj) { |
418 |
if (obj == null || obj.getClass() != tclass || cclass != null) fullCheck(obj); |
419 |
synchronized (this) { |
420 |
return unsafe.getLong(obj, offset); |
421 |
} |
422 |
} |
423 |
|
424 |
private void ensureProtectedAccess(T obj) { |
425 |
if (cclass.isInstance(obj)) { |
426 |
return; |
427 |
} |
428 |
throw new RuntimeException( |
429 |
new IllegalAccessException("Class " + |
430 |
cclass.getName() + |
431 |
" can not access a protected member of class " + |
432 |
tclass.getName() + |
433 |
" using an instance of " + |
434 |
obj.getClass().getName() |
435 |
) |
436 |
); |
437 |
} |
438 |
} |
439 |
|
440 |
/** |
441 |
* Returns true if the second classloader can be found in the first |
442 |
* classloader's delegation chain. |
443 |
* Equivalent to the inaccessible: first.isAncestor(second). |
444 |
*/ |
445 |
private static boolean isAncestor(ClassLoader first, ClassLoader second) { |
446 |
ClassLoader acl = first; |
447 |
do { |
448 |
acl = acl.getParent(); |
449 |
if (second == acl) { |
450 |
return true; |
451 |
} |
452 |
} while (acl != null); |
453 |
return false; |
454 |
} |
455 |
} |