Browse Source

Refactor KeyedLock disposal logic and update tests

pull/24502/head
maliming 9 months ago
parent
commit
b06e8a9381
No known key found for this signature in database GPG Key ID: A646B9CB645ECEA4
  1. 37
      framework/src/Volo.Abp.Core/Volo/Abp/Threading/KeyedLock.cs
  2. 4
      framework/test/Volo.Abp.Core.Tests/Volo/Abp/Threading/KeyedLock_Tests.cs

37
framework/src/Volo.Abp.Core/Volo/Abp/Threading/KeyedLock.cs

@ -15,7 +15,7 @@ public static class KeyedLock
public static async Task<IDisposable> LockAsync(object key) public static async Task<IDisposable> LockAsync(object key)
{ {
return await LockAsync(key, CancellationToken.None).ConfigureAwait(false); return await LockAsync(key, CancellationToken.None);
} }
public static async Task<IDisposable> LockAsync(object key, CancellationToken cancellationToken) public static async Task<IDisposable> LockAsync(object key, CancellationToken cancellationToken)
@ -23,7 +23,7 @@ public static class KeyedLock
var semaphore = GetOrCreate(key); var semaphore = GetOrCreate(key);
try try
{ {
await semaphore.WaitAsync(cancellationToken).ConfigureAwait(false); await semaphore.WaitAsync(cancellationToken);
} }
catch (OperationCanceledException) catch (OperationCanceledException)
{ {
@ -36,7 +36,7 @@ public static class KeyedLock
public static async Task<IDisposable?> TryLockAsync(object key) public static async Task<IDisposable?> TryLockAsync(object key)
{ {
return await TryLockAsync(key, default, CancellationToken.None).ConfigureAwait(false); return await TryLockAsync(key, default, CancellationToken.None);
} }
public static async Task<IDisposable?> TryLockAsync(object key, TimeSpan timeout, CancellationToken cancellationToken = default) public static async Task<IDisposable?> TryLockAsync(object key, TimeSpan timeout, CancellationToken cancellationToken = default)
@ -47,11 +47,11 @@ public static class KeyedLock
{ {
if (timeout == default) if (timeout == default)
{ {
acquired = await semaphore.WaitAsync(0, cancellationToken).ConfigureAwait(false); acquired = await semaphore.WaitAsync(0, cancellationToken);
} }
else else
{ {
acquired = await semaphore.WaitAsync(timeout, cancellationToken).ConfigureAwait(false); acquired = await semaphore.WaitAsync(timeout, cancellationToken);
} }
} }
catch (OperationCanceledException) catch (OperationCanceledException)
@ -99,9 +99,17 @@ public static class KeyedLock
private sealed class Releaser(object key) : IDisposable private sealed class Releaser(object key) : IDisposable
{ {
private int _disposed;
public void Dispose() public void Dispose()
{ {
if (Interlocked.Exchange(ref _disposed, 1) == 1)
{
return;
}
RefCounted<SemaphoreSlim> item; RefCounted<SemaphoreSlim> item;
var shouldDispose = false;
lock (SemaphoreSlims) lock (SemaphoreSlims)
{ {
if (!SemaphoreSlims.TryGetValue(key, out item!)) if (!SemaphoreSlims.TryGetValue(key, out item!))
@ -109,19 +117,10 @@ public static class KeyedLock
return; return;
} }
--item.RefCount; --item.RefCount;
} if (item.RefCount == 0)
item.Value.Release();
bool shouldDispose = false;
lock (SemaphoreSlims)
{
if (SemaphoreSlims.TryGetValue(key, out var current) && ReferenceEquals(current, item))
{ {
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(); item.Value.Dispose();
} }
else
{
item.Value.Release();
}
} }
} }

4
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 // While holding the outer lock, inner waiter should not complete
await Task.Delay(150); await Task.Delay(200);
inner.IsCompleted.ShouldBeFalse(); inner.IsCompleted.ShouldBeFalse();
} }
// After releasing, inner should complete; elapsed >= hold time // After releasing, inner should complete; elapsed >= hold time
await inner; await inner;
sw.ElapsedMilliseconds.ShouldBeGreaterThanOrEqualTo(150); sw.ElapsedMilliseconds.ShouldBeGreaterThanOrEqualTo(180);
} }
[Fact] [Fact]

Loading…
Cancel
Save