Skip to content

Commit 81ad06f

Browse files
committed
Update
1 parent 427a5bd commit 81ad06f

File tree

1 file changed

+9
-1
lines changed

1 file changed

+9
-1
lines changed

src/Grpc.Net.Client/Balancer/Internal/ConnectionManager.cs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ internal sealed class ConnectionManager : IDisposable, IChannelControlHelper
3131
private static readonly ChannelIdProvider _channelIdProvider = new ChannelIdProvider();
3232

3333
private readonly object _lock;
34+
private readonly object _subChannelStateChangedLock;
3435
internal readonly Resolver _resolver;
3536
private readonly ISubchannelTransportFactory _subchannelTransportFactory;
3637
private readonly List<Subchannel> _subchannels;
@@ -57,6 +58,7 @@ internal ConnectionManager(
5758
LoadBalancerFactory[] loadBalancerFactories)
5859
{
5960
_lock = new object();
61+
_subChannelStateChangedLock = new object();
6062
_nextPickerTcs = new TaskCompletionSource<SubchannelPicker>(TaskCreationOptions.RunContinuationsAsynchronously);
6163
_resolverStartedTcs = new TaskCompletionSource<object?>(TaskCreationOptions.RunContinuationsAsynchronously);
6264
_channelId = _channelIdProvider.GetNextChannelId();
@@ -204,7 +206,13 @@ internal void OnSubchannelStateChange(Subchannel subchannel, ConnectivityState s
204206
}
205207
}
206208

207-
subchannel.RaiseStateChanged(state, status);
209+
// Lock to avoid parallel state change events.
210+
// This is here so consumers of the state changed API don't need to worry about synchronization.
211+
// Use a new lock to avoid deadlocks. See https://github.com/grpc/grpc-dotnet/issues/2589.
212+
lock (_subChannelStateChangedLock)
213+
{
214+
subchannel.RaiseStateChanged(state, status);
215+
}
208216
}
209217

210218
public async Task ConnectAsync(bool waitForReady, CancellationToken cancellationToken)

0 commit comments

Comments
 (0)