diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/Threading/KeyedLock.cs b/framework/src/Volo.Abp.Core/Volo/Abp/Threading/KeyedLock.cs index 80e750ac33..b7871cb0e7 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/Threading/KeyedLock.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/Threading/KeyedLock.cs @@ -15,7 +15,7 @@ public static class KeyedLock public static async Task LockAsync(object key) { - return await LockAsync(key, CancellationToken.None).ConfigureAwait(false); + return await LockAsync(key, CancellationToken.None); } public static async Task LockAsync(object key, CancellationToken cancellationToken) @@ -23,7 +23,7 @@ public static class KeyedLock var semaphore = GetOrCreate(key); try { - await semaphore.WaitAsync(cancellationToken).ConfigureAwait(false); + await semaphore.WaitAsync(cancellationToken); } catch (OperationCanceledException) { @@ -36,7 +36,7 @@ public static class KeyedLock public static async Task TryLockAsync(object key) { - return await TryLockAsync(key, default, CancellationToken.None).ConfigureAwait(false); + return await TryLockAsync(key, default, CancellationToken.None); } public static async Task TryLockAsync(object key, TimeSpan timeout, CancellationToken cancellationToken = default) @@ -47,11 +47,11 @@ public static class KeyedLock { if (timeout == default) { - acquired = await semaphore.WaitAsync(0, cancellationToken).ConfigureAwait(false); + acquired = await semaphore.WaitAsync(0, cancellationToken); } else { - acquired = await semaphore.WaitAsync(timeout, cancellationToken).ConfigureAwait(false); + acquired = await semaphore.WaitAsync(timeout, cancellationToken); } } catch (OperationCanceledException) @@ -99,9 +99,17 @@ public static class KeyedLock private sealed class Releaser(object key) : IDisposable { + private int _disposed; + public void Dispose() { + if (Interlocked.Exchange(ref _disposed, 1) == 1) + { + return; + } + RefCounted item; + var shouldDispose = false; lock (SemaphoreSlims) { if (!SemaphoreSlims.TryGetValue(key, out item!)) @@ -109,19 +117,10 @@ public static class KeyedLock return; } --item.RefCount; - } - item.Value.Release(); - - bool shouldDispose = false; - lock (SemaphoreSlims) - { - if (SemaphoreSlims.TryGetValue(key, out var current) && ReferenceEquals(current, item)) + if (item.RefCount == 0) { - if (item.RefCount == 0) - { - SemaphoreSlims.Remove(key); - shouldDispose = true; - } + SemaphoreSlims.Remove(key); + shouldDispose = true; } } @@ -129,6 +128,10 @@ public static class KeyedLock { item.Value.Dispose(); } + else + { + item.Value.Release(); + } } } diff --git a/framework/test/Volo.Abp.Core.Tests/Volo/Abp/Threading/KeyedLock_Tests.cs b/framework/test/Volo.Abp.Core.Tests/Volo/Abp/Threading/KeyedLock_Tests.cs index 633ef3dd76..1477bd7dd5 100644 --- a/framework/test/Volo.Abp.Core.Tests/Volo/Abp/Threading/KeyedLock_Tests.cs +++ b/framework/test/Volo.Abp.Core.Tests/Volo/Abp/Threading/KeyedLock_Tests.cs @@ -56,13 +56,13 @@ public class KeyedLock_Tests }); // While holding the outer lock, inner waiter should not complete - await Task.Delay(150); + await Task.Delay(200); inner.IsCompleted.ShouldBeFalse(); } // After releasing, inner should complete; elapsed >= hold time await inner; - sw.ElapsedMilliseconds.ShouldBeGreaterThanOrEqualTo(150); + sw.ElapsedMilliseconds.ShouldBeGreaterThanOrEqualTo(180); } [Fact]