Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 2 commits into from
Apr 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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>
Loading