make environment shutdown asynchronous
diff --git a/src/csharp/Grpc.Core.Tests/GrpcEnvironmentTest.cs b/src/csharp/Grpc.Core.Tests/GrpcEnvironmentTest.cs
index 6fe3827..3ec2cf4 100644
--- a/src/csharp/Grpc.Core.Tests/GrpcEnvironmentTest.cs
+++ b/src/csharp/Grpc.Core.Tests/GrpcEnvironmentTest.cs
@@ -49,7 +49,7 @@
{
Assert.IsNotNull(env.CompletionQueues.ElementAt(i));
}
- GrpcEnvironment.Release();
+ GrpcEnvironment.ReleaseAsync().Wait();
}
[Test]
@@ -58,8 +58,8 @@
var env1 = GrpcEnvironment.AddRef();
var env2 = GrpcEnvironment.AddRef();
Assert.AreSame(env1, env2);
- GrpcEnvironment.Release();
- GrpcEnvironment.Release();
+ GrpcEnvironment.ReleaseAsync().Wait();
+ GrpcEnvironment.ReleaseAsync().Wait();
}
[Test]
@@ -68,10 +68,10 @@
Assert.AreEqual(0, GrpcEnvironment.GetRefCount());
var env1 = GrpcEnvironment.AddRef();
- GrpcEnvironment.Release();
+ GrpcEnvironment.ReleaseAsync().Wait();
var env2 = GrpcEnvironment.AddRef();
- GrpcEnvironment.Release();
+ GrpcEnvironment.ReleaseAsync().Wait();
Assert.AreNotSame(env1, env2);
}
@@ -80,7 +80,7 @@
public void ReleaseWithoutAddRef()
{
Assert.AreEqual(0, GrpcEnvironment.GetRefCount());
- Assert.Throws(typeof(InvalidOperationException), () => GrpcEnvironment.Release());
+ Assert.ThrowsAsync(typeof(InvalidOperationException), async () => await GrpcEnvironment.ReleaseAsync());
}
[Test]
diff --git a/src/csharp/Grpc.Core.Tests/Internal/CompletionQueueSafeHandleTest.cs b/src/csharp/Grpc.Core.Tests/Internal/CompletionQueueSafeHandleTest.cs
index 195119f..e9ec59e 100644
--- a/src/csharp/Grpc.Core.Tests/Internal/CompletionQueueSafeHandleTest.cs
+++ b/src/csharp/Grpc.Core.Tests/Internal/CompletionQueueSafeHandleTest.cs
@@ -48,7 +48,7 @@
GrpcEnvironment.AddRef();
var cq = CompletionQueueSafeHandle.Create();
cq.Dispose();
- GrpcEnvironment.Release();
+ GrpcEnvironment.ReleaseAsync().Wait();
}
[Test]
@@ -59,7 +59,7 @@
cq.Shutdown();
var ev = cq.Next();
cq.Dispose();
- GrpcEnvironment.Release();
+ GrpcEnvironment.ReleaseAsync().Wait();
Assert.AreEqual(CompletionQueueEvent.CompletionType.Shutdown, ev.type);
Assert.AreNotEqual(IntPtr.Zero, ev.success);
Assert.AreEqual(IntPtr.Zero, ev.tag);
diff --git a/src/csharp/Grpc.Core.Tests/PInvokeTest.cs b/src/csharp/Grpc.Core.Tests/PInvokeTest.cs
index d2b2fc6..d3735c7 100644
--- a/src/csharp/Grpc.Core.Tests/PInvokeTest.cs
+++ b/src/csharp/Grpc.Core.Tests/PInvokeTest.cs
@@ -65,7 +65,7 @@
cq.Dispose();
});
- GrpcEnvironment.Release();
+ GrpcEnvironment.ReleaseAsync().Wait();
}
/// <summary>
diff --git a/src/csharp/Grpc.Core/Channel.cs b/src/csharp/Grpc.Core/Channel.cs
index 9cee752..b58a6a7 100644
--- a/src/csharp/Grpc.Core/Channel.cs
+++ b/src/csharp/Grpc.Core/Channel.cs
@@ -220,7 +220,7 @@
handle.Dispose();
- await Task.Run(() => GrpcEnvironment.Release()).ConfigureAwait(false);
+ await GrpcEnvironment.ReleaseAsync().ConfigureAwait(false);
}
internal ChannelSafeHandle Handle
diff --git a/src/csharp/Grpc.Core/GrpcEnvironment.cs b/src/csharp/Grpc.Core/GrpcEnvironment.cs
index 18af109..6e56b6e 100644
--- a/src/csharp/Grpc.Core/GrpcEnvironment.cs
+++ b/src/csharp/Grpc.Core/GrpcEnvironment.cs
@@ -35,6 +35,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
+using System.Threading.Tasks;
using Grpc.Core.Internal;
using Grpc.Core.Logging;
using Grpc.Core.Utils;
@@ -79,21 +80,26 @@
}
/// <summary>
- /// Decrements the reference count for currently active environment and shuts down the gRPC environment if reference count drops to zero.
- /// (and blocks until the environment has been fully shutdown).
+ /// Decrements the reference count for currently active environment and asynchronously shuts down the gRPC environment if reference count drops to zero.
/// </summary>
- internal static void Release()
+ internal static async Task ReleaseAsync()
{
+ GrpcEnvironment instanceToShutdown = null;
lock (staticLock)
{
GrpcPreconditions.CheckState(refCount > 0);
refCount--;
if (refCount == 0)
{
- instance.Close();
+ instanceToShutdown = instance;
instance = null;
}
}
+
+ if (instanceToShutdown != null)
+ {
+ await instanceToShutdown.ShutdownAsync();
+ }
}
internal static int GetRefCount()
@@ -223,13 +229,13 @@
/// <summary>
/// Shuts down this environment.
/// </summary>
- private void Close()
+ private async Task ShutdownAsync()
{
if (isClosed)
{
throw new InvalidOperationException("Close has already been called");
}
- threadPool.Stop();
+ await threadPool.StopAsync().ConfigureAwait(false);
GrpcNativeShutdown();
isClosed = true;
diff --git a/src/csharp/Grpc.Core/Internal/GrpcThreadPool.cs b/src/csharp/Grpc.Core/Internal/GrpcThreadPool.cs
index 4de543b..f50f2a6 100644
--- a/src/csharp/Grpc.Core/Internal/GrpcThreadPool.cs
+++ b/src/csharp/Grpc.Core/Internal/GrpcThreadPool.cs
@@ -35,6 +35,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading;
+using System.Threading.Tasks;
using Grpc.Core.Logging;
using Grpc.Core.Utils;
@@ -53,6 +54,8 @@
readonly int poolSize;
readonly int completionQueueCount;
+ bool stopRequested;
+
IReadOnlyCollection<CompletionQueueSafeHandle> completionQueues;
/// <summary>
@@ -84,15 +87,21 @@
}
}
- public void Stop()
+ public Task StopAsync()
{
lock (myLock)
{
+ GrpcPreconditions.CheckState(!stopRequested, "Stop already requested.");
+ stopRequested = true;
+
foreach (var cq in completionQueues)
{
cq.Shutdown();
}
+ }
+ return Task.Run(() =>
+ {
foreach (var thread in threads)
{
thread.Join();
@@ -102,7 +111,7 @@
{
cq.Dispose();
}
- }
+ });
}
internal IReadOnlyCollection<CompletionQueueSafeHandle> CompletionQueues
diff --git a/src/csharp/Grpc.Core/Server.cs b/src/csharp/Grpc.Core/Server.cs
index 6bd7900..18a808e 100644
--- a/src/csharp/Grpc.Core/Server.cs
+++ b/src/csharp/Grpc.Core/Server.cs
@@ -169,7 +169,7 @@
await shutdownTcs.Task.ConfigureAwait(false);
DisposeHandle();
- await Task.Run(() => GrpcEnvironment.Release()).ConfigureAwait(false);
+ await GrpcEnvironment.ReleaseAsync().ConfigureAwait(false);
}
/// <summary>
@@ -194,7 +194,7 @@
await shutdownTcs.Task.ConfigureAwait(false);
DisposeHandle();
- await Task.Run(() => GrpcEnvironment.Release()).ConfigureAwait(false);
+ await GrpcEnvironment.ReleaseAsync().ConfigureAwait(false);
}
internal void AddCallReference(object call)