diff --git a/FolderSyncNet.csproj b/FolderSyncNet.csproj
index 45125ec..000ddd9 100644
--- a/FolderSyncNet.csproj
+++ b/FolderSyncNet.csproj
@@ -77,6 +77,9 @@
packages\AsyncEnumerator.4.0.2\lib\net461\AsyncEnumerable.dll
+
+ packages\AsyncKeyedLock.7.1.3\lib\netstandard2.0\AsyncKeyedLock.dll
+
packages\Microsoft.Bcl.AsyncInterfaces.7.0.0\lib\net462\Microsoft.Bcl.AsyncInterfaces.dll
@@ -396,4 +399,4 @@
-
\ No newline at end of file
+
diff --git a/InitialScan.cs b/InitialScan.cs
index 24f6560..8394aef 100644
--- a/InitialScan.cs
+++ b/InitialScan.cs
@@ -242,7 +242,7 @@ private static IAsyncEnumerable ProcessSubDirs(FolderSyncNetSource.Dir
var historyFileInfosDict = new Dictionary();
//var historyFileInfosTask = Task.CompletedTask;
- AsyncLockQueueDictionary.LockDictReleaser destOrHistoryDirCacheLock = null;
+ IDisposable destOrHistoryDirCacheLock = null;
//AsyncLockQueueDictionary.LockDictReleaser historyDirCacheLock = null;
FileInfo[] fileInfos = null;
diff --git a/Synchronisation.cs b/Synchronisation.cs
index 864c39a..831594b 100644
--- a/Synchronisation.cs
+++ b/Synchronisation.cs
@@ -7,141 +7,39 @@
//
#define ASYNC
+using AsyncKeyedLock;
using System;
using System.Collections.Generic;
-using System.Diagnostics;
+using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
-using Nito.AsyncEx;
namespace FolderSync
{
public class AsyncLockQueueDictionary
where KeyT : IComparable, IEquatable
{
- private static readonly bool IsStringDictionary = typeof(KeyT) == typeof(string);
-
- private readonly object DictionaryAccessMutex = new object();
- //private readonly SemaphoreSlim DictionaryAccessMutex = new SemaphoreSlim(1, 1);
- private readonly Dictionary LockQueueDictionary = new Dictionary();
-
- public sealed class AsyncLockWithWaiterCount
- {
- public readonly AsyncLock LockEntry;
-#pragma warning disable S1104 //Warning S1104 Make this field 'private' and encapsulate it in a 'public' property.
- public int WaiterCount;
-#pragma warning restore S1104
-
- public AsyncLockWithWaiterCount()
- {
- this.LockEntry = new AsyncLock();
- this.WaiterCount = 1;
- }
- }
+ private readonly AsyncKeyedLocker LockQueueDictionary = new AsyncKeyedLocker();
- public sealed class LockDictReleaser : IDisposable //TODO: implement IAsyncDisposable in .NET 5.0
- {
- private readonly KeyT Name;
- private readonly AsyncLockWithWaiterCount LockEntry;
-#if !NOASYNC
- private readonly IDisposable LockHandle;
-#endif
- private readonly AsyncLockQueueDictionary AsyncLockQueueDictionary;
-
-#if NOASYNC
- internal LockDictReleaser(KeyT name, AsyncLockWithWaiterCount lockEntry, AsyncLockQueueDictionary asyncLockQueueDictionary)
-#else
- internal LockDictReleaser(KeyT name, AsyncLockWithWaiterCount lockEntry, IDisposable lockHandle, AsyncLockQueueDictionary asyncLockQueueDictionary)
-#endif
- {
- this.Name = name;
- this.LockEntry = lockEntry;
-#if !NOASYNC
- this.LockHandle = lockHandle;
-#endif
- this.AsyncLockQueueDictionary = asyncLockQueueDictionary;
- }
-
- public void Dispose()
- {
-#if NOASYNC
- this.AsyncLockQueueDictionary.ReleaseLock(this.Name, this.LockEntry);
-#else
- this.AsyncLockQueueDictionary.ReleaseLock(this.Name, this.LockEntry, this.LockHandle);
-#endif
- }
- }
+ private static readonly bool IsStringDictionary = typeof(KeyT) == typeof(string);
-#if NOASYNC
- private void ReleaseLock(KeyT name, AsyncLockWithWaiterCount lockEntry)
-#else
- private void ReleaseLock(KeyT name, AsyncLockWithWaiterCount lockEntry, IDisposable lockHandle)
-#endif
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public async ValueTask LockAsync(KeyT name)
{
-#if NOASYNC
- Monitor.Exit(lockEntry.LockEntry);
-#else
- lockHandle.Dispose();
-#endif
-
- lock (DictionaryAccessMutex)
- //DictionaryAccessMutex.Wait();
- //try
- {
- lockEntry.WaiterCount--;
- Debug.Assert(lockEntry.WaiterCount >= 0);
-
- if (lockEntry.WaiterCount == 0) //NB!
- {
- LockQueueDictionary.Remove(name);
- }
- }
- //finally
- //{
- // DictionaryAccessMutex.Release();
- //}
+ return await LockQueueDictionary.LockAsync(name).ConfigureAwait(false);
}
- public async Task LockAsync(KeyT name, CancellationToken cancellationToken = default(CancellationToken))
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public async ValueTask LockAsync(KeyT name, CancellationToken cancellationToken)
{
- AsyncLockWithWaiterCount lockEntry;
-#pragma warning disable PH_S023 //Message PH_S023 Using the blocking synchronization mechanics of a monitor inside an async method is discouraged; use SemaphoreSlim instead.
- lock (DictionaryAccessMutex)
-#pragma warning restore PH_S023
- //await DictionaryAccessMutex.WaitAsync(cancellationToken);
- //try
- {
- if (!LockQueueDictionary.TryGetValue(name, out lockEntry))
- {
- lockEntry = new AsyncLockWithWaiterCount();
- LockQueueDictionary.Add(name, lockEntry);
- }
- else
- {
- lockEntry.WaiterCount++; //NB! must be done inside the lock and BEFORE waiting for the lock
- }
- }
- //finally
- //{
- // DictionaryAccessMutex.Release();
- //}
-
-#if NOASYNC
-#pragma warning disable PH_P006 //warning PH_P006 Favor the use of the lock-statement instead of the use of Monitor.Enter when no timeouts are needed.
- Monitor.Enter(lockEntry.LockEntry);
-#pragma warning restore PH_P006
- return new LockDictReleaser(name, lockEntry, this);
-#else
- var lockHandle = await lockEntry.LockEntry.LockAsync(cancellationToken);
- return new LockDictReleaser(name, lockEntry, lockHandle, this);
-#endif
+ return await LockQueueDictionary.LockAsync(name, cancellationToken).ConfigureAwait(false);
}
public sealed class MultiLockDictReleaser : IDisposable //TODO: implement IAsyncDisposable in .NET 5.0
{
- private readonly LockDictReleaser[] Releasers;
+ private readonly IDisposable[] Releasers;
- public MultiLockDictReleaser(params LockDictReleaser[] releasers)
+ public MultiLockDictReleaser(params IDisposable[] releasers)
{
this.Releasers = releasers;
}
@@ -156,6 +54,7 @@ public void Dispose()
}
}
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public async Task LockAsync(KeyT name1, KeyT name2, CancellationToken cancellationToken = default(CancellationToken))
{
var names = new List()
diff --git a/packages.config b/packages.config
index 9f9985a..47dfd9e 100644
--- a/packages.config
+++ b/packages.config
@@ -4,6 +4,7 @@
+