Skip to content

Commit e45aab0

Browse files
authored
core: Don't mark calls as cancelled if they are successfully completed. (grpc#8408)
The semantics around cancel vary slightly between ServerCall and CancellableContext - the context should always be cancelled regardless of the outcome of the call while the ServerCall should only be cancelled on a non-OK status. This fixes a bug where the ServerCall was always marked cancelled regardless of call status. Fixes grpc#5882
1 parent c54fcba commit e45aab0

3 files changed

Lines changed: 15 additions & 3 deletions

File tree

core/src/main/java/io/grpc/internal/ServerCallImpl.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -279,7 +279,11 @@ public ServerStreamListenerImpl(
279279
new Context.CancellationListener() {
280280
@Override
281281
public void cancelled(Context context) {
282-
ServerStreamListenerImpl.this.call.cancelled = true;
282+
// If the context has a cancellation cause then something exceptional happened
283+
// and we should also mark the call as cancelled.
284+
if (context.cancellationCause() != null) {
285+
ServerStreamListenerImpl.this.call.cancelled = true;
286+
}
283287
}
284288
},
285289
MoreExecutors.directExecutor());
@@ -355,6 +359,8 @@ private void closedInternal(Status status) {
355359
} finally {
356360
// Cancel context after delivering RPC closure notification to allow the application to
357361
// clean up and update any state based on whether onComplete or onCancel was called.
362+
// Note that in failure situations JumpToApplicationThreadServerStreamListener has already
363+
// closed the context. In these situations this cancel() call will be a no-op.
358364
context.cancel(null);
359365
}
360366
}

core/src/test/java/io/grpc/internal/ServerCallImplTest.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
import static com.google.common.base.Charsets.UTF_8;
2020
import static org.junit.Assert.assertEquals;
21+
import static org.junit.Assert.assertFalse;
2122
import static org.junit.Assert.assertNull;
2223
import static org.junit.Assert.assertTrue;
2324
import static org.junit.Assert.fail;
@@ -378,6 +379,9 @@ public void streamListener_closedOk() {
378379
verify(callListener).onComplete();
379380
assertTrue(context.isCancelled());
380381
assertNull(context.cancellationCause());
382+
// The call considers cancellation to be an exceptional situation so it should
383+
// not be cancelled with an OK status.
384+
assertFalse(call.isCancelled());
381385
}
382386

383387
@Test

core/src/test/java/io/grpc/internal/ServerImplTest.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1196,15 +1196,17 @@ public void testStreamClose_clientOkTriggersDelayedCancellation() throws Excepti
11961196
context, contextCancelled, null);
11971197

11981198
// For close status OK:
1199-
// isCancelled is expected to be true after all pending work is done
1199+
// The context isCancelled is expected to be true after all pending work is done,
1200+
// but for the call it should be false as it gets set cancelled only if the call
1201+
// fails with a non-OK status.
12001202
assertFalse(callReference.get().isCancelled());
12011203
assertFalse(context.get().isCancelled());
12021204
streamListener.closed(Status.OK);
12031205
assertFalse(callReference.get().isCancelled());
12041206
assertFalse(context.get().isCancelled());
12051207

12061208
assertEquals(1, executor.runDueTasks());
1207-
assertTrue(callReference.get().isCancelled());
1209+
assertFalse(callReference.get().isCancelled());
12081210
assertTrue(context.get().isCancelled());
12091211
assertTrue(contextCancelled.get());
12101212
}

0 commit comments

Comments
 (0)