Reworked GrpcEnvironment initialization and shutdown to allow running all tests at once.
diff --git a/src/csharp/GrpcApiTests/MathClientServerTests.cs b/src/csharp/GrpcApiTests/MathClientServerTests.cs
index 9b51924..7654ec8 100644
--- a/src/csharp/GrpcApiTests/MathClientServerTests.cs
+++ b/src/csharp/GrpcApiTests/MathClientServerTests.cs
@@ -21,6 +21,8 @@
[TestFixtureSetUp]
public void Init()
{
+ GrpcEnvironment.Initialize();
+
server = new Server();
server.AddServiceDefinition(MathGrpc.BindService(new MathServiceImpl()));
int port = server.AddPort(host + ":0");
diff --git a/src/csharp/GrpcCore/Channel.cs b/src/csharp/GrpcCore/Channel.cs
index b0d8bee..36ebef7 100644
--- a/src/csharp/GrpcCore/Channel.cs
+++ b/src/csharp/GrpcCore/Channel.cs
@@ -8,13 +8,6 @@
{
public class Channel : IDisposable
{
- /// <summary>
- /// Make sure GPRC environment is initialized before any channels get used.
- /// </summary>
- static Channel() {
- GrpcEnvironment.EnsureInitialized();
- }
-
readonly ChannelSafeHandle handle;
readonly String target;
diff --git a/src/csharp/GrpcCore/GrpcEnvironment.cs b/src/csharp/GrpcCore/GrpcEnvironment.cs
index 7a7ff39..dcd3bc6 100644
--- a/src/csharp/GrpcCore/GrpcEnvironment.cs
+++ b/src/csharp/GrpcCore/GrpcEnvironment.cs
@@ -5,11 +5,9 @@
namespace Google.GRPC.Core
{
/// <summary>
- /// Encapsulates initialization and shutdown of GRPC C core library.
- /// You should not need to initialize it manually, as static constructors
- /// should load the library when needed.
+ /// Encapsulates initialization and shutdown of gRPC library.
/// </summary>
- public static class GrpcEnvironment
+ public class GrpcEnvironment
{
const int THREAD_POOL_SIZE = 1;
@@ -20,21 +18,24 @@
static extern void grpcsharp_shutdown();
static object staticLock = new object();
- static bool initCalled = false;
- static bool shutdownCalled = false;
-
- static GrpcThreadPool threadPool = new GrpcThreadPool(THREAD_POOL_SIZE);
+ static volatile GrpcEnvironment instance;
+
+ readonly GrpcThreadPool threadPool;
+ bool isClosed;
/// <summary>
- /// Makes sure GRPC environment is initialized.
+ /// Makes sure GRPC environment is initialized. Subsequent invocations don't have any
+ /// effect unless you call Shutdown first.
+ /// Although normal use cases assume you will call this just once in your application's
+ /// lifetime (and call Shutdown once you're done), for the sake of easier testing it's
+ /// allowed to initialize the environment again after it has been successfully shutdown.
/// </summary>
- public static void EnsureInitialized() {
+ public static void Initialize() {
lock(staticLock)
{
- if (!initCalled)
+ if (instance == null)
{
- initCalled = true;
- GrpcInit();
+ instance = new GrpcEnvironment();
}
}
}
@@ -47,45 +48,55 @@
{
lock(staticLock)
{
- if (initCalled && !shutdownCalled)
+ if (instance != null)
{
- shutdownCalled = true;
- GrpcShutdown();
+ instance.Close();
+ instance = null;
}
}
-
- }
-
- /// <summary>
- /// Initializes GRPC C Core library.
- /// </summary>
- private static void GrpcInit()
- {
- grpcsharp_init();
- threadPool.Start();
- // TODO: use proper logging here
- Console.WriteLine("GRPC initialized.");
- }
-
- /// <summary>
- /// Shutdown GRPC C Core library.
- /// </summary>
- private static void GrpcShutdown()
- {
- threadPool.Stop();
- grpcsharp_shutdown();
-
- // TODO: use proper logging here
- Console.WriteLine("GRPC shutdown.");
}
internal static GrpcThreadPool ThreadPool
{
get
{
- return threadPool;
+ var inst = instance;
+ if (inst == null)
+ {
+ throw new InvalidOperationException("GRPC environment not initialized");
+ }
+ return inst.threadPool;
}
}
+
+ /// <summary>
+ /// Creates gRPC environment.
+ /// </summary>
+ private GrpcEnvironment()
+ {
+ grpcsharp_init();
+ threadPool = new GrpcThreadPool(THREAD_POOL_SIZE);
+ threadPool.Start();
+ // TODO: use proper logging here
+ Console.WriteLine("GRPC initialized.");
+ }
+
+ /// <summary>
+ /// Shuts down this environment.
+ /// </summary>
+ private void Close()
+ {
+ if (isClosed)
+ {
+ throw new InvalidOperationException("Close has already been called");
+ }
+ threadPool.Stop();
+ grpcsharp_shutdown();
+ isClosed = true;
+
+ // TODO: use proper logging here
+ Console.WriteLine("GRPC shutdown.");
+ }
}
}
diff --git a/src/csharp/GrpcCore/Server.cs b/src/csharp/GrpcCore/Server.cs
index d3bc81e..3c4a73a 100644
--- a/src/csharp/GrpcCore/Server.cs
+++ b/src/csharp/GrpcCore/Server.cs
@@ -26,10 +26,6 @@
readonly TaskCompletionSource<object> shutdownTcs = new TaskCompletionSource<object>();
- static Server() {
- GrpcEnvironment.EnsureInitialized();
- }
-
public Server()
{
// TODO: what is the tag for server shutdown?
diff --git a/src/csharp/GrpcCore/Utils/RecordingQueue.cs b/src/csharp/GrpcCore/Utils/RecordingQueue.cs
index 0726f00..81e1a82 100644
--- a/src/csharp/GrpcCore/Utils/RecordingQueue.cs
+++ b/src/csharp/GrpcCore/Utils/RecordingQueue.cs
@@ -5,6 +5,7 @@
namespace Google.GRPC.Core.Utils
{
+ // TODO: replace this by something that implements IAsyncEnumerator.
/// <summary>
/// Observer that allows us to await incoming messages one-by-one.
/// The implementation is not ideal and class will be probably replaced
diff --git a/src/csharp/GrpcCoreTests/ClientServerTest.cs b/src/csharp/GrpcCoreTests/ClientServerTest.cs
index 513141f..3896686 100644
--- a/src/csharp/GrpcCoreTests/ClientServerTest.cs
+++ b/src/csharp/GrpcCoreTests/ClientServerTest.cs
@@ -21,6 +21,8 @@
[Test]
public void EmptyCall()
{
+ GrpcEnvironment.Initialize();
+
Server server = new Server();
server.AddServiceDefinition(
ServerServiceDefinition.CreateBuilder("someService")
diff --git a/src/csharp/GrpcCoreTests/GrpcEnvironmentTest.cs b/src/csharp/GrpcCoreTests/GrpcEnvironmentTest.cs
index 136878d..171c13f 100644
--- a/src/csharp/GrpcCoreTests/GrpcEnvironmentTest.cs
+++ b/src/csharp/GrpcCoreTests/GrpcEnvironmentTest.cs
@@ -9,10 +9,30 @@
{
[Test]
public void InitializeAndShutdownGrpcEnvironment() {
- GrpcEnvironment.EnsureInitialized();
- Thread.Sleep(500);
+ GrpcEnvironment.Initialize();
Assert.IsNotNull(GrpcEnvironment.ThreadPool.CompletionQueue);
GrpcEnvironment.Shutdown();
}
+
+ [Test]
+ public void SubsequentInvocations() {
+ GrpcEnvironment.Initialize();
+ GrpcEnvironment.Initialize();
+ GrpcEnvironment.Shutdown();
+ GrpcEnvironment.Shutdown();
+ }
+
+ [Test]
+ public void InitializeAfterShutdown() {
+ GrpcEnvironment.Initialize();
+ var tp1 = GrpcEnvironment.ThreadPool;
+ GrpcEnvironment.Shutdown();
+
+ GrpcEnvironment.Initialize();
+ var tp2 = GrpcEnvironment.ThreadPool;
+ GrpcEnvironment.Shutdown();
+
+ Assert.IsFalse(Object.ReferenceEquals(tp1, tp2));
+ }
}
}
diff --git a/src/csharp/GrpcCoreTests/ServerTest.cs b/src/csharp/GrpcCoreTests/ServerTest.cs
index b8ec250..a0de9f4 100644
--- a/src/csharp/GrpcCoreTests/ServerTest.cs
+++ b/src/csharp/GrpcCoreTests/ServerTest.cs
@@ -9,7 +9,9 @@
public class ServerTest
{
[Test]
- public void StartAndShutdownServer() {
+ public void StartAndShutdownServer()
+ {
+ GrpcEnvironment.Initialize();
Server server = new Server();
int port = server.AddPort("localhost:0");
diff --git a/src/csharp/InteropClient/Client.cs b/src/csharp/InteropClient/Client.cs
index 9f58150..9fd44f9 100644
--- a/src/csharp/InteropClient/Client.cs
+++ b/src/csharp/InteropClient/Client.cs
@@ -60,6 +60,8 @@
private void Run()
{
+ GrpcEnvironment.Initialize();
+
string addr = string.Format("{0}:{1}", options.serverHost, options.serverPort);
using (Channel channel = new Channel(addr))
{
diff --git a/src/csharp/MathClient/MathClient.cs b/src/csharp/MathClient/MathClient.cs
index 45222ab..52dd2a5 100644
--- a/src/csharp/MathClient/MathClient.cs
+++ b/src/csharp/MathClient/MathClient.cs
@@ -9,6 +9,8 @@
{
public static void Main (string[] args)
{
+ GrpcEnvironment.Initialize();
+
using (Channel channel = new Channel("127.0.0.1:23456"))
{
MathGrpc.IMathServiceClient stub = new MathGrpc.MathServiceClientStub(channel);