Skip to content

Commit 400af25

Browse files
cpovirkGoogle Java Core Libraries
authored and
Google Java Core Libraries
committed
Make AtomicReferenceFieldUpdater fields static for [better performance](https://shipilev.net/blog/2015/faster-atomic-fu/#:~:text=thrown%20out%20of%20the%20window).
This may eliminate the reason for [an `Unsafe`-based implementation](#6806) even under Java 8, but we will ideally confirm that with benchmarks before ripping that implementation out. (There's also Android: `AtomicReferenceFieldUpdater` is available there, but we should look into performance, including under older versions, especially with AFAIK no plans to remove `Unsafe` there.) (Also, make a few other tiny edits to the backport copy so that it matches the mainline copy more closely.) RELNOTES=n/a PiperOrigin-RevId: 713006636
1 parent 12bf71e commit 400af25

File tree

4 files changed

+77
-63
lines changed

4 files changed

+77
-63
lines changed

android/guava-tests/test/com/google/common/util/concurrent/AggregateFutureStateFallbackAtomicHelperTest.java

+7-1
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,13 @@ public static TestSuite suite() {
6868
// corresponding method on FuturesTest in the correct classloader.
6969
TestSuite suite = new TestSuite(AggregateFutureStateFallbackAtomicHelperTest.class.getName());
7070
for (Method method : FuturesTest.class.getDeclaredMethods()) {
71-
if (Modifier.isPublic(method.getModifiers()) && method.getName().startsWith("test")) {
71+
if (Modifier.isPublic(method.getModifiers())
72+
&& method.getName().startsWith("test")
73+
/*
74+
* When we block access to AtomicReferenceFieldUpdater, we can't even reflect on
75+
* AbstractFuture, since it declares methods that use that type in their signatures.
76+
*/
77+
&& !method.getName().equals("testFutures_nullChecks")) {
7278
suite.addTest(
7379
TestSuite.createTest(
7480
AggregateFutureStateFallbackAtomicHelperTest.class, method.getName()));

android/guava/src/com/google/common/util/concurrent/AbstractFuture.java

+33-32
Original file line numberDiff line numberDiff line change
@@ -159,24 +159,15 @@ public final boolean cancel(boolean mayInterruptIfRunning) {
159159
helper = new UnsafeAtomicHelper();
160160
} catch (Exception | Error unsafeFailure) { // sneaky checked exception
161161
thrownUnsafeFailure = unsafeFailure;
162-
// catch absolutely everything and fall through to our
163-
// 'AtomicReferenceFieldUpdaterAtomicHelper' The access control checks that ARFU does means
164-
// the caller class has to be AbstractFuture instead of
165-
// AtomicReferenceFieldUpdaterAtomicHelper, so we annoyingly define these here
162+
// Catch absolutely everything and fall through to AtomicReferenceFieldUpdaterAtomicHelper.
166163
try {
167-
helper =
168-
new AtomicReferenceFieldUpdaterAtomicHelper(
169-
newUpdater(Waiter.class, Thread.class, "thread"),
170-
newUpdater(Waiter.class, Waiter.class, "next"),
171-
newUpdater(AbstractFuture.class, Waiter.class, "waiters"),
172-
newUpdater(AbstractFuture.class, Listener.class, "listeners"),
173-
newUpdater(AbstractFuture.class, Object.class, "value"));
164+
helper = new AtomicReferenceFieldUpdaterAtomicHelper();
174165
} catch (Exception // sneaky checked exception
175166
| Error atomicReferenceFieldUpdaterFailure) {
176167
// Some Android 5.0.x Samsung devices have bugs in JDK reflection APIs that cause
177168
// getDeclaredField to throw a NoSuchFieldException when the field is definitely there.
178-
// For these users fallback to a suboptimal implementation, based on synchronized. This will
179-
// be a definite performance hit to those users.
169+
// For these users fallback to a suboptimal implementation, based on synchronized. This
170+
// will be a definite performance hit to those users.
180171
thrownAtomicReferenceFieldUpdaterFailure = atomicReferenceFieldUpdaterFailure;
181172
helper = new SynchronizedHelper();
182173
}
@@ -716,7 +707,7 @@ mayInterruptIfRunning, new CancellationException("Future.cancel() was called."))
716707
*
717708
* <p>The default implementation does nothing.
718709
*
719-
* <p>This method is likely to be deprecated. Prefer to override {@link #afterDone}, consulting
710+
* <p>This method is likely to be deprecated. Prefer to override {@link #afterDone}, checking
720711
* {@link #wasInterrupted} to decide whether to interrupt your task.
721712
*
722713
* @since 10.0
@@ -1447,24 +1438,16 @@ boolean casValue(AbstractFuture<?> future, @Nullable Object expect, Object updat
14471438

14481439
/** {@link AtomicHelper} based on {@link AtomicReferenceFieldUpdater}. */
14491440
private static final class AtomicReferenceFieldUpdaterAtomicHelper extends AtomicHelper {
1450-
final AtomicReferenceFieldUpdater<Waiter, Thread> waiterThreadUpdater;
1451-
final AtomicReferenceFieldUpdater<Waiter, Waiter> waiterNextUpdater;
1452-
final AtomicReferenceFieldUpdater<? super AbstractFuture<?>, Waiter> waitersUpdater;
1453-
final AtomicReferenceFieldUpdater<? super AbstractFuture<?>, Listener> listenersUpdater;
1454-
final AtomicReferenceFieldUpdater<? super AbstractFuture<?>, Object> valueUpdater;
1455-
1456-
AtomicReferenceFieldUpdaterAtomicHelper(
1457-
AtomicReferenceFieldUpdater<Waiter, Thread> waiterThreadUpdater,
1458-
AtomicReferenceFieldUpdater<Waiter, Waiter> waiterNextUpdater,
1459-
AtomicReferenceFieldUpdater<? super AbstractFuture<?>, Waiter> waitersUpdater,
1460-
AtomicReferenceFieldUpdater<? super AbstractFuture<?>, Listener> listenersUpdater,
1461-
AtomicReferenceFieldUpdater<? super AbstractFuture<?>, Object> valueUpdater) {
1462-
this.waiterThreadUpdater = waiterThreadUpdater;
1463-
this.waiterNextUpdater = waiterNextUpdater;
1464-
this.waitersUpdater = waitersUpdater;
1465-
this.listenersUpdater = listenersUpdater;
1466-
this.valueUpdater = valueUpdater;
1467-
}
1441+
private static final AtomicReferenceFieldUpdater<Waiter, Thread> waiterThreadUpdater =
1442+
newUpdater(Waiter.class, Thread.class, "thread");
1443+
private static final AtomicReferenceFieldUpdater<Waiter, Waiter> waiterNextUpdater =
1444+
newUpdater(Waiter.class, Waiter.class, "next");
1445+
private static final AtomicReferenceFieldUpdater<? super AbstractFuture<?>, Waiter>
1446+
waitersUpdater = waitersUpdaterFromWithinAbstractFuture();
1447+
private static final AtomicReferenceFieldUpdater<? super AbstractFuture<?>, Listener>
1448+
listenersUpdater = listenersUpdaterFromWithinAbstractFuture();
1449+
private static final AtomicReferenceFieldUpdater<? super AbstractFuture<?>, Object>
1450+
valueUpdater = valueUpdaterFromWithinAbstractFuture();
14681451

14691452
@Override
14701453
void putThread(Waiter waiter, Thread newValue) {
@@ -1502,6 +1485,24 @@ boolean casValue(AbstractFuture<?> future, @Nullable Object expect, Object updat
15021485
}
15031486
}
15041487

1488+
/** Returns an {@link AtomicReferenceFieldUpdater} for {@link #waiters}. */
1489+
private static AtomicReferenceFieldUpdater<? super AbstractFuture<?>, Waiter>
1490+
waitersUpdaterFromWithinAbstractFuture() {
1491+
return newUpdater(AbstractFuture.class, Waiter.class, "waiters");
1492+
}
1493+
1494+
/** Returns an {@link AtomicReferenceFieldUpdater} for {@link #listeners}. */
1495+
private static AtomicReferenceFieldUpdater<? super AbstractFuture<?>, Listener>
1496+
listenersUpdaterFromWithinAbstractFuture() {
1497+
return newUpdater(AbstractFuture.class, Listener.class, "listeners");
1498+
}
1499+
1500+
/** Returns an {@link AtomicReferenceFieldUpdater} for {@link #value}. */
1501+
private static AtomicReferenceFieldUpdater<? super AbstractFuture<?>, Object>
1502+
valueUpdaterFromWithinAbstractFuture() {
1503+
return newUpdater(AbstractFuture.class, Object.class, "value");
1504+
}
1505+
15051506
/**
15061507
* {@link AtomicHelper} based on {@code synchronized} and volatile writes.
15071508
*

guava-tests/test/com/google/common/util/concurrent/AggregateFutureStateFallbackAtomicHelperTest.java

+7-1
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,13 @@ public static TestSuite suite() {
6868
// corresponding method on FuturesTest in the correct classloader.
6969
TestSuite suite = new TestSuite(AggregateFutureStateFallbackAtomicHelperTest.class.getName());
7070
for (Method method : FuturesTest.class.getDeclaredMethods()) {
71-
if (Modifier.isPublic(method.getModifiers()) && method.getName().startsWith("test")) {
71+
if (Modifier.isPublic(method.getModifiers())
72+
&& method.getName().startsWith("test")
73+
/*
74+
* When we block access to AtomicReferenceFieldUpdater, we can't even reflect on
75+
* AbstractFuture, since it declares methods that use that type in their signatures.
76+
*/
77+
&& !method.getName().equals("testFutures_nullChecks")) {
7278
suite.addTest(
7379
TestSuite.createTest(
7480
AggregateFutureStateFallbackAtomicHelperTest.class, method.getName()));

guava/src/com/google/common/util/concurrent/AbstractFuture.java

+30-29
Original file line numberDiff line numberDiff line change
@@ -186,18 +186,9 @@ private static MethodHandles.Lookup methodHandlesLookupFromWithinAbstractFuture(
186186
helper = new UnsafeAtomicHelper();
187187
} catch (Exception | Error unsafeFailure) { // sneaky checked exception
188188
thrownUnsafeFailure = unsafeFailure;
189-
// catch absolutely everything and fall through to our
190-
// 'AtomicReferenceFieldUpdaterAtomicHelper' The access control checks that ARFU does means
191-
// the caller class has to be AbstractFuture instead of
192-
// AtomicReferenceFieldUpdaterAtomicHelper, so we annoyingly define these here
189+
// Catch absolutely everything and fall through to AtomicReferenceFieldUpdaterAtomicHelper.
193190
try {
194-
helper =
195-
new AtomicReferenceFieldUpdaterAtomicHelper(
196-
newUpdater(Waiter.class, Thread.class, "thread"),
197-
newUpdater(Waiter.class, Waiter.class, "next"),
198-
newUpdater(AbstractFuture.class, Waiter.class, "waiters"),
199-
newUpdater(AbstractFuture.class, Listener.class, "listeners"),
200-
newUpdater(AbstractFuture.class, Object.class, "value"));
191+
helper = new AtomicReferenceFieldUpdaterAtomicHelper();
201192
} catch (Exception // sneaky checked exception
202193
| Error atomicReferenceFieldUpdaterFailure) {
203194
// Some Android 5.0.x Samsung devices have bugs in JDK reflection APIs that cause
@@ -1559,24 +1550,16 @@ boolean casValue(AbstractFuture<?> future, @Nullable Object expect, Object updat
15591550

15601551
/** {@link AtomicHelper} based on {@link AtomicReferenceFieldUpdater}. */
15611552
private static final class AtomicReferenceFieldUpdaterAtomicHelper extends AtomicHelper {
1562-
final AtomicReferenceFieldUpdater<Waiter, Thread> waiterThreadUpdater;
1563-
final AtomicReferenceFieldUpdater<Waiter, Waiter> waiterNextUpdater;
1564-
final AtomicReferenceFieldUpdater<? super AbstractFuture<?>, Waiter> waitersUpdater;
1565-
final AtomicReferenceFieldUpdater<? super AbstractFuture<?>, Listener> listenersUpdater;
1566-
final AtomicReferenceFieldUpdater<? super AbstractFuture<?>, Object> valueUpdater;
1567-
1568-
AtomicReferenceFieldUpdaterAtomicHelper(
1569-
AtomicReferenceFieldUpdater<Waiter, Thread> waiterThreadUpdater,
1570-
AtomicReferenceFieldUpdater<Waiter, Waiter> waiterNextUpdater,
1571-
AtomicReferenceFieldUpdater<? super AbstractFuture<?>, Waiter> waitersUpdater,
1572-
AtomicReferenceFieldUpdater<? super AbstractFuture<?>, Listener> listenersUpdater,
1573-
AtomicReferenceFieldUpdater<? super AbstractFuture<?>, Object> valueUpdater) {
1574-
this.waiterThreadUpdater = waiterThreadUpdater;
1575-
this.waiterNextUpdater = waiterNextUpdater;
1576-
this.waitersUpdater = waitersUpdater;
1577-
this.listenersUpdater = listenersUpdater;
1578-
this.valueUpdater = valueUpdater;
1579-
}
1553+
private static final AtomicReferenceFieldUpdater<Waiter, Thread> waiterThreadUpdater =
1554+
newUpdater(Waiter.class, Thread.class, "thread");
1555+
private static final AtomicReferenceFieldUpdater<Waiter, Waiter> waiterNextUpdater =
1556+
newUpdater(Waiter.class, Waiter.class, "next");
1557+
private static final AtomicReferenceFieldUpdater<? super AbstractFuture<?>, Waiter>
1558+
waitersUpdater = waitersUpdaterFromWithinAbstractFuture();
1559+
private static final AtomicReferenceFieldUpdater<? super AbstractFuture<?>, Listener>
1560+
listenersUpdater = listenersUpdaterFromWithinAbstractFuture();
1561+
private static final AtomicReferenceFieldUpdater<? super AbstractFuture<?>, Object>
1562+
valueUpdater = valueUpdaterFromWithinAbstractFuture();
15801563

15811564
@Override
15821565
void putThread(Waiter waiter, Thread newValue) {
@@ -1614,6 +1597,24 @@ boolean casValue(AbstractFuture<?> future, @Nullable Object expect, Object updat
16141597
}
16151598
}
16161599

1600+
/** Returns an {@link AtomicReferenceFieldUpdater} for {@link #waiters}. */
1601+
private static AtomicReferenceFieldUpdater<? super AbstractFuture<?>, Waiter>
1602+
waitersUpdaterFromWithinAbstractFuture() {
1603+
return newUpdater(AbstractFuture.class, Waiter.class, "waiters");
1604+
}
1605+
1606+
/** Returns an {@link AtomicReferenceFieldUpdater} for {@link #listeners}. */
1607+
private static AtomicReferenceFieldUpdater<? super AbstractFuture<?>, Listener>
1608+
listenersUpdaterFromWithinAbstractFuture() {
1609+
return newUpdater(AbstractFuture.class, Listener.class, "listeners");
1610+
}
1611+
1612+
/** Returns an {@link AtomicReferenceFieldUpdater} for {@link #value}. */
1613+
private static AtomicReferenceFieldUpdater<? super AbstractFuture<?>, Object>
1614+
valueUpdaterFromWithinAbstractFuture() {
1615+
return newUpdater(AbstractFuture.class, Object.class, "value");
1616+
}
1617+
16171618
/**
16181619
* {@link AtomicHelper} based on {@code synchronized} and volatile writes.
16191620
*

0 commit comments

Comments
 (0)