Skip to content

Commit 55156d2

Browse files
committed
Added support of synchronous lock on WASM
1 parent 4c05956 commit 55156d2

File tree

10 files changed

+132
-251
lines changed

10 files changed

+132
-251
lines changed

src/DotNext.Threading/Threading/AsyncAutoResetEvent.cs

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ internal StateManager(bool initialState)
2222

2323
readonly bool ILockManager.IsLockAllowed => Value;
2424

25-
void ILockManager.AcquireLock(bool synchronously) => Value = false;
25+
void ILockManager.AcquireLock() => Value = false;
2626
}
2727

2828
private ValueTaskPool<bool, DefaultWaitNode, Action<DefaultWaitNode>> pool;
@@ -78,7 +78,7 @@ public bool Reset()
7878
ObjectDisposedException.ThrowIf(IsDisposed, this);
7979

8080
Monitor.Enter(SyncRoot);
81-
var result = TryAcquire(ref manager, synchronously: true);
81+
var result = TryAcquire(ref manager);
8282
Monitor.Exit(SyncRoot);
8383

8484
return result;
@@ -96,25 +96,28 @@ public bool Set()
9696
var suspendedCaller = default(ManualResetCompletionSource);
9797
bool result;
9898

99-
lock (SyncRoot)
99+
if (result = !manager.Value)
100100
{
101-
if (result = !manager.Value)
101+
lock (SyncRoot)
102102
{
103-
for (LinkedValueTaskCompletionSource<bool>? current = WaitQueueHead, next; ; current = next)
103+
if (result = !manager.Value)
104104
{
105-
if (current is null)
106-
{
107-
manager.Value = true;
108-
break;
109-
}
110-
111-
next = current.Next;
112-
113-
// skip dead node
114-
if (RemoveAndSignal(current, out var resumable))
105+
for (LinkedValueTaskCompletionSource<bool>? current = WaitQueueHead, next;; current = next)
115106
{
116-
suspendedCaller = resumable ? current : null;
117-
break;
107+
if (current is null)
108+
{
109+
manager.Value = true;
110+
break;
111+
}
112+
113+
next = current.Next;
114+
115+
// skip dead node
116+
if (RemoveAndSignal(current, out var resumable))
117+
{
118+
suspendedCaller = resumable ? current : null;
119+
break;
120+
}
118121
}
119122
}
120123
}

src/DotNext.Threading/Threading/AsyncCountdownEvent.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ internal void IncrementInitial(long value)
3333
internal bool Decrement(long value = 1L)
3434
=> (Current = Math.Max(0L, Current - value)) is 0L;
3535

36-
readonly void ILockManager.AcquireLock(bool synchronously)
36+
readonly void ILockManager.AcquireLock()
3737
{
3838
// nothing to do here
3939
}

src/DotNext.Threading/Threading/AsyncCounter.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ public class AsyncCounter : QueuedSynchronizer, IAsyncEvent
2121
[StructLayout(LayoutKind.Auto)]
2222
private struct StateManager : ILockManager<DefaultWaitNode>
2323
{
24-
required internal long Value;
24+
internal required long Value;
2525

2626
internal void Increment(long delta) => Value = checked(Value + delta);
2727

@@ -36,7 +36,7 @@ internal bool TryReset()
3636

3737
readonly bool ILockManager.IsLockAllowed => Value > 0L;
3838

39-
void ILockManager.AcquireLock(bool synchronously) => Decrement();
39+
void ILockManager.AcquireLock() => Decrement();
4040
}
4141

4242
private ValueTaskPool<bool, DefaultWaitNode, Action<DefaultWaitNode>> pool;
@@ -203,7 +203,7 @@ public bool TryDecrement()
203203
ObjectDisposedException.ThrowIf(IsDisposed, this);
204204

205205
Monitor.Enter(SyncRoot);
206-
var result = TryAcquire(ref manager, synchronously: true);
206+
var result = TryAcquire(ref manager);
207207
Monitor.Exit(SyncRoot);
208208

209209
return result;

src/DotNext.Threading/Threading/AsyncEventHub.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -562,7 +562,7 @@ internal WaitAllManager(AsyncEventHub stateHolder, in UInt128 mask)
562562

563563
bool ILockManager.IsLockAllowed => (state.Value & mask) == mask;
564564

565-
void ILockManager.AcquireLock(bool synchronously)
565+
void ILockManager.AcquireLock()
566566
{
567567
// no need to reset events
568568
}
@@ -595,7 +595,7 @@ internal ICollection<int>? Events
595595

596596
bool ILockManager.IsLockAllowed => (state.Value & mask) != UInt128.Zero;
597597

598-
void ILockManager.AcquireLock(bool synchronously)
598+
void ILockManager.AcquireLock()
599599
{
600600
if (Events is { } collection)
601601
FillIndices(state.Value & mask, collection);

src/DotNext.Threading/Threading/AsyncExclusiveLock.cs

Lines changed: 34 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
using System.Diagnostics;
22
using System.Runtime.InteropServices;
3-
using System.Runtime.Versioning;
43

54
namespace DotNext.Threading;
65

@@ -17,25 +16,23 @@ public class AsyncExclusiveLock : QueuedSynchronizer, IAsyncDisposable
1716
private struct LockManager : ILockManager<DefaultWaitNode>
1817
{
1918
// null - not acquired, Sentinel.Instance - acquired asynchronously, Thread - acquired synchronously
20-
private object? state;
19+
private bool state;
2120

22-
internal readonly bool Value => state is not null;
21+
internal readonly bool Value => state;
2322

24-
internal readonly bool VolatileRead() => Volatile.Read(in state) is not null;
23+
internal readonly bool VolatileRead() => Volatile.Read(in state);
2524

26-
public readonly bool IsLockAllowed => state is null;
25+
public readonly bool IsLockAllowed => state is false;
2726

28-
public void AcquireLock(bool synchronously)
29-
=> state = synchronously ? Thread.CurrentThread : Sentinel.Instance;
27+
public void AcquireLock()
28+
=> state = true;
3029

31-
internal void ExitLock() => state = null;
32-
33-
readonly bool ILockManager.IsLockHeldByCurrentThread
34-
=> ReferenceEquals(state, Thread.CurrentThread);
30+
internal void ExitLock() => state = false;
3531
}
3632

3733
private ValueTaskPool<bool, DefaultWaitNode, Action<DefaultWaitNode>> pool;
3834
private LockManager manager;
35+
private Thread? lockOwner;
3936

4037
/// <summary>
4138
/// Initializes a new asynchronous exclusive lock.
@@ -88,10 +85,20 @@ public bool TryAcquire()
8885
return TryAcquireCore();
8986
}
9087

88+
private bool IsLockHelpByCurrentThread
89+
{
90+
get => lockOwner is not { } owner || ReferenceEquals(owner, Thread.CurrentThread);
91+
set
92+
{
93+
if (value)
94+
lockOwner = Thread.CurrentThread;
95+
}
96+
}
97+
9198
private bool TryAcquireCore()
9299
{
93100
Monitor.Enter(SyncRoot);
94-
var result = TryAcquire(ref manager, synchronously: true);
101+
var result = IsLockHelpByCurrentThread = TryAcquire(ref manager);
95102
Monitor.Exit(SyncRoot);
96103

97104
return result;
@@ -106,16 +113,22 @@ private bool TryAcquireCore()
106113
/// <exception cref="ArgumentOutOfRangeException"><paramref name="timeout"/> is negative.</exception>
107114
/// <exception cref="ObjectDisposedException">This object has been disposed.</exception>
108115
/// <exception cref="LockRecursionException">The lock is already acquired by the current thread.</exception>
109-
[UnsupportedOSPlatform("browser")]
110116
public bool TryAcquire(TimeSpan timeout, CancellationToken token = default)
111117
{
112-
ObjectDisposedException.ThrowIf(IsDisposed, this);
118+
if (IsLockHelpByCurrentThread)
119+
throw new LockRecursionException();
120+
121+
bool result;
122+
try
123+
{
124+
IsLockHelpByCurrentThread = result = TryAcquireAsync(timeout, token).Wait();
125+
}
126+
catch (OperationCanceledException e) when (e.CancellationToken == token)
127+
{
128+
result = false;
129+
}
113130

114-
return timeout == TimeSpan.Zero
115-
? TryAcquireCore()
116-
: token.CanBeCanceled
117-
? TryAcquire(new Timeout(timeout), ref manager, token)
118-
: TryAcquire(new Timeout(timeout), ref manager);
131+
return result;
119132
}
120133

121134
/// <summary>
@@ -228,7 +241,7 @@ public ValueTask StealAsync(object? reason = null, CancellationToken token = def
228241
// skip dead node
229242
if (RemoveAndSignal(current, out var resumable))
230243
{
231-
manager.AcquireLock(synchronously: false);
244+
manager.AcquireLock();
232245
return resumable ? current : null;
233246
}
234247
}
@@ -252,16 +265,12 @@ public void Release()
252265
throw new SynchronizationLockException(ExceptionMessages.NotInLock);
253266

254267
manager.ExitLock();
268+
lockOwner = null;
255269
suspendedCaller = DrainWaitQueue();
256270

257271
if (IsDisposing && IsReadyToDispose)
258272
{
259273
Dispose(true);
260-
Monitor.PulseAll(SyncRoot);
261-
}
262-
else
263-
{
264-
Monitor.Pulse(SyncRoot);
265274
}
266275
}
267276

src/DotNext.Threading/Threading/AsyncManualResetEvent.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ internal bool TryReset()
3030

3131
readonly bool ILockManager.IsLockAllowed => Value;
3232

33-
readonly void ILockManager.AcquireLock(bool synchronously)
33+
readonly void ILockManager.AcquireLock()
3434
{
3535
// nothing to do here
3636
}

0 commit comments

Comments
 (0)