Reworked GrpcEnvironment initialization and shutdown to allow running all tests at once.

pull/516/head
Jan Tattermusch 10 years ago
parent 07fadea2ff
commit 23821ceb69
  1. 2
      src/csharp/GrpcApiTests/MathClientServerTests.cs
  2. 7
      src/csharp/GrpcCore/Channel.cs
  3. 67
      src/csharp/GrpcCore/GrpcEnvironment.cs
  4. 4
      src/csharp/GrpcCore/Server.cs
  5. 1
      src/csharp/GrpcCore/Utils/RecordingQueue.cs
  6. 2
      src/csharp/GrpcCoreTests/ClientServerTest.cs
  7. 24
      src/csharp/GrpcCoreTests/GrpcEnvironmentTest.cs
  8. 4
      src/csharp/GrpcCoreTests/ServerTest.cs
  9. 2
      src/csharp/InteropClient/Client.cs
  10. 2
      src/csharp/MathClient/MathClient.cs

@ -21,6 +21,8 @@ namespace math.Tests
[TestFixtureSetUp] [TestFixtureSetUp]
public void Init() public void Init()
{ {
GrpcEnvironment.Initialize();
server = new Server(); server = new Server();
server.AddServiceDefinition(MathGrpc.BindService(new MathServiceImpl())); server.AddServiceDefinition(MathGrpc.BindService(new MathServiceImpl()));
int port = server.AddPort(host + ":0"); int port = server.AddPort(host + ":0");

@ -8,13 +8,6 @@ namespace Google.GRPC.Core
{ {
public class Channel : IDisposable 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 ChannelSafeHandle handle;
readonly String target; readonly String target;

@ -5,11 +5,9 @@ using System.Runtime.InteropServices;
namespace Google.GRPC.Core namespace Google.GRPC.Core
{ {
/// <summary> /// <summary>
/// Encapsulates initialization and shutdown of GRPC C core library. /// Encapsulates initialization and shutdown of gRPC library.
/// You should not need to initialize it manually, as static constructors
/// should load the library when needed.
/// </summary> /// </summary>
public static class GrpcEnvironment public class GrpcEnvironment
{ {
const int THREAD_POOL_SIZE = 1; const int THREAD_POOL_SIZE = 1;
@ -20,21 +18,24 @@ namespace Google.GRPC.Core
static extern void grpcsharp_shutdown(); static extern void grpcsharp_shutdown();
static object staticLock = new object(); static object staticLock = new object();
static bool initCalled = false; static volatile GrpcEnvironment instance;
static bool shutdownCalled = false;
readonly GrpcThreadPool threadPool;
static GrpcThreadPool threadPool = new GrpcThreadPool(THREAD_POOL_SIZE); bool isClosed;
/// <summary> /// <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> /// </summary>
public static void EnsureInitialized() { public static void Initialize() {
lock(staticLock) lock(staticLock)
{ {
if (!initCalled) if (instance == null)
{ {
initCalled = true; instance = new GrpcEnvironment();
GrpcInit();
} }
} }
} }
@ -47,45 +48,55 @@ namespace Google.GRPC.Core
{ {
lock(staticLock) lock(staticLock)
{ {
if (initCalled && !shutdownCalled) if (instance != null)
{ {
shutdownCalled = true; instance.Close();
GrpcShutdown(); instance = null;
} }
} }
}
internal static GrpcThreadPool ThreadPool
{
get
{
var inst = instance;
if (inst == null)
{
throw new InvalidOperationException("GRPC environment not initialized");
}
return inst.threadPool;
}
} }
/// <summary> /// <summary>
/// Initializes GRPC C Core library. /// Creates gRPC environment.
/// </summary> /// </summary>
private static void GrpcInit() private GrpcEnvironment()
{ {
grpcsharp_init(); grpcsharp_init();
threadPool = new GrpcThreadPool(THREAD_POOL_SIZE);
threadPool.Start(); threadPool.Start();
// TODO: use proper logging here // TODO: use proper logging here
Console.WriteLine("GRPC initialized."); Console.WriteLine("GRPC initialized.");
} }
/// <summary> /// <summary>
/// Shutdown GRPC C Core library. /// Shuts down this environment.
/// </summary> /// </summary>
private static void GrpcShutdown() private void Close()
{ {
if (isClosed)
{
throw new InvalidOperationException("Close has already been called");
}
threadPool.Stop(); threadPool.Stop();
grpcsharp_shutdown(); grpcsharp_shutdown();
isClosed = true;
// TODO: use proper logging here // TODO: use proper logging here
Console.WriteLine("GRPC shutdown."); Console.WriteLine("GRPC shutdown.");
} }
internal static GrpcThreadPool ThreadPool
{
get
{
return threadPool;
}
}
} }
} }

@ -26,10 +26,6 @@ namespace Google.GRPC.Core
readonly TaskCompletionSource<object> shutdownTcs = new TaskCompletionSource<object>(); readonly TaskCompletionSource<object> shutdownTcs = new TaskCompletionSource<object>();
static Server() {
GrpcEnvironment.EnsureInitialized();
}
public Server() public Server()
{ {
// TODO: what is the tag for server shutdown? // TODO: what is the tag for server shutdown?

@ -5,6 +5,7 @@ using System.Collections.Concurrent;
namespace Google.GRPC.Core.Utils namespace Google.GRPC.Core.Utils
{ {
// TODO: replace this by something that implements IAsyncEnumerator.
/// <summary> /// <summary>
/// Observer that allows us to await incoming messages one-by-one. /// Observer that allows us to await incoming messages one-by-one.
/// The implementation is not ideal and class will be probably replaced /// The implementation is not ideal and class will be probably replaced

@ -21,6 +21,8 @@ namespace Google.GRPC.Core.Tests
[Test] [Test]
public void EmptyCall() public void EmptyCall()
{ {
GrpcEnvironment.Initialize();
Server server = new Server(); Server server = new Server();
server.AddServiceDefinition( server.AddServiceDefinition(
ServerServiceDefinition.CreateBuilder("someService") ServerServiceDefinition.CreateBuilder("someService")

@ -9,10 +9,30 @@ namespace Google.GRPC.Core.Tests
{ {
[Test] [Test]
public void InitializeAndShutdownGrpcEnvironment() { public void InitializeAndShutdownGrpcEnvironment() {
GrpcEnvironment.EnsureInitialized(); GrpcEnvironment.Initialize();
Thread.Sleep(500);
Assert.IsNotNull(GrpcEnvironment.ThreadPool.CompletionQueue); Assert.IsNotNull(GrpcEnvironment.ThreadPool.CompletionQueue);
GrpcEnvironment.Shutdown(); 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));
}
} }
} }

@ -9,7 +9,9 @@ namespace Google.GRPC.Core.Tests
public class ServerTest public class ServerTest
{ {
[Test] [Test]
public void StartAndShutdownServer() { public void StartAndShutdownServer()
{
GrpcEnvironment.Initialize();
Server server = new Server(); Server server = new Server();
int port = server.AddPort("localhost:0"); int port = server.AddPort("localhost:0");

@ -60,6 +60,8 @@ namespace Google.GRPC.Interop
private void Run() private void Run()
{ {
GrpcEnvironment.Initialize();
string addr = string.Format("{0}:{1}", options.serverHost, options.serverPort); string addr = string.Format("{0}:{1}", options.serverHost, options.serverPort);
using (Channel channel = new Channel(addr)) using (Channel channel = new Channel(addr))
{ {

@ -9,6 +9,8 @@ namespace math
{ {
public static void Main (string[] args) public static void Main (string[] args)
{ {
GrpcEnvironment.Initialize();
using (Channel channel = new Channel("127.0.0.1:23456")) using (Channel channel = new Channel("127.0.0.1:23456"))
{ {
MathGrpc.IMathServiceClient stub = new MathGrpc.MathServiceClientStub(channel); MathGrpc.IMathServiceClient stub = new MathGrpc.MathServiceClientStub(channel);

Loading…
Cancel
Save