Skip to content

Commit

Permalink
Performance enhancement to MemoryLock; now using AsyncKeyedLock libra…
Browse files Browse the repository at this point in the history
…ry with pooling. (#531)

* Performance enhancement to MemoryLock; now using AsyncKeyedLock library with pooling.

* Removed leftover line
  • Loading branch information
MarkCiliaVincenti authored Apr 25, 2024
1 parent 4e636f0 commit 784a50f
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 21 deletions.
55 changes: 34 additions & 21 deletions src/EasyCaching.Core/DistributedLock/MemoryLock.cs
Original file line number Diff line number Diff line change
@@ -1,74 +1,84 @@
using System;
using AsyncKeyedLock;
using System;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;

namespace EasyCaching.Core.DistributedLock
{
public class MemoryLock : IDistributedLock
{
private static readonly RefCounterPool<string, SemaphoreSlim> SemaphoreSlims
= new RefCounterPool<string, SemaphoreSlim>();
private static readonly AsyncKeyedLocker<string> _locker = new AsyncKeyedLocker<string>(o =>
{
o.PoolSize = 20;
o.PoolInitialFill = 1;
});

public string Key { get; }

private readonly object _syncObj = new object();

public MemoryLock(string key) => Key = key;

private SemaphoreSlim _semaphore;
private AsyncKeyedLockReleaser<string> _releaser;

private SemaphoreSlim GetOrCreate()
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private AsyncKeyedLockReleaser<string> GetOrCreate()
{
if (Volatile.Read(ref _semaphore) != null) throw new DistributedLockException();
if (Volatile.Read(ref _releaser) != null) throw new DistributedLockException();

lock (_syncObj)
{
if (Volatile.Read(ref _semaphore) != null) throw new DistributedLockException();
if (Volatile.Read(ref _releaser) != null) throw new DistributedLockException();

var semaphore = SemaphoreSlims.GetOrAdd(Key, _ => new SemaphoreSlim(1, 1));
var releaser = _locker.GetOrAdd(Key);

Volatile.Write(ref _semaphore, semaphore);
Volatile.Write(ref _releaser, releaser);

return semaphore;
return releaser;
}
}

#region Dispose

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Dispose()
{
Dispose(true);

GC.SuppressFinalize(this);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public virtual async ValueTask DisposeAsync()
{
await ReleaseAsync();

GC.SuppressFinalize(this);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected virtual void Dispose(bool disposing) => Release();

[MethodImpl(MethodImplOptions.AggressiveInlining)]
~MemoryLock() => Dispose(false);

#endregion Dispose

#region Release
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void InternalRelease()
{
var semaphore = Interlocked.Exchange(ref _semaphore, null);
var semaphore = Interlocked.Exchange(ref _releaser, null);

if (semaphore == null) return;

semaphore.Release();

SemaphoreSlims.TryRemove(Key)?.Dispose();
semaphore.Dispose();
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public virtual void Release() => InternalRelease();

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public virtual ValueTask ReleaseAsync()
{
InternalRelease();
Expand All @@ -77,23 +87,25 @@ public virtual ValueTask ReleaseAsync()
}
#endregion

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void LockFail()
{
var semaphore = Interlocked.Exchange(ref _semaphore, null);
var semaphore = Interlocked.Exchange(ref _releaser, null);

if (semaphore == null) return;

SemaphoreSlims.TryRemove(Key)?.Dispose();
new AsyncKeyedLockTimeoutReleaser<string>(false, semaphore).Dispose();
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public virtual bool Lock(int millisecondsTimeout, CancellationToken cancellationToken)
{
var semaphore = GetOrCreate();

var locked = false;
bool locked = false;
try
{
locked = semaphore.Wait(millisecondsTimeout, cancellationToken);
locked = semaphore.SemaphoreSlim.Wait(millisecondsTimeout, cancellationToken);
}
finally
{
Expand All @@ -103,14 +115,15 @@ public virtual bool Lock(int millisecondsTimeout, CancellationToken cancellation
return locked;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public virtual async ValueTask<bool> LockAsync(int millisecondsTimeout, CancellationToken cancellationToken)
{
var semaphore = GetOrCreate();

var locked = false;
bool locked = false;
try
{
locked = await semaphore.WaitAsync(millisecondsTimeout, cancellationToken);
locked = await semaphore.SemaphoreSlim.WaitAsync(millisecondsTimeout, cancellationToken).ConfigureAwait(false);
}
finally
{
Expand Down
4 changes: 4 additions & 0 deletions src/EasyCaching.Core/EasyCaching.Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,8 @@
<PackageReference Include="Microsoft.Extensions.Logging" Version="6.0.0" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="AsyncKeyedLock" Version="6.4.2" />
</ItemGroup>

</Project>

0 comments on commit 784a50f

Please sign in to comment.