wait for queued continuation to finish

pull/12176/head
Jan Tattermusch 7 years ago
parent 7a3bd5b7d6
commit 98ed73c389
  1. 28
      src/csharp/Grpc.Core/Internal/GrpcThreadPool.cs

@ -33,8 +33,7 @@ namespace Grpc.Core.Internal
internal class GrpcThreadPool internal class GrpcThreadPool
{ {
static readonly ILogger Logger = GrpcEnvironment.Logger.ForType<GrpcThreadPool>(); static readonly ILogger Logger = GrpcEnvironment.Logger.ForType<GrpcThreadPool>();
static readonly WaitCallback RunCompletionQueueEventCallbackSuccess = new WaitCallback((callback) => RunCompletionQueueEventCallback((OpCompletionDelegate) callback, true)); const int FinishContinuationsSleepMillis = 10;
static readonly WaitCallback RunCompletionQueueEventCallbackFailure = new WaitCallback((callback) => RunCompletionQueueEventCallback((OpCompletionDelegate) callback, false));
readonly GrpcEnvironment environment; readonly GrpcEnvironment environment;
readonly object myLock = new object(); readonly object myLock = new object();
@ -42,6 +41,9 @@ namespace Grpc.Core.Internal
readonly int poolSize; readonly int poolSize;
readonly int completionQueueCount; readonly int completionQueueCount;
readonly bool inlineHandlers; readonly bool inlineHandlers;
readonly WaitCallback runCompletionQueueEventCallbackSuccess;
readonly WaitCallback runCompletionQueueEventCallbackFailure;
readonly AtomicCounter queuedContinuationCounter = new AtomicCounter();
readonly List<BasicProfiler> threadProfilers = new List<BasicProfiler>(); // profilers assigned to threadpool threads readonly List<BasicProfiler> threadProfilers = new List<BasicProfiler>(); // profilers assigned to threadpool threads
@ -64,6 +66,9 @@ namespace Grpc.Core.Internal
this.inlineHandlers = inlineHandlers; this.inlineHandlers = inlineHandlers;
GrpcPreconditions.CheckArgument(poolSize >= completionQueueCount, GrpcPreconditions.CheckArgument(poolSize >= completionQueueCount,
"Thread pool size cannot be smaller than the number of completion queues used."); "Thread pool size cannot be smaller than the number of completion queues used.");
this.runCompletionQueueEventCallbackSuccess = new WaitCallback((callback) => RunCompletionQueueEventCallback((OpCompletionDelegate) callback, true));
this.runCompletionQueueEventCallbackFailure = new WaitCallback((callback) => RunCompletionQueueEventCallback((OpCompletionDelegate) callback, false));
} }
public void Start() public void Start()
@ -173,7 +178,8 @@ namespace Grpc.Core.Internal
// Use cached delegates to avoid unnecessary allocations // Use cached delegates to avoid unnecessary allocations
if (!inlineHandlers) if (!inlineHandlers)
{ {
ThreadPool.QueueUserWorkItem(success ? RunCompletionQueueEventCallbackSuccess : RunCompletionQueueEventCallbackFailure, callback); queuedContinuationCounter.Increment();
ThreadPool.QueueUserWorkItem(success ? runCompletionQueueEventCallbackSuccess : runCompletionQueueEventCallbackFailure, callback);
} }
else else
{ {
@ -187,6 +193,16 @@ namespace Grpc.Core.Internal
} }
} }
while (ev.type != CompletionQueueEvent.CompletionType.Shutdown); while (ev.type != CompletionQueueEvent.CompletionType.Shutdown);
// Continuations are running on default threadpool that consists of background threads.
// GrpcThreadPool thread (a foreground thread) will not exit unless all queued work had
// been finished to prevent terminating the continuations queued prematurely.
while (queuedContinuationCounter.Count != 0)
{
// Only happens on shutdown and having pending continuations shouldn't very common,
// so sleeping here for a little bit is fine.
Thread.Sleep(FinishContinuationsSleepMillis);
}
} }
private static IReadOnlyCollection<CompletionQueueSafeHandle> CreateCompletionQueueList(GrpcEnvironment environment, int completionQueueCount) private static IReadOnlyCollection<CompletionQueueSafeHandle> CreateCompletionQueueList(GrpcEnvironment environment, int completionQueueCount)
@ -200,7 +216,7 @@ namespace Grpc.Core.Internal
return list.AsReadOnly(); return list.AsReadOnly();
} }
private static void RunCompletionQueueEventCallback(OpCompletionDelegate callback, bool success) private void RunCompletionQueueEventCallback(OpCompletionDelegate callback, bool success)
{ {
try try
{ {
@ -210,6 +226,10 @@ namespace Grpc.Core.Internal
{ {
Logger.Error(e, "Exception occured while invoking completion delegate"); Logger.Error(e, "Exception occured while invoking completion delegate");
} }
finally
{
queuedContinuationCounter.Decrement();
}
} }
} }
} }

Loading…
Cancel
Save