From 6423e80932b1752e11832878147cc7655f757ef5 Mon Sep 17 00:00:00 2001 From: Nick Hill Date: Mon, 9 Sep 2019 00:54:38 -0700 Subject: [PATCH] Avoid redundant volatile read in DefaultPromise#get() (#9547) Motivation Currently every call to get() on a promise results in two reads of the volatile result field when one would suffice. Maybe this is optimized away but it seems sensible not to rely on that. Modification Reimplement get() and get(...) in DefaultPromise to reduce volatile access. Result Fewer volatile reads. --- .../netty/util/concurrent/DefaultPromise.java | 67 ++++++++++++++++--- 1 file changed, 59 insertions(+), 8 deletions(-) diff --git a/common/src/main/java/io/netty/util/concurrent/DefaultPromise.java b/common/src/main/java/io/netty/util/concurrent/DefaultPromise.java index 6facc2fe71..2ff8c68275 100644 --- a/common/src/main/java/io/netty/util/concurrent/DefaultPromise.java +++ b/common/src/main/java/io/netty/util/concurrent/DefaultPromise.java @@ -16,7 +16,6 @@ package io.netty.util.concurrent; import io.netty.util.internal.InternalThreadLocalMap; -import io.netty.util.internal.PlatformDependent; import io.netty.util.internal.StringUtil; import io.netty.util.internal.SystemPropertyUtil; import io.netty.util.internal.ThrowableUtil; @@ -25,7 +24,9 @@ import io.netty.util.internal.logging.InternalLoggerFactory; import java.util.concurrent.CancellationException; import java.util.concurrent.CompletionException; +import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; import static java.util.Objects.requireNonNull; @@ -145,15 +146,21 @@ public class DefaultPromise implements Promise { @Override public Throwable cause() { - Object result = this.result; - if (result != CANCELLATION_CAUSE_HOLDER) { - return (result instanceof CauseHolder) ? ((CauseHolder) result).cause : null; + return cause0(result); + } + + private Throwable cause0(Object result) { + if (!(result instanceof CauseHolder)) { + return null; } - CancellationException ce = new LeanCancellationException(); - if (RESULT_UPDATER.compareAndSet(this, CANCELLATION_CAUSE_HOLDER, new CauseHolder(ce))) { - return ce; + if (result == CANCELLATION_CAUSE_HOLDER) { + CancellationException ce = new LeanCancellationException(); + if (RESULT_UPDATER.compareAndSet(this, CANCELLATION_CAUSE_HOLDER, new CauseHolder(ce))) { + return ce; + } + result = this.result; } - return ((CauseHolder) this.result).cause; + return ((CauseHolder) result).cause; } @Override @@ -313,6 +320,50 @@ public class DefaultPromise implements Promise { return (V) result; } + @SuppressWarnings("unchecked") + @Override + public V get() throws InterruptedException, ExecutionException { + Object result = this.result; + if (!isDone0(result)) { + await(); + result = this.result; + } + if (result == SUCCESS || result == UNCANCELLABLE) { + return null; + } + Throwable cause = cause0(result); + if (cause == null) { + return (V) result; + } + if (cause instanceof CancellationException) { + throw (CancellationException) cause; + } + throw new ExecutionException(cause); + } + + @SuppressWarnings("unchecked") + @Override + public V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { + Object result = this.result; + if (!isDone0(result)) { + if (!await(timeout, unit)) { + throw new TimeoutException(); + } + result = this.result; + } + if (result == SUCCESS || result == UNCANCELLABLE) { + return null; + } + Throwable cause = cause0(result); + if (cause == null) { + return (V) result; + } + if (cause instanceof CancellationException) { + throw (CancellationException) cause; + } + throw new ExecutionException(cause); + } + /** * {@inheritDoc} *