Skip to content

Commit a57c14a

Browse files
authored
refactor: Stops exception allocation on channel shutdown
This fixes #11955. Stops exception allocation and its propagation on channel shutdown.
1 parent e80c197 commit a57c14a

File tree

16 files changed

+56
-66
lines changed

16 files changed

+56
-66
lines changed

Diff for: binder/src/main/java/io/grpc/binder/internal/PingTracker.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ private final class Ping {
9999
private synchronized void fail(Status status) {
100100
if (!done) {
101101
done = true;
102-
executor.execute(() -> callback.onFailure(status.asException()));
102+
executor.execute(() -> callback.onFailure(status));
103103
}
104104
}
105105

Diff for: binder/src/test/java/io/grpc/binder/internal/PingTrackerTest.java

+5-5
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ private static final class TestCallback implements ClientTransport.PingCallback
9696
private int numCallbacks;
9797
private boolean success;
9898
private boolean failure;
99-
private Throwable failureException;
99+
private Status failureStatus;
100100
private long roundtripTimeNanos;
101101

102102
@Override
@@ -107,10 +107,10 @@ public synchronized void onSuccess(long roundtripTimeNanos) {
107107
}
108108

109109
@Override
110-
public synchronized void onFailure(Throwable failureException) {
110+
public synchronized void onFailure(Status failureStatus) {
111111
numCallbacks += 1;
112112
failure = true;
113-
this.failureException = failureException;
113+
this.failureStatus = failureStatus;
114114
}
115115

116116
public void assertNotCalled() {
@@ -130,13 +130,13 @@ public void assertSuccess(long expectRoundTripTimeNanos) {
130130
public void assertFailure(Status status) {
131131
assertThat(numCallbacks).isEqualTo(1);
132132
assertThat(failure).isTrue();
133-
assertThat(((StatusException) failureException).getStatus()).isSameInstanceAs(status);
133+
assertThat(failureStatus).isSameInstanceAs(status);
134134
}
135135

136136
public void assertFailure(Status.Code statusCode) {
137137
assertThat(numCallbacks).isEqualTo(1);
138138
assertThat(failure).isTrue();
139-
assertThat(((StatusException) failureException).getStatus().getCode()).isEqualTo(statusCode);
139+
assertThat(failureStatus.getCode()).isEqualTo(statusCode);
140140
}
141141
}
142142
}

Diff for: core/src/main/java/io/grpc/internal/ClientTransport.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import io.grpc.InternalInstrumented;
2323
import io.grpc.Metadata;
2424
import io.grpc.MethodDescriptor;
25+
import io.grpc.Status;
2526
import java.util.concurrent.Executor;
2627
import javax.annotation.concurrent.ThreadSafe;
2728

@@ -90,6 +91,6 @@ interface PingCallback {
9091
*
9192
* @param cause the cause of the ping failure
9293
*/
93-
void onFailure(Throwable cause);
94+
void onFailure(Status cause);
9495
}
9596
}

Diff for: core/src/main/java/io/grpc/internal/FailingClientTransport.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ public ClientStream newStream(
5555
public void ping(final PingCallback callback, Executor executor) {
5656
executor.execute(new Runnable() {
5757
@Override public void run() {
58-
callback.onFailure(error.asException());
58+
callback.onFailure(error);
5959
}
6060
});
6161
}

Diff for: core/src/main/java/io/grpc/internal/Http2Ping.java

+5-4
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
import com.google.common.base.Stopwatch;
2020
import com.google.errorprone.annotations.concurrent.GuardedBy;
21+
import io.grpc.Status;
2122
import io.grpc.internal.ClientTransport.PingCallback;
2223
import java.util.LinkedHashMap;
2324
import java.util.Map;
@@ -62,7 +63,7 @@ public class Http2Ping {
6263
/**
6364
* If non-null, indicates the ping failed.
6465
*/
65-
@GuardedBy("this") private Throwable failureCause;
66+
@GuardedBy("this") private Status failureCause;
6667

6768
/**
6869
* The round-trip time for the ping, in nanoseconds. This value is only meaningful when
@@ -144,7 +145,7 @@ public boolean complete() {
144145
*
145146
* @param failureCause the cause of failure
146147
*/
147-
public void failed(Throwable failureCause) {
148+
public void failed(Status failureCause) {
148149
Map<ClientTransport.PingCallback, Executor> callbacks;
149150
synchronized (this) {
150151
if (completed) {
@@ -167,7 +168,7 @@ public void failed(Throwable failureCause) {
167168
* @param executor the executor used to invoke the callback
168169
* @param cause the cause of failure
169170
*/
170-
public static void notifyFailed(PingCallback callback, Executor executor, Throwable cause) {
171+
public static void notifyFailed(PingCallback callback, Executor executor, Status cause) {
171172
doExecute(executor, asRunnable(callback, cause));
172173
}
173174

@@ -203,7 +204,7 @@ public void run() {
203204
* failure.
204205
*/
205206
private static Runnable asRunnable(final ClientTransport.PingCallback callback,
206-
final Throwable failureCause) {
207+
final Status failureCause) {
207208
return new Runnable() {
208209
@Override
209210
public void run() {

Diff for: core/src/main/java/io/grpc/internal/KeepAliveManager.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,7 @@ public void ping() {
275275
public void onSuccess(long roundTripTimeNanos) {}
276276

277277
@Override
278-
public void onFailure(Throwable cause) {
278+
public void onFailure(Status cause) {
279279
transport.shutdownNow(Status.UNAVAILABLE.withDescription(
280280
"Keepalive failed. The connection is likely gone"));
281281
}

Diff for: core/src/test/java/io/grpc/internal/KeepAliveManagerTest.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ public void clientKeepAlivePinger_pingFailure() {
127127
verify(transport).ping(pingCallbackCaptor.capture(), isA(Executor.class));
128128
ClientTransport.PingCallback pingCallback = pingCallbackCaptor.getValue();
129129

130-
pingCallback.onFailure(new Throwable());
130+
pingCallback.onFailure(Status.UNAVAILABLE.withDescription("I must write descriptions"));
131131

132132
ArgumentCaptor<Status> statusCaptor = ArgumentCaptor.forClass(Status.class);
133133
verify(transport).shutdownNow(statusCaptor.capture());

Diff for: core/src/testFixtures/java/io/grpc/internal/AbstractTransportTest.java

+3-3
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ public void log(ChannelLogLevel level, String messageFormat, Object... args) {}
181181
protected ManagedClientTransport.Listener mockClientTransportListener
182182
= mock(ManagedClientTransport.Listener.class);
183183
protected MockServerListener serverListener = new MockServerListener();
184-
private ArgumentCaptor<Throwable> throwableCaptor = ArgumentCaptor.forClass(Throwable.class);
184+
private ArgumentCaptor<Status> statusCaptor = ArgumentCaptor.forClass(Status.class);
185185
protected final TestClientStreamTracer clientStreamTracer1 = new TestHeaderClientStreamTracer();
186186
private final TestClientStreamTracer clientStreamTracer2 = new TestHeaderClientStreamTracer();
187187
protected final ClientStreamTracer[] tracers = new ClientStreamTracer[] {
@@ -626,8 +626,8 @@ public void ping_afterTermination() throws Exception {
626626
// Transport doesn't support ping, so this neither passes nor fails.
627627
assumeTrue(false);
628628
}
629-
verify(mockPingCallback, timeout(TIMEOUT_MS)).onFailure(throwableCaptor.capture());
630-
Status status = Status.fromThrowable(throwableCaptor.getValue());
629+
verify(mockPingCallback, timeout(TIMEOUT_MS)).onFailure(statusCaptor.capture());
630+
Status status = statusCaptor.getValue();
631631
assertSame(shutdownReason, status);
632632
}
633633

Diff for: inprocess/src/main/java/io/grpc/inprocess/InProcessTransport.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,7 @@ public synchronized void ping(final PingCallback callback, Executor executor) {
246246
executor.execute(new Runnable() {
247247
@Override
248248
public void run() {
249-
callback.onFailure(shutdownStatus.asRuntimeException());
249+
callback.onFailure(shutdownStatus);
250250
}
251251
});
252252
} else {

Diff for: netty/src/main/java/io/grpc/netty/ClientTransportLifecycleManager.java

-5
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ final class ClientTransportLifecycleManager {
3030
/** null iff !transportShutdown. */
3131
private Status shutdownStatus;
3232
/** null iff !transportShutdown. */
33-
private Throwable shutdownThrowable;
3433
private boolean transportTerminated;
3534

3635
public ClientTransportLifecycleManager(ManagedClientTransport.Listener listener) {
@@ -72,7 +71,6 @@ public boolean notifyShutdown(Status s) {
7271
return false;
7372
}
7473
shutdownStatus = s;
75-
shutdownThrowable = s.asException();
7674
return true;
7775
}
7876

@@ -97,7 +95,4 @@ public Status getShutdownStatus() {
9795
return shutdownStatus;
9896
}
9997

100-
public Throwable getShutdownThrowable() {
101-
return shutdownThrowable;
102-
}
10398
}

Diff for: netty/src/main/java/io/grpc/netty/NettyClientHandler.java

+20-17
Original file line numberDiff line numberDiff line change
@@ -499,7 +499,7 @@ public void channelInactive(ChannelHandlerContext ctx) throws Exception {
499499
streamStatus = lifecycleManager.getShutdownStatus();
500500
}
501501
try {
502-
cancelPing(lifecycleManager.getShutdownThrowable());
502+
cancelPing(lifecycleManager.getShutdownStatus());
503503
// Report status to the application layer for any open streams
504504
connection().forEachActiveStream(new Http2StreamVisitor() {
505505
@Override
@@ -593,13 +593,14 @@ protected boolean isGracefulShutdownComplete() {
593593
*/
594594
private void createStream(CreateStreamCommand command, ChannelPromise promise)
595595
throws Exception {
596-
if (lifecycleManager.getShutdownThrowable() != null) {
596+
if (lifecycleManager.getShutdownStatus() != null) {
597597
command.stream().setNonExistent();
598598
// The connection is going away (it is really the GOAWAY case),
599599
// just terminate the stream now.
600600
command.stream().transportReportStatus(
601601
lifecycleManager.getShutdownStatus(), RpcProgress.MISCARRIED, true, new Metadata());
602-
promise.setFailure(lifecycleManager.getShutdownThrowable());
602+
promise.setFailure(InternalStatus.asRuntimeExceptionWithoutStacktrace(
603+
lifecycleManager.getShutdownStatus(), null));
603604
return;
604605
}
605606

@@ -852,19 +853,21 @@ private void sendPingFrameTraced(ChannelHandlerContext ctx, SendPingCommand msg,
852853
public void operationComplete(ChannelFuture future) throws Exception {
853854
if (future.isSuccess()) {
854855
transportTracer.reportKeepAliveSent();
855-
} else {
856-
Throwable cause = future.cause();
857-
if (cause instanceof ClosedChannelException) {
858-
cause = lifecycleManager.getShutdownThrowable();
859-
if (cause == null) {
860-
cause = Status.UNKNOWN.withDescription("Ping failed but for unknown reason.")
861-
.withCause(future.cause()).asException();
862-
}
863-
}
864-
finalPing.failed(cause);
865-
if (ping == finalPing) {
866-
ping = null;
856+
return;
857+
}
858+
Throwable cause = future.cause();
859+
Status status = lifecycleManager.getShutdownStatus();
860+
if (cause instanceof ClosedChannelException) {
861+
if (status == null) {
862+
status = Status.UNKNOWN.withDescription("Ping failed but for unknown reason.")
863+
.withCause(future.cause());
867864
}
865+
} else {
866+
status = Utils.statusFromThrowable(cause);
867+
}
868+
finalPing.failed(status);
869+
if (ping == finalPing) {
870+
ping = null;
868871
}
869872
}
870873
});
@@ -963,9 +966,9 @@ public boolean visit(Http2Stream stream) throws Http2Exception {
963966
}
964967
}
965968

966-
private void cancelPing(Throwable t) {
969+
private void cancelPing(Status s) {
967970
if (ping != null) {
968-
ping.failed(t);
971+
ping.failed(s);
969972
ping = null;
970973
}
971974
}

Diff for: netty/src/main/java/io/grpc/netty/NettyClientTransport.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ public void ping(final PingCallback callback, final Executor executor) {
165165
executor.execute(new Runnable() {
166166
@Override
167167
public void run() {
168-
callback.onFailure(statusExplainingWhyTheChannelIsNull.asException());
168+
callback.onFailure(statusExplainingWhyTheChannelIsNull);
169169
}
170170
});
171171
return;
@@ -177,7 +177,7 @@ public void run() {
177177
public void operationComplete(ChannelFuture future) throws Exception {
178178
if (!future.isSuccess()) {
179179
Status s = statusFromFailedFuture(future);
180-
Http2Ping.notifyFailed(callback, executor, s.asException());
180+
Http2Ping.notifyFailed(callback, executor, s);
181181
}
182182
}
183183
};

Diff for: netty/src/test/java/io/grpc/netty/NettyClientHandlerTest.java

+3-6
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,6 @@
5959
import io.grpc.CallOptions;
6060
import io.grpc.Metadata;
6161
import io.grpc.Status;
62-
import io.grpc.StatusException;
6362
import io.grpc.internal.AbstractStream;
6463
import io.grpc.internal.ClientStreamListener;
6564
import io.grpc.internal.ClientStreamListener.RpcProgress;
@@ -812,9 +811,7 @@ public void ping_failsWhenChannelCloses() throws Exception {
812811
handler().channelInactive(ctx());
813812
// ping failed on channel going inactive
814813
assertEquals(1, callback.invocationCount);
815-
assertTrue(callback.failureCause instanceof StatusException);
816-
assertEquals(Status.Code.UNAVAILABLE,
817-
((StatusException) callback.failureCause).getStatus().getCode());
814+
assertEquals(Status.Code.UNAVAILABLE, callback.failureCause.getCode());
818815
// A failed ping is still counted
819816
assertEquals(1, transportTracer.getStats().keepAlivesSent);
820817
}
@@ -1169,7 +1166,7 @@ private static CreateStreamCommand newCreateStreamCommand(
11691166
private static class PingCallbackImpl implements ClientTransport.PingCallback {
11701167
int invocationCount;
11711168
long roundTripTime;
1172-
Throwable failureCause;
1169+
Status failureCause;
11731170

11741171
@Override
11751172
public void onSuccess(long roundTripTimeNanos) {
@@ -1178,7 +1175,7 @@ public void onSuccess(long roundTripTimeNanos) {
11781175
}
11791176

11801177
@Override
1181-
public void onFailure(Throwable cause) {
1178+
public void onFailure(Status cause) {
11821179
invocationCount++;
11831180
this.failureCause = cause;
11841181
}

Diff for: netty/src/test/java/io/grpc/netty/NettyClientTransportTest.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -548,8 +548,8 @@ public void onSuccess(long roundTripTimeNanos) {
548548
}
549549

550550
@Override
551-
public void onFailure(Throwable cause) {
552-
pingResult.setException(cause);
551+
public void onFailure(Status cause) {
552+
pingResult.setException(cause.asException());
553553
}
554554
};
555555
transport.ping(pingCallback, clock.getScheduledExecutorService());

Diff for: okhttp/src/main/java/io/grpc/okhttp/OkHttpClientTransport.java

+3-3
Original file line numberDiff line numberDiff line change
@@ -1062,12 +1062,12 @@ private void setInUse(OkHttpClientStream stream) {
10621062
}
10631063
}
10641064

1065-
private Throwable getPingFailure() {
1065+
private Status getPingFailure() {
10661066
synchronized (lock) {
10671067
if (goAwayStatus != null) {
1068-
return goAwayStatus.asException();
1068+
return goAwayStatus;
10691069
} else {
1070-
return Status.UNAVAILABLE.withDescription("Connection closed").asException();
1070+
return Status.UNAVAILABLE.withDescription("Connection closed");
10711071
}
10721072
}
10731073
}

0 commit comments

Comments
 (0)