Skip to content

Commit 1b15655

Browse files
committed
Use volatile instead of Atomic when no CAS, use FieldUpdater otherwise
1 parent f0e274a commit 1b15655

File tree

1 file changed

+38
-23
lines changed

1 file changed

+38
-23
lines changed

client/src/main/java/org/asynchttpclient/netty/NettyResponseFuture.java

Lines changed: 38 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
import static org.asynchttpclient.util.DateUtils.millisTime;
1717
import static org.asynchttpclient.util.MiscUtils.getCause;
18+
import static io.netty.util.internal.PlatformDependent.*;
1819
import io.netty.channel.Channel;
1920

2021
import java.util.concurrent.CancellationException;
@@ -26,9 +27,8 @@
2627
import java.util.concurrent.TimeUnit;
2728
import java.util.concurrent.TimeoutException;
2829
import java.util.concurrent.atomic.AtomicBoolean;
29-
import java.util.concurrent.atomic.AtomicInteger;
30-
import java.util.concurrent.atomic.AtomicLong;
31-
import java.util.concurrent.atomic.AtomicReference;
30+
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
31+
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
3232

3333
import org.asynchttpclient.AsyncHandler;
3434
import org.asynchttpclient.Realm;
@@ -53,6 +53,15 @@ public final class NettyResponseFuture<V> extends AbstractListenableFuture<V> {
5353

5454
private static final Logger LOGGER = LoggerFactory.getLogger(NettyResponseFuture.class);
5555

56+
private static final AtomicIntegerFieldUpdater<NettyResponseFuture<?>> REDIRECT_COUNT_UPDATER = newAtomicIntegerFieldUpdater(NettyResponseFuture.class, "redirectCount");
57+
private static final AtomicIntegerFieldUpdater<NettyResponseFuture<?>> CURRENT_RETRY_UPDATER = newAtomicIntegerFieldUpdater(NettyResponseFuture.class, "currentRetry");
58+
@SuppressWarnings("rawtypes")
59+
// FIXME see https://github.com/netty/netty/pull/4669
60+
private static final AtomicReferenceFieldUpdater<NettyResponseFuture, Object> CONTENT_UPDATER = newAtomicReferenceFieldUpdater(NettyResponseFuture.class, "content");
61+
@SuppressWarnings("rawtypes")
62+
// FIXME see https://github.com/netty/netty/pull/4669
63+
private static final AtomicReferenceFieldUpdater<NettyResponseFuture, ExecutionException> EX_EX_UPDATER = newAtomicReferenceFieldUpdater(NettyResponseFuture.class, "exEx");
64+
5665
private final long start = millisTime();
5766
private final ChannelPoolPartitioning connectionPoolPartitioning;
5867
private final ProxyServer proxyServer;
@@ -63,18 +72,22 @@ public final class NettyResponseFuture<V> extends AbstractListenableFuture<V> {
6372
// TODO check if they are indeed mutated outside the event loop
6473
private final AtomicBoolean isDone = new AtomicBoolean(false);
6574
private final AtomicBoolean isCancelled = new AtomicBoolean(false);
66-
private final AtomicInteger redirectCount = new AtomicInteger();
6775
private final AtomicBoolean inAuth = new AtomicBoolean(false);
6876
private final AtomicBoolean inProxyAuth = new AtomicBoolean(false);
6977
private final AtomicBoolean statusReceived = new AtomicBoolean(false);
70-
private final AtomicLong touch = new AtomicLong(millisTime());
71-
private final AtomicReference<ChannelState> channelState = new AtomicReference<>(ChannelState.NEW);
7278
private final AtomicBoolean contentProcessed = new AtomicBoolean(false);
73-
private final AtomicInteger currentRetry = new AtomicInteger(0);
7479
private final AtomicBoolean onThrowableCalled = new AtomicBoolean(false);
75-
private final AtomicReference<V> content = new AtomicReference<>();
76-
private final AtomicReference<ExecutionException> exEx = new AtomicReference<>();
80+
81+
// volatile where we need CAS ops
82+
private volatile int redirectCount = 0;
83+
private volatile int currentRetry = 0;
84+
private volatile V content;
85+
private volatile ExecutionException exEx;
86+
87+
// volatile where we don't need CAS ops
88+
private volatile long touch = millisTime();
7789
private volatile TimeoutsHolder timeoutsHolder;
90+
private volatile ChannelState channelState = ChannelState.NEW;
7891

7992
// state mutated only inside the event loop
8093
private Channel channel;
@@ -162,13 +175,14 @@ private V getContent() throws ExecutionException {
162175
if (isCancelled())
163176
throw new CancellationException();
164177

165-
ExecutionException e = exEx.get();
178+
ExecutionException e = EX_EX_UPDATER.get(this);
166179
if (e != null)
167180
throw e;
168181

169-
V update = content.get();
182+
@SuppressWarnings("unchecked")
183+
V update = (V) CONTENT_UPDATER.get(this);
170184
// No more retry
171-
currentRetry.set(maxRetry);
185+
CURRENT_RETRY_UPDATER.set(this, maxRetry);
172186
if (!contentProcessed.getAndSet(true)) {
173187
try {
174188
update = asyncHandler.onCompleted();
@@ -186,7 +200,7 @@ private V getContent() throws ExecutionException {
186200
}
187201
}
188202
}
189-
content.compareAndSet(null, update);
203+
CONTENT_UPDATER.compareAndSet(this, null, update);
190204
}
191205
return update;
192206
}
@@ -211,7 +225,7 @@ public final void done() {
211225
} catch (ExecutionException t) {
212226
return;
213227
} catch (RuntimeException t) {
214-
exEx.compareAndSet(null, new ExecutionException(getCause(t)));
228+
EX_EX_UPDATER.compareAndSet(this, null, new ExecutionException(getCause(t)));
215229

216230
} finally {
217231
latch.countDown();
@@ -222,7 +236,7 @@ public final void done() {
222236

223237
public final void abort(final Throwable t) {
224238

225-
exEx.compareAndSet(null, new ExecutionException(t));
239+
EX_EX_UPDATER.compareAndSet(this, null, new ExecutionException(t));
226240

227241
if (terminateAndExit())
228242
return;
@@ -240,20 +254,21 @@ public final void abort(final Throwable t) {
240254

241255
@Override
242256
public void touch() {
243-
touch.set(millisTime());
257+
touch = millisTime();
244258
}
245259

246260
@Override
247261
public CompletableFuture<V> toCompletableFuture() {
248262
CompletableFuture<V> completable = new CompletableFuture<>();
249263
addListener(new Runnable() {
250264
@Override
265+
@SuppressWarnings("unchecked")
251266
public void run() {
252-
ExecutionException e = exEx.get();
267+
ExecutionException e = EX_EX_UPDATER.get(NettyResponseFuture.this);
253268
if (e != null)
254269
completable.completeExceptionally(e);
255270
else
256-
completable.complete(content.get());
271+
completable.complete((V) CONTENT_UPDATER.get(NettyResponseFuture.this));
257272
}
258273

259274
}, new Executor() {
@@ -320,7 +335,7 @@ public final void setKeepAlive(final boolean keepAlive) {
320335
}
321336

322337
public int incrementAndGetCurrentRedirectCount() {
323-
return redirectCount.incrementAndGet();
338+
return REDIRECT_COUNT_UPDATER.incrementAndGet(this);
324339
}
325340

326341
public void setTimeoutsHolder(TimeoutsHolder timeoutsHolder) {
@@ -340,11 +355,11 @@ public AtomicBoolean getInProxyAuth() {
340355
}
341356

342357
public ChannelState getChannelState() {
343-
return channelState.get();
358+
return channelState;
344359
}
345360

346361
public void setChannelState(ChannelState channelState) {
347-
this.channelState.set(channelState);
362+
this.channelState = channelState;
348363
}
349364

350365
public boolean getAndSetStatusReceived(boolean sr) {
@@ -360,7 +375,7 @@ public void setStreamWasAlreadyConsumed(boolean streamWasAlreadyConsumed) {
360375
}
361376

362377
public long getLastTouch() {
363-
return touch.get();
378+
return touch;
364379
}
365380

366381
public void setHeadersAlreadyWrittenOnContinue(boolean headersAlreadyWrittenOnContinue) {
@@ -411,7 +426,7 @@ public boolean reuseChannel() {
411426
}
412427

413428
public boolean canRetry() {
414-
return maxRetry > 0 && currentRetry.incrementAndGet() <= maxRetry;
429+
return maxRetry > 0 && CURRENT_RETRY_UPDATER.incrementAndGet(this) <= maxRetry;
415430
}
416431

417432
public void setTargetRequest(Request targetRequest) {

0 commit comments

Comments
 (0)