Skip to content

Commit

Permalink
add a repro and fix for grpc#19090
Browse files Browse the repository at this point in the history
  • Loading branch information
jtattermusch committed Mar 30, 2022
1 parent 3c558dd commit 34d131e
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 1 deletion.
48 changes: 48 additions & 0 deletions src/csharp/Grpc.Core.Tests/CallAfterShutdownTest.cs
@@ -0,0 +1,48 @@
#region Copyright notice and license

// Copyright 2020 The gRPC Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#endregion

using System;
using NUnit.Framework;

namespace Grpc.Core.Tests
{
public class CallAfterShutdownTest
{
Method<string, string> dummyUnaryMethod = new Method<string, string>(MethodType.Unary, "fooservice", "dummyMethod", Marshallers.StringMarshaller, Marshallers.StringMarshaller);

[Test]
public void StartBlockingUnaryCallAfterChannelShutdown()
{
// create a channel and immediately shut it down.
var channel = new Channel("127.0.0.1", 1000, ChannelCredentials.Insecure);
channel.ShutdownAsync().Wait(); // also shuts down GrpcEnvironment

Assert.Throws(typeof(ObjectDisposedException), () => Calls.BlockingUnaryCall(new CallInvocationDetails<string, string>(channel, dummyUnaryMethod, new CallOptions()), "THE REQUEST"));
}

[Test]
public void StartAsyncUnaryCallAfterChannelShutdown()
{
// create a channel and immediately shut it down.
var channel = new Channel("127.0.0.1", 1000, ChannelCredentials.Insecure);
channel.ShutdownAsync().Wait(); // also shuts down GrpcEnvironment

Assert.Throws(typeof(ObjectDisposedException), () => Calls.AsyncUnaryCall(new CallInvocationDetails<string, string>(channel, dummyUnaryMethod, new CallOptions()), "THE REQUEST"));
}
}
}
7 changes: 6 additions & 1 deletion src/csharp/Grpc.Core/Internal/AsyncCall.cs
Expand Up @@ -78,7 +78,12 @@ public TResponse UnaryCall(TRequest msg)
var profiler = Profilers.ForCurrentThread();

using (profiler.NewScope("AsyncCall.UnaryCall"))
using (CompletionQueueSafeHandle cq = CompletionQueueSafeHandle.CreateSync())

// Create a pluckable completion queue for the call. Avoid creating a completion queue when we know the channel has already
// been shutdown. In such case, the call will fail with ObjectDisposedException immediately anyway and creating / destroying
// a completion queue would lead to crash if this was the last channel in the application (and thus GrpcEnvironment has been shutdown).
// See https://github.com/grpc/grpc/issues/19090
using (CompletionQueueSafeHandle cq = details.Channel.Handle.IsClosed ? null : CompletionQueueSafeHandle.CreateSync())
{
bool callStartedOk = false;
try
Expand Down
1 change: 1 addition & 0 deletions src/csharp/tests.json
Expand Up @@ -22,6 +22,7 @@
"Grpc.Core.Tests.AppDomainUnloadTest",
"Grpc.Core.Tests.AuthContextTest",
"Grpc.Core.Tests.AuthPropertyTest",
"Grpc.Core.Tests.CallAfterShutdownTest",
"Grpc.Core.Tests.CallCancellationTest",
"Grpc.Core.Tests.CallCredentialsTest",
"Grpc.Core.Tests.CallOptionsTest",
Expand Down

0 comments on commit 34d131e

Please sign in to comment.