Skip to content

Commit

Permalink
core: Don't mark calls as cancelled if they are successfully complete…
Browse files Browse the repository at this point in the history
…d. (#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 #5882
  • Loading branch information
temawi committed Aug 20, 2021
1 parent c54fcba commit e45aab0
Show file tree
Hide file tree
Showing 3 changed files with 15 additions and 3 deletions.
8 changes: 7 additions & 1 deletion core/src/main/java/io/grpc/internal/ServerCallImpl.java
Expand Up @@ -279,7 +279,11 @@ public ServerStreamListenerImpl(
new Context.CancellationListener() {
@Override
public void cancelled(Context context) {
ServerStreamListenerImpl.this.call.cancelled = true;
// If the context has a cancellation cause then something exceptional happened
// and we should also mark the call as cancelled.
if (context.cancellationCause() != null) {
ServerStreamListenerImpl.this.call.cancelled = true;
}
}
},
MoreExecutors.directExecutor());
Expand Down Expand Up @@ -355,6 +359,8 @@ private void closedInternal(Status status) {
} finally {
// Cancel context after delivering RPC closure notification to allow the application to
// clean up and update any state based on whether onComplete or onCancel was called.
// Note that in failure situations JumpToApplicationThreadServerStreamListener has already
// closed the context. In these situations this cancel() call will be a no-op.
context.cancel(null);
}
}
Expand Down
4 changes: 4 additions & 0 deletions core/src/test/java/io/grpc/internal/ServerCallImplTest.java
Expand Up @@ -18,6 +18,7 @@

import static com.google.common.base.Charsets.UTF_8;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
Expand Down Expand Up @@ -378,6 +379,9 @@ public void streamListener_closedOk() {
verify(callListener).onComplete();
assertTrue(context.isCancelled());
assertNull(context.cancellationCause());
// The call considers cancellation to be an exceptional situation so it should
// not be cancelled with an OK status.
assertFalse(call.isCancelled());
}

@Test
Expand Down
6 changes: 4 additions & 2 deletions core/src/test/java/io/grpc/internal/ServerImplTest.java
Expand Up @@ -1196,15 +1196,17 @@ public void testStreamClose_clientOkTriggersDelayedCancellation() throws Excepti
context, contextCancelled, null);

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

assertEquals(1, executor.runDueTasks());
assertTrue(callReference.get().isCancelled());
assertFalse(callReference.get().isCancelled());
assertTrue(context.get().isCancelled());
assertTrue(contextCancelled.get());
}
Expand Down

0 comments on commit e45aab0

Please sign in to comment.