mirror of https://github.com/grpc/grpc-java.git
core: workaround for Atomic*FieldUpdater bug on some Android devices
This commit is contained in:
parent
2f155606b8
commit
4c483ef7a0
|
|
@ -52,6 +52,7 @@ import java.util.concurrent.atomic.AtomicLongFieldUpdater;
|
||||||
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
|
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides factories for {@link StreamTracer} that records stats to Census.
|
* Provides factories for {@link StreamTracer} that records stats to Census.
|
||||||
|
|
@ -154,18 +155,58 @@ public final class CensusStatsModule {
|
||||||
|
|
||||||
private static final class ClientTracer extends ClientStreamTracer {
|
private static final class ClientTracer extends ClientStreamTracer {
|
||||||
|
|
||||||
private static final AtomicLongFieldUpdater<ClientTracer> outboundMessageCountUpdater =
|
@Nullable private static final AtomicLongFieldUpdater<ClientTracer> outboundMessageCountUpdater;
|
||||||
AtomicLongFieldUpdater.newUpdater(ClientTracer.class, "outboundMessageCount");
|
@Nullable private static final AtomicLongFieldUpdater<ClientTracer> inboundMessageCountUpdater;
|
||||||
private static final AtomicLongFieldUpdater<ClientTracer> inboundMessageCountUpdater =
|
@Nullable private static final AtomicLongFieldUpdater<ClientTracer> outboundWireSizeUpdater;
|
||||||
AtomicLongFieldUpdater.newUpdater(ClientTracer.class, "inboundMessageCount");
|
@Nullable private static final AtomicLongFieldUpdater<ClientTracer> inboundWireSizeUpdater;
|
||||||
private static final AtomicLongFieldUpdater<ClientTracer> outboundWireSizeUpdater =
|
|
||||||
AtomicLongFieldUpdater.newUpdater(ClientTracer.class, "outboundWireSize");
|
@Nullable
|
||||||
private static final AtomicLongFieldUpdater<ClientTracer> inboundWireSizeUpdater =
|
private static final AtomicLongFieldUpdater<ClientTracer> outboundUncompressedSizeUpdater;
|
||||||
AtomicLongFieldUpdater.newUpdater(ClientTracer.class, "inboundWireSize");
|
|
||||||
private static final AtomicLongFieldUpdater<ClientTracer> outboundUncompressedSizeUpdater =
|
@Nullable
|
||||||
AtomicLongFieldUpdater.newUpdater(ClientTracer.class, "outboundUncompressedSize");
|
private static final AtomicLongFieldUpdater<ClientTracer> inboundUncompressedSizeUpdater;
|
||||||
private static final AtomicLongFieldUpdater<ClientTracer> inboundUncompressedSizeUpdater =
|
|
||||||
AtomicLongFieldUpdater.newUpdater(ClientTracer.class, "inboundUncompressedSize");
|
/**
|
||||||
|
* When using Atomic*FieldUpdater, some Samsung Android 5.0.x devices encounter a bug in their
|
||||||
|
* JDK reflection API that triggers a NoSuchFieldException. When this occurs, we fallback to
|
||||||
|
* (potentially racy) direct updates of the volatile variables.
|
||||||
|
*/
|
||||||
|
static {
|
||||||
|
AtomicLongFieldUpdater<ClientTracer> tmpOutboundMessageCountUpdater;
|
||||||
|
AtomicLongFieldUpdater<ClientTracer> tmpInboundMessageCountUpdater;
|
||||||
|
AtomicLongFieldUpdater<ClientTracer> tmpOutboundWireSizeUpdater;
|
||||||
|
AtomicLongFieldUpdater<ClientTracer> tmpInboundWireSizeUpdater;
|
||||||
|
AtomicLongFieldUpdater<ClientTracer> tmpOutboundUncompressedSizeUpdater;
|
||||||
|
AtomicLongFieldUpdater<ClientTracer> tmpInboundUncompressedSizeUpdater;
|
||||||
|
try {
|
||||||
|
tmpOutboundMessageCountUpdater =
|
||||||
|
AtomicLongFieldUpdater.newUpdater(ClientTracer.class, "outboundMessageCount");
|
||||||
|
tmpInboundMessageCountUpdater =
|
||||||
|
AtomicLongFieldUpdater.newUpdater(ClientTracer.class, "inboundMessageCount");
|
||||||
|
tmpOutboundWireSizeUpdater =
|
||||||
|
AtomicLongFieldUpdater.newUpdater(ClientTracer.class, "outboundWireSize");
|
||||||
|
tmpInboundWireSizeUpdater =
|
||||||
|
AtomicLongFieldUpdater.newUpdater(ClientTracer.class, "inboundWireSize");
|
||||||
|
tmpOutboundUncompressedSizeUpdater =
|
||||||
|
AtomicLongFieldUpdater.newUpdater(ClientTracer.class, "outboundUncompressedSize");
|
||||||
|
tmpInboundUncompressedSizeUpdater =
|
||||||
|
AtomicLongFieldUpdater.newUpdater(ClientTracer.class, "inboundUncompressedSize");
|
||||||
|
} catch (Throwable t) {
|
||||||
|
logger.log(Level.SEVERE, "Creating atomic field updaters failed", t);
|
||||||
|
tmpOutboundMessageCountUpdater = null;
|
||||||
|
tmpInboundMessageCountUpdater = null;
|
||||||
|
tmpOutboundWireSizeUpdater = null;
|
||||||
|
tmpInboundWireSizeUpdater = null;
|
||||||
|
tmpOutboundUncompressedSizeUpdater = null;
|
||||||
|
tmpInboundUncompressedSizeUpdater = null;
|
||||||
|
}
|
||||||
|
outboundMessageCountUpdater = tmpOutboundMessageCountUpdater;
|
||||||
|
inboundMessageCountUpdater = tmpInboundMessageCountUpdater;
|
||||||
|
outboundWireSizeUpdater = tmpOutboundWireSizeUpdater;
|
||||||
|
inboundWireSizeUpdater = tmpInboundWireSizeUpdater;
|
||||||
|
outboundUncompressedSizeUpdater = tmpOutboundUncompressedSizeUpdater;
|
||||||
|
inboundUncompressedSizeUpdater = tmpInboundUncompressedSizeUpdater;
|
||||||
|
}
|
||||||
|
|
||||||
volatile long outboundMessageCount;
|
volatile long outboundMessageCount;
|
||||||
volatile long inboundMessageCount;
|
volatile long inboundMessageCount;
|
||||||
|
|
@ -175,33 +216,63 @@ public final class CensusStatsModule {
|
||||||
volatile long inboundUncompressedSize;
|
volatile long inboundUncompressedSize;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@SuppressWarnings("NonAtomicVolatileUpdate")
|
||||||
public void outboundWireSize(long bytes) {
|
public void outboundWireSize(long bytes) {
|
||||||
outboundWireSizeUpdater.getAndAdd(this, bytes);
|
if (outboundWireSizeUpdater != null) {
|
||||||
|
outboundWireSizeUpdater.getAndAdd(this, bytes);
|
||||||
|
} else {
|
||||||
|
outboundWireSize += bytes;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@SuppressWarnings("NonAtomicVolatileUpdate")
|
||||||
public void inboundWireSize(long bytes) {
|
public void inboundWireSize(long bytes) {
|
||||||
inboundWireSizeUpdater.getAndAdd(this, bytes);
|
if (inboundWireSizeUpdater != null) {
|
||||||
|
inboundWireSizeUpdater.getAndAdd(this, bytes);
|
||||||
|
} else {
|
||||||
|
inboundWireSize += bytes;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@SuppressWarnings("NonAtomicVolatileUpdate")
|
||||||
public void outboundUncompressedSize(long bytes) {
|
public void outboundUncompressedSize(long bytes) {
|
||||||
outboundUncompressedSizeUpdater.getAndAdd(this, bytes);
|
if (outboundUncompressedSizeUpdater != null) {
|
||||||
|
outboundUncompressedSizeUpdater.getAndAdd(this, bytes);
|
||||||
|
} else {
|
||||||
|
outboundUncompressedSize += bytes;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@SuppressWarnings("NonAtomicVolatileUpdate")
|
||||||
public void inboundUncompressedSize(long bytes) {
|
public void inboundUncompressedSize(long bytes) {
|
||||||
inboundUncompressedSizeUpdater.getAndAdd(this, bytes);
|
if (inboundUncompressedSizeUpdater != null) {
|
||||||
|
inboundUncompressedSizeUpdater.getAndAdd(this, bytes);
|
||||||
|
} else {
|
||||||
|
inboundUncompressedSize += bytes;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@SuppressWarnings("NonAtomicVolatileUpdate")
|
||||||
public void inboundMessage(int seqNo) {
|
public void inboundMessage(int seqNo) {
|
||||||
inboundMessageCountUpdater.getAndIncrement(this);
|
if (inboundMessageCountUpdater != null) {
|
||||||
|
inboundMessageCountUpdater.getAndIncrement(this);
|
||||||
|
} else {
|
||||||
|
inboundMessageCount++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@SuppressWarnings("NonAtomicVolatileUpdate")
|
||||||
public void outboundMessage(int seqNo) {
|
public void outboundMessage(int seqNo) {
|
||||||
outboundMessageCountUpdater.getAndIncrement(this);
|
if (outboundMessageCountUpdater != null) {
|
||||||
|
outboundMessageCountUpdater.getAndIncrement(this);
|
||||||
|
} else {
|
||||||
|
outboundMessageCount++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -209,12 +280,34 @@ public final class CensusStatsModule {
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
static final class ClientCallTracer extends ClientStreamTracer.Factory {
|
static final class ClientCallTracer extends ClientStreamTracer.Factory {
|
||||||
|
@Nullable
|
||||||
private static final AtomicReferenceFieldUpdater<ClientCallTracer, ClientTracer>
|
private static final AtomicReferenceFieldUpdater<ClientCallTracer, ClientTracer>
|
||||||
streamTracerUpdater =
|
streamTracerUpdater;
|
||||||
|
|
||||||
|
@Nullable private static final AtomicIntegerFieldUpdater<ClientCallTracer> callEndedUpdater;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When using Atomic*FieldUpdater, some Samsung Android 5.0.x devices encounter a bug in their
|
||||||
|
* JDK reflection API that triggers a NoSuchFieldException. When this occurs, we fallback to
|
||||||
|
* (potentially racy) direct updates of the volatile variables.
|
||||||
|
*/
|
||||||
|
static {
|
||||||
|
AtomicReferenceFieldUpdater<ClientCallTracer, ClientTracer> tmpStreamTracerUpdater;
|
||||||
|
AtomicIntegerFieldUpdater<ClientCallTracer> tmpCallEndedUpdater;
|
||||||
|
try {
|
||||||
|
tmpStreamTracerUpdater =
|
||||||
AtomicReferenceFieldUpdater.newUpdater(
|
AtomicReferenceFieldUpdater.newUpdater(
|
||||||
ClientCallTracer.class, ClientTracer.class, "streamTracer");
|
ClientCallTracer.class, ClientTracer.class, "streamTracer");
|
||||||
private static final AtomicIntegerFieldUpdater<ClientCallTracer> callEndedUpdater =
|
tmpCallEndedUpdater =
|
||||||
AtomicIntegerFieldUpdater.newUpdater(ClientCallTracer.class, "callEnded");
|
AtomicIntegerFieldUpdater.newUpdater(ClientCallTracer.class, "callEnded");
|
||||||
|
} catch (Throwable t) {
|
||||||
|
logger.log(Level.SEVERE, "Creating atomic field updaters failed", t);
|
||||||
|
tmpStreamTracerUpdater = null;
|
||||||
|
tmpCallEndedUpdater = null;
|
||||||
|
}
|
||||||
|
streamTracerUpdater = tmpStreamTracerUpdater;
|
||||||
|
callEndedUpdater = tmpCallEndedUpdater;
|
||||||
|
}
|
||||||
|
|
||||||
private final CensusStatsModule module;
|
private final CensusStatsModule module;
|
||||||
private final String fullMethodName;
|
private final String fullMethodName;
|
||||||
|
|
@ -250,9 +343,16 @@ public final class CensusStatsModule {
|
||||||
ClientTracer tracer = new ClientTracer();
|
ClientTracer tracer = new ClientTracer();
|
||||||
// TODO(zhangkun83): Once retry or hedging is implemented, a ClientCall may start more than
|
// TODO(zhangkun83): Once retry or hedging is implemented, a ClientCall may start more than
|
||||||
// one streams. We will need to update this file to support them.
|
// one streams. We will need to update this file to support them.
|
||||||
checkState(
|
if (streamTracerUpdater != null) {
|
||||||
streamTracerUpdater.compareAndSet(this, null, tracer),
|
checkState(
|
||||||
"Are you creating multiple streams per call? This class doesn't yet support this case.");
|
streamTracerUpdater.compareAndSet(this, null, tracer),
|
||||||
|
"Are you creating multiple streams per call? This class doesn't yet support this case");
|
||||||
|
} else {
|
||||||
|
checkState(
|
||||||
|
streamTracer == null,
|
||||||
|
"Are you creating multiple streams per call? This class doesn't yet support this case");
|
||||||
|
streamTracer = tracer;
|
||||||
|
}
|
||||||
if (module.propagateTags) {
|
if (module.propagateTags) {
|
||||||
headers.discardAll(module.statsHeader);
|
headers.discardAll(module.statsHeader);
|
||||||
if (!module.tagger.empty().equals(parentCtx)) {
|
if (!module.tagger.empty().equals(parentCtx)) {
|
||||||
|
|
@ -269,8 +369,15 @@ public final class CensusStatsModule {
|
||||||
* is a no-op.
|
* is a no-op.
|
||||||
*/
|
*/
|
||||||
void callEnded(Status status) {
|
void callEnded(Status status) {
|
||||||
if (callEndedUpdater.getAndSet(this, 1) != 0) {
|
if (callEndedUpdater != null) {
|
||||||
return;
|
if (callEndedUpdater.getAndSet(this, 1) != 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (callEnded != 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
callEnded = 1;
|
||||||
}
|
}
|
||||||
if (!recordFinishedRpcs) {
|
if (!recordFinishedRpcs) {
|
||||||
return;
|
return;
|
||||||
|
|
@ -308,20 +415,64 @@ public final class CensusStatsModule {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final class ServerTracer extends ServerStreamTracer {
|
private static final class ServerTracer extends ServerStreamTracer {
|
||||||
private static final AtomicIntegerFieldUpdater<ServerTracer> streamClosedUpdater =
|
@Nullable private static final AtomicIntegerFieldUpdater<ServerTracer> streamClosedUpdater;
|
||||||
AtomicIntegerFieldUpdater.newUpdater(ServerTracer.class, "streamClosed");
|
@Nullable private static final AtomicLongFieldUpdater<ServerTracer> outboundMessageCountUpdater;
|
||||||
private static final AtomicLongFieldUpdater<ServerTracer> outboundMessageCountUpdater =
|
@Nullable private static final AtomicLongFieldUpdater<ServerTracer> inboundMessageCountUpdater;
|
||||||
AtomicLongFieldUpdater.newUpdater(ServerTracer.class, "outboundMessageCount");
|
@Nullable private static final AtomicLongFieldUpdater<ServerTracer> outboundWireSizeUpdater;
|
||||||
private static final AtomicLongFieldUpdater<ServerTracer> inboundMessageCountUpdater =
|
@Nullable private static final AtomicLongFieldUpdater<ServerTracer> inboundWireSizeUpdater;
|
||||||
AtomicLongFieldUpdater.newUpdater(ServerTracer.class, "inboundMessageCount");
|
|
||||||
private static final AtomicLongFieldUpdater<ServerTracer> outboundWireSizeUpdater =
|
@Nullable
|
||||||
AtomicLongFieldUpdater.newUpdater(ServerTracer.class, "outboundWireSize");
|
private static final AtomicLongFieldUpdater<ServerTracer> outboundUncompressedSizeUpdater;
|
||||||
private static final AtomicLongFieldUpdater<ServerTracer> inboundWireSizeUpdater =
|
|
||||||
AtomicLongFieldUpdater.newUpdater(ServerTracer.class, "inboundWireSize");
|
@Nullable
|
||||||
private static final AtomicLongFieldUpdater<ServerTracer> outboundUncompressedSizeUpdater =
|
private static final AtomicLongFieldUpdater<ServerTracer> inboundUncompressedSizeUpdater;
|
||||||
AtomicLongFieldUpdater.newUpdater(ServerTracer.class, "outboundUncompressedSize");
|
|
||||||
private static final AtomicLongFieldUpdater<ServerTracer> inboundUncompressedSizeUpdater =
|
/**
|
||||||
AtomicLongFieldUpdater.newUpdater(ServerTracer.class, "inboundUncompressedSize");
|
* When using Atomic*FieldUpdater, some Samsung Android 5.0.x devices encounter a bug in their
|
||||||
|
* JDK reflection API that triggers a NoSuchFieldException. When this occurs, we fallback to
|
||||||
|
* (potentially racy) direct updates of the volatile variables.
|
||||||
|
*/
|
||||||
|
static {
|
||||||
|
AtomicIntegerFieldUpdater<ServerTracer> tmpStreamClosedUpdater;
|
||||||
|
AtomicLongFieldUpdater<ServerTracer> tmpOutboundMessageCountUpdater;
|
||||||
|
AtomicLongFieldUpdater<ServerTracer> tmpInboundMessageCountUpdater;
|
||||||
|
AtomicLongFieldUpdater<ServerTracer> tmpOutboundWireSizeUpdater;
|
||||||
|
AtomicLongFieldUpdater<ServerTracer> tmpInboundWireSizeUpdater;
|
||||||
|
AtomicLongFieldUpdater<ServerTracer> tmpOutboundUncompressedSizeUpdater;
|
||||||
|
AtomicLongFieldUpdater<ServerTracer> tmpInboundUncompressedSizeUpdater;
|
||||||
|
try {
|
||||||
|
tmpStreamClosedUpdater =
|
||||||
|
AtomicIntegerFieldUpdater.newUpdater(ServerTracer.class, "streamClosed");
|
||||||
|
tmpOutboundMessageCountUpdater =
|
||||||
|
AtomicLongFieldUpdater.newUpdater(ServerTracer.class, "outboundMessageCount");
|
||||||
|
tmpInboundMessageCountUpdater =
|
||||||
|
AtomicLongFieldUpdater.newUpdater(ServerTracer.class, "inboundMessageCount");
|
||||||
|
tmpOutboundWireSizeUpdater =
|
||||||
|
AtomicLongFieldUpdater.newUpdater(ServerTracer.class, "outboundWireSize");
|
||||||
|
tmpInboundWireSizeUpdater =
|
||||||
|
AtomicLongFieldUpdater.newUpdater(ServerTracer.class, "inboundWireSize");
|
||||||
|
tmpOutboundUncompressedSizeUpdater =
|
||||||
|
AtomicLongFieldUpdater.newUpdater(ServerTracer.class, "outboundUncompressedSize");
|
||||||
|
tmpInboundUncompressedSizeUpdater =
|
||||||
|
AtomicLongFieldUpdater.newUpdater(ServerTracer.class, "inboundUncompressedSize");
|
||||||
|
} catch (Throwable t) {
|
||||||
|
logger.log(Level.SEVERE, "Creating atomic field updaters failed", t);
|
||||||
|
tmpStreamClosedUpdater = null;
|
||||||
|
tmpOutboundMessageCountUpdater = null;
|
||||||
|
tmpInboundMessageCountUpdater = null;
|
||||||
|
tmpOutboundWireSizeUpdater = null;
|
||||||
|
tmpInboundWireSizeUpdater = null;
|
||||||
|
tmpOutboundUncompressedSizeUpdater = null;
|
||||||
|
tmpInboundUncompressedSizeUpdater = null;
|
||||||
|
}
|
||||||
|
streamClosedUpdater = tmpStreamClosedUpdater;
|
||||||
|
outboundMessageCountUpdater = tmpOutboundMessageCountUpdater;
|
||||||
|
inboundMessageCountUpdater = tmpInboundMessageCountUpdater;
|
||||||
|
outboundWireSizeUpdater = tmpOutboundWireSizeUpdater;
|
||||||
|
inboundWireSizeUpdater = tmpInboundWireSizeUpdater;
|
||||||
|
outboundUncompressedSizeUpdater = tmpOutboundUncompressedSizeUpdater;
|
||||||
|
inboundUncompressedSizeUpdater = tmpInboundUncompressedSizeUpdater;
|
||||||
|
}
|
||||||
|
|
||||||
private final CensusStatsModule module;
|
private final CensusStatsModule module;
|
||||||
private final String fullMethodName;
|
private final String fullMethodName;
|
||||||
|
|
@ -358,33 +509,63 @@ public final class CensusStatsModule {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@SuppressWarnings("NonAtomicVolatileUpdate")
|
||||||
public void outboundWireSize(long bytes) {
|
public void outboundWireSize(long bytes) {
|
||||||
outboundWireSizeUpdater.getAndAdd(this, bytes);
|
if (outboundWireSizeUpdater != null) {
|
||||||
|
outboundWireSizeUpdater.getAndAdd(this, bytes);
|
||||||
|
} else {
|
||||||
|
outboundWireSize += bytes;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@SuppressWarnings("NonAtomicVolatileUpdate")
|
||||||
public void inboundWireSize(long bytes) {
|
public void inboundWireSize(long bytes) {
|
||||||
inboundWireSizeUpdater.getAndAdd(this, bytes);
|
if (inboundWireSizeUpdater != null) {
|
||||||
|
inboundWireSizeUpdater.getAndAdd(this, bytes);
|
||||||
|
} else {
|
||||||
|
inboundWireSize += bytes;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@SuppressWarnings("NonAtomicVolatileUpdate")
|
||||||
public void outboundUncompressedSize(long bytes) {
|
public void outboundUncompressedSize(long bytes) {
|
||||||
outboundUncompressedSizeUpdater.getAndAdd(this, bytes);
|
if (outboundUncompressedSizeUpdater != null) {
|
||||||
|
outboundUncompressedSizeUpdater.getAndAdd(this, bytes);
|
||||||
|
} else {
|
||||||
|
outboundUncompressedSize += bytes;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@SuppressWarnings("NonAtomicVolatileUpdate")
|
||||||
public void inboundUncompressedSize(long bytes) {
|
public void inboundUncompressedSize(long bytes) {
|
||||||
inboundUncompressedSizeUpdater.getAndAdd(this, bytes);
|
if (inboundUncompressedSizeUpdater != null) {
|
||||||
|
inboundUncompressedSizeUpdater.getAndAdd(this, bytes);
|
||||||
|
} else {
|
||||||
|
inboundUncompressedSize += bytes;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@SuppressWarnings("NonAtomicVolatileUpdate")
|
||||||
public void inboundMessage(int seqNo) {
|
public void inboundMessage(int seqNo) {
|
||||||
inboundMessageCountUpdater.getAndIncrement(this);
|
if (inboundMessageCountUpdater != null) {
|
||||||
|
inboundMessageCountUpdater.getAndIncrement(this);
|
||||||
|
} else {
|
||||||
|
inboundMessageCount++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@SuppressWarnings("NonAtomicVolatileUpdate")
|
||||||
public void outboundMessage(int seqNo) {
|
public void outboundMessage(int seqNo) {
|
||||||
outboundMessageCountUpdater.getAndIncrement(this);
|
if (outboundMessageCountUpdater != null) {
|
||||||
|
outboundMessageCountUpdater.getAndIncrement(this);
|
||||||
|
} else {
|
||||||
|
outboundMessageCount++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -395,8 +576,15 @@ public final class CensusStatsModule {
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void streamClosed(Status status) {
|
public void streamClosed(Status status) {
|
||||||
if (streamClosedUpdater.getAndSet(this, 1) != 0) {
|
if (streamClosedUpdater != null) {
|
||||||
return;
|
if (streamClosedUpdater.getAndSet(this, 1) != 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (streamClosed != 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
streamClosed = 1;
|
||||||
}
|
}
|
||||||
if (!recordFinishedRpcs) {
|
if (!recordFinishedRpcs) {
|
||||||
return;
|
return;
|
||||||
|
|
|
||||||
|
|
@ -58,10 +58,32 @@ import javax.annotation.Nullable;
|
||||||
*/
|
*/
|
||||||
final class CensusTracingModule {
|
final class CensusTracingModule {
|
||||||
private static final Logger logger = Logger.getLogger(CensusTracingModule.class.getName());
|
private static final Logger logger = Logger.getLogger(CensusTracingModule.class.getName());
|
||||||
private static final AtomicIntegerFieldUpdater<ClientCallTracer> callEndedUpdater =
|
|
||||||
AtomicIntegerFieldUpdater.newUpdater(ClientCallTracer.class, "callEnded");
|
@Nullable private static final AtomicIntegerFieldUpdater<ClientCallTracer> callEndedUpdater;
|
||||||
private static final AtomicIntegerFieldUpdater<ServerTracer> streamClosedUpdater =
|
|
||||||
AtomicIntegerFieldUpdater.newUpdater(ServerTracer.class, "streamClosed");
|
@Nullable private static final AtomicIntegerFieldUpdater<ServerTracer> streamClosedUpdater;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When using Atomic*FieldUpdater, some Samsung Android 5.0.x devices encounter a bug in their JDK
|
||||||
|
* reflection API that triggers a NoSuchFieldException. When this occurs, we fallback to
|
||||||
|
* (potentially racy) direct updates of the volatile variables.
|
||||||
|
*/
|
||||||
|
static {
|
||||||
|
AtomicIntegerFieldUpdater<ClientCallTracer> tmpCallEndedUpdater;
|
||||||
|
AtomicIntegerFieldUpdater<ServerTracer> tmpStreamClosedUpdater;
|
||||||
|
try {
|
||||||
|
tmpCallEndedUpdater =
|
||||||
|
AtomicIntegerFieldUpdater.newUpdater(ClientCallTracer.class, "callEnded");
|
||||||
|
tmpStreamClosedUpdater =
|
||||||
|
AtomicIntegerFieldUpdater.newUpdater(ServerTracer.class, "streamClosed");
|
||||||
|
} catch (Throwable t) {
|
||||||
|
logger.log(Level.SEVERE, "Creating atomic field updaters failed", t);
|
||||||
|
tmpCallEndedUpdater = null;
|
||||||
|
tmpStreamClosedUpdater = null;
|
||||||
|
}
|
||||||
|
callEndedUpdater = tmpCallEndedUpdater;
|
||||||
|
streamClosedUpdater = tmpStreamClosedUpdater;
|
||||||
|
}
|
||||||
|
|
||||||
private final Tracer censusTracer;
|
private final Tracer censusTracer;
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
|
|
@ -232,8 +254,15 @@ final class CensusTracingModule {
|
||||||
* is a no-op.
|
* is a no-op.
|
||||||
*/
|
*/
|
||||||
void callEnded(io.grpc.Status status) {
|
void callEnded(io.grpc.Status status) {
|
||||||
if (callEndedUpdater.getAndSet(this, 1) != 0) {
|
if (callEndedUpdater != null) {
|
||||||
return;
|
if (callEndedUpdater.getAndSet(this, 1) != 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (callEnded != 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
callEnded = 1;
|
||||||
}
|
}
|
||||||
span.end(createEndSpanOptions(status, isSampledToLocalTracing));
|
span.end(createEndSpanOptions(status, isSampledToLocalTracing));
|
||||||
}
|
}
|
||||||
|
|
@ -291,8 +320,15 @@ final class CensusTracingModule {
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void streamClosed(io.grpc.Status status) {
|
public void streamClosed(io.grpc.Status status) {
|
||||||
if (streamClosedUpdater.getAndSet(this, 1) != 0) {
|
if (streamClosedUpdater != null) {
|
||||||
return;
|
if (streamClosedUpdater.getAndSet(this, 1) != 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (streamClosed != 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
streamClosed = 1;
|
||||||
}
|
}
|
||||||
span.end(createEndSpanOptions(status, isSampledToLocalTracing));
|
span.end(createEndSpanOptions(status, isSampledToLocalTracing));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -37,8 +37,24 @@ public final class SerializingExecutor implements Executor, Runnable {
|
||||||
private static final Logger log =
|
private static final Logger log =
|
||||||
Logger.getLogger(SerializingExecutor.class.getName());
|
Logger.getLogger(SerializingExecutor.class.getName());
|
||||||
|
|
||||||
private static final AtomicIntegerFieldUpdater<SerializingExecutor> runStateUpdater =
|
// When using Atomic*FieldUpdater, some Samsung Android 5.0.x devices encounter a bug in their JDK
|
||||||
AtomicIntegerFieldUpdater.newUpdater(SerializingExecutor.class, "runState");
|
// reflection API that triggers a NoSuchFieldException. When this occurs, fallback to a
|
||||||
|
// synchronized implementation.
|
||||||
|
private static final AtomicHelper atomicHelper = getAtomicHelper();
|
||||||
|
|
||||||
|
private static AtomicHelper getAtomicHelper() {
|
||||||
|
AtomicHelper helper;
|
||||||
|
try {
|
||||||
|
helper =
|
||||||
|
new FieldUpdaterAtomicHelper(
|
||||||
|
AtomicIntegerFieldUpdater.newUpdater(SerializingExecutor.class, "runState"));
|
||||||
|
} catch (Throwable t) {
|
||||||
|
log.log(Level.SEVERE, "FieldUpdaterAtomicHelper failed", t);
|
||||||
|
helper = new SynchronizedAtomicHelper();
|
||||||
|
}
|
||||||
|
return helper;
|
||||||
|
}
|
||||||
|
|
||||||
private static final int STOPPED = 0;
|
private static final int STOPPED = 0;
|
||||||
private static final int RUNNING = -1;
|
private static final int RUNNING = -1;
|
||||||
|
|
||||||
|
|
@ -71,7 +87,7 @@ public final class SerializingExecutor implements Executor, Runnable {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void schedule(@Nullable Runnable removable) {
|
private void schedule(@Nullable Runnable removable) {
|
||||||
if (runStateUpdater.compareAndSet(this, STOPPED, RUNNING)) {
|
if (atomicHelper.runStateCompareAndSet(this, STOPPED, RUNNING)) {
|
||||||
boolean success = false;
|
boolean success = false;
|
||||||
try {
|
try {
|
||||||
executor.execute(this);
|
executor.execute(this);
|
||||||
|
|
@ -92,7 +108,7 @@ public final class SerializingExecutor implements Executor, Runnable {
|
||||||
// to execute don't succeed and accidentally run a previous runnable.
|
// to execute don't succeed and accidentally run a previous runnable.
|
||||||
runQueue.remove(removable);
|
runQueue.remove(removable);
|
||||||
}
|
}
|
||||||
runStateUpdater.set(this, STOPPED);
|
atomicHelper.runStateSet(this, STOPPED);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -111,11 +127,56 @@ public final class SerializingExecutor implements Executor, Runnable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
runStateUpdater.set(this, STOPPED);
|
atomicHelper.runStateSet(this, STOPPED);
|
||||||
}
|
}
|
||||||
if (!runQueue.isEmpty()) {
|
if (!runQueue.isEmpty()) {
|
||||||
// we didn't enqueue anything but someone else did.
|
// we didn't enqueue anything but someone else did.
|
||||||
schedule(null);
|
schedule(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private abstract static class AtomicHelper {
|
||||||
|
public abstract boolean runStateCompareAndSet(SerializingExecutor obj, int expect, int update);
|
||||||
|
|
||||||
|
public abstract void runStateSet(SerializingExecutor obj, int newValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class FieldUpdaterAtomicHelper extends AtomicHelper {
|
||||||
|
private final AtomicIntegerFieldUpdater<SerializingExecutor> runStateUpdater;
|
||||||
|
|
||||||
|
private FieldUpdaterAtomicHelper(
|
||||||
|
AtomicIntegerFieldUpdater<SerializingExecutor> runStateUpdater) {
|
||||||
|
this.runStateUpdater = runStateUpdater;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean runStateCompareAndSet(SerializingExecutor obj, int expect, int update) {
|
||||||
|
return runStateUpdater.compareAndSet(obj, expect, update);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void runStateSet(SerializingExecutor obj, int newValue) {
|
||||||
|
runStateUpdater.set(obj, newValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class SynchronizedAtomicHelper extends AtomicHelper {
|
||||||
|
@Override
|
||||||
|
public boolean runStateCompareAndSet(SerializingExecutor obj, int expect, int update) {
|
||||||
|
synchronized (obj) {
|
||||||
|
if (obj.runState == expect) {
|
||||||
|
obj.runState = update;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void runStateSet(SerializingExecutor obj, int newValue) {
|
||||||
|
synchronized (obj) {
|
||||||
|
obj.runState = newValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue