Skip to content

Commit 0ab3ada

Browse files
authoredJul 26, 2023
Fix gRPC client retry deadlock (#2209)
1 parent bff852c commit 0ab3ada

File tree

2 files changed

+11
-5
lines changed

2 files changed

+11
-5
lines changed
 

‎src/Grpc.Net.Client/Internal/Retry/HedgingCall.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -365,10 +365,10 @@ private async Task HedgingDelayAsync(TimeSpan hedgingDelay)
365365

366366
protected override void Dispose(bool disposing)
367367
{
368+
base.Dispose(disposing);
369+
368370
lock (Lock)
369371
{
370-
base.Dispose(disposing);
371-
372372
CleanUpUnsynchronized();
373373
}
374374
}

‎src/Grpc.Net.Client/Internal/Retry/RetryCall.cs

+9-3
Original file line numberDiff line numberDiff line change
@@ -273,17 +273,23 @@ private static bool IsSuccessfulStreamingCall(Status responseStatus, GrpcCall<TR
273273

274274
protected override void OnCommitCall(IGrpcCall<TRequest, TResponse> call)
275275
{
276+
Debug.Assert(Monitor.IsEntered(Lock));
277+
276278
_activeCall = null;
277279
}
278280

279281
protected override void Dispose(bool disposing)
280282
{
283+
base.Dispose(disposing);
284+
285+
// Don't dispose the active call inside the retry lock.
286+
// Canceling the call could cause callbacks to run on other threads that want to aquire this lock, causing an app deadlock.
287+
GrpcCall<TRequest, TResponse>? activeCall = null;
281288
lock (Lock)
282289
{
283-
base.Dispose(disposing);
284-
285-
_activeCall?.Dispose();
290+
activeCall = _activeCall;
286291
}
292+
activeCall?.Dispose();
287293
}
288294

289295
protected override void StartCore(Action<GrpcCall<TRequest, TResponse>> startCallFunc)

0 commit comments

Comments
 (0)
Please sign in to comment.