diff --git a/src/Uno.Foundation/Uno.Core.Extensions/Uno.Core.Extensions.Compatibility/Threading/AsyncLock.cs b/src/Uno.Foundation/Uno.Core.Extensions/Uno.Core.Extensions.Compatibility/Threading/AsyncLock.cs
index 61a61722124e..343e1a8acb25 100644
--- a/src/Uno.Foundation/Uno.Core.Extensions/Uno.Core.Extensions.Compatibility/Threading/AsyncLock.cs
+++ b/src/Uno.Foundation/Uno.Core.Extensions/Uno.Core.Extensions.Compatibility/Threading/AsyncLock.cs
@@ -1,3 +1,4 @@
+#nullable enable
// ******************************************************************
// Copyright � 2015-2018 Uno Platform Inc. All rights reserved.
//
@@ -18,27 +19,37 @@
using System.Threading;
using System.Threading.Tasks;
-using Uno.Disposables;
+namespace Uno.Threading;
-namespace Uno.Threading
+///
+/// An asynchronous lock, that can be used in conjuction with C# async/await
+///
+internal sealed class AsyncLock
{
+ private readonly SemaphoreSlim _semaphore = new(1, 1);
+ private ulong _handleId;
+
///
- /// An asynchronous lock, that can be used in conjuction with C# async/await
+ /// Acquires the lock, then provides a disposable to release it.
+ /// WARNING: This DOES NOT support reentrancy.
///
- internal sealed class AsyncLock
+ /// A cancellation token to cancel the lock
+ /// An IDisposable instance that allows the release of the lock.
+ public async ValueTask LockAsync(CancellationToken ct)
{
- private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1);
+ await _semaphore.WaitAsync(ct);
- ///
- /// Acquires the lock, then provides a disposable to release it.
- ///
- /// A cancellation token to cancel the lock
- /// An IDisposable instance that allows the release of the lock.
- public async Task LockAsync(CancellationToken ct)
- {
- await _semaphore.WaitAsync(ct);
+ return new Handle(this, _handleId);
+ }
- return Disposable.Create(() => _semaphore.Release());
+ public record struct Handle(AsyncLock Lock, ulong Id) : IDisposable
+ {
+ public void Dispose()
+ {
+ if (Interlocked.CompareExchange(ref Lock._handleId, Id + 1, Id) == Id) // This avoids (concurrent) double dispose / release
+ {
+ Lock._semaphore.Release();
+ }
}
}
}