diff --git a/WelsonJS.Toolkit/WelsonJS.Launcher/ConnectionManagerBase.cs b/WelsonJS.Toolkit/WelsonJS.Launcher/ConnectionManagerBase.cs index d2129f5..5723bd2 100644 --- a/WelsonJS.Toolkit/WelsonJS.Launcher/ConnectionManagerBase.cs +++ b/WelsonJS.Toolkit/WelsonJS.Launcher/ConnectionManagerBase.cs @@ -24,6 +24,8 @@ namespace WelsonJS.Launcher = new ConcurrentDictionary(); private readonly ConcurrentDictionary _openLocks = new ConcurrentDictionary(); + private readonly ConcurrentDictionary _opLocks + = new ConcurrentDictionary(); /// /// Creates a unique cache key for the given connection parameters. @@ -155,14 +157,16 @@ namespace WelsonJS.Launcher if (maxAttempts < 1) throw new ArgumentOutOfRangeException(nameof(maxAttempts)); Exception lastError = null; + var key = CreateKey(parameters); + var opLock = _opLocks.GetOrAdd(key, _ => new SemaphoreSlim(1, 1)); for (int attempt = 0; attempt < maxAttempts; attempt++) { - token.ThrowIfCancellationRequested(); - var connection = await GetOrCreateAsync(parameters, token).ConfigureAwait(false); - + await opLock.WaitAsync(token).ConfigureAwait(false); try { + token.ThrowIfCancellationRequested(); + var connection = await GetOrCreateAsync(parameters, token).ConfigureAwait(false); return await operation(connection, token).ConfigureAwait(false); } catch (OperationCanceledException) @@ -178,6 +182,10 @@ namespace WelsonJS.Launcher throw; } } + finally + { + opLock.Release(); + } } throw lastError ?? new InvalidOperationException("Unreachable state in ExecuteWithRetryAsync");