From d07cb72feec885b2f01e9124f2b0cc4eaa9fc5f5 Mon Sep 17 00:00:00 2001 From: Mark Cilia Vincenti Date: Thu, 4 Dec 2025 12:04:14 +0100 Subject: [PATCH 1/2] KeyedInMemoryLock cleans up after itself and uses pooled SemaphoreSlim instances --- source/Directory.Packages.props | 1 + .../KeyedInMemoryLock/InternalKeyedInMemoryLock.cs | 10 +++------- .../InternalPackages/KeyedInMemoryLock/LockInstance.cs | 8 ++++---- source/TransactionalBox/TransactionalBox.csproj | 1 + 4 files changed, 9 insertions(+), 11 deletions(-) diff --git a/source/Directory.Packages.props b/source/Directory.Packages.props index c9c378bc..9a9b86dd 100644 --- a/source/Directory.Packages.props +++ b/source/Directory.Packages.props @@ -4,6 +4,7 @@ false + diff --git a/source/TransactionalBox/Internals/InternalPackages/KeyedInMemoryLock/InternalKeyedInMemoryLock.cs b/source/TransactionalBox/Internals/InternalPackages/KeyedInMemoryLock/InternalKeyedInMemoryLock.cs index 38e7f3ae..370ec358 100644 --- a/source/TransactionalBox/Internals/InternalPackages/KeyedInMemoryLock/InternalKeyedInMemoryLock.cs +++ b/source/TransactionalBox/Internals/InternalPackages/KeyedInMemoryLock/InternalKeyedInMemoryLock.cs @@ -1,18 +1,14 @@ -using System.Collections.Concurrent; +using AsyncKeyedLock; namespace TransactionalBox.Internals.InternalPackages.KeyedInMemoryLock { internal sealed class InternalKeyedInMemoryLock : IKeyedInMemoryLock { - private static ConcurrentDictionary _locks = new ConcurrentDictionary(); + private static readonly AsyncKeyedLocker _locks = new(); public async Task Acquire(string key, CancellationToken cancellationToken = default) { - var @lock = _locks.GetOrAdd(key, x => new SemaphoreSlim(1, 1)); - - await @lock.WaitAsync(cancellationToken); - - return new LockInstance(@lock); + return new LockInstance(await _locks.LockAsync(key, cancellationToken)); } } } diff --git a/source/TransactionalBox/Internals/InternalPackages/KeyedInMemoryLock/LockInstance.cs b/source/TransactionalBox/Internals/InternalPackages/KeyedInMemoryLock/LockInstance.cs index b43a7ca8..27ac7631 100644 --- a/source/TransactionalBox/Internals/InternalPackages/KeyedInMemoryLock/LockInstance.cs +++ b/source/TransactionalBox/Internals/InternalPackages/KeyedInMemoryLock/LockInstance.cs @@ -1,11 +1,11 @@ namespace TransactionalBox.Internals.InternalPackages.KeyedInMemoryLock { - internal sealed class LockInstance : ILockInstance + internal readonly struct LockInstance : ILockInstance { - private readonly SemaphoreSlim _semaphoreSlim; + private readonly IDisposable _instance; - internal LockInstance(SemaphoreSlim semaphoreSlim) => _semaphoreSlim = semaphoreSlim; + internal LockInstance(IDisposable instance) => _instance = instance; - public void Dispose() => _semaphoreSlim.Release(); + public void Dispose() => _instance.Dispose(); } } diff --git a/source/TransactionalBox/TransactionalBox.csproj b/source/TransactionalBox/TransactionalBox.csproj index a43e2b69..89a45f33 100644 --- a/source/TransactionalBox/TransactionalBox.csproj +++ b/source/TransactionalBox/TransactionalBox.csproj @@ -1,6 +1,7 @@  + From e4c7907f692597e9acb18a7324868e138b711eea Mon Sep 17 00:00:00 2001 From: Mark Cilia Vincenti Date: Fri, 2 Jan 2026 10:48:28 +0100 Subject: [PATCH 2/2] Bump AsyncKeyedLock to 8.0.0 --- source/Directory.Packages.props | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/Directory.Packages.props b/source/Directory.Packages.props index 9a9b86dd..48edcb16 100644 --- a/source/Directory.Packages.props +++ b/source/Directory.Packages.props @@ -4,7 +4,7 @@ false - + @@ -19,4 +19,4 @@ - \ No newline at end of file +