Skip to content

Commit d04394d

Browse files
committed
Make UseWriterLockAsync thread-safe
1 parent 79082c7 commit d04394d

File tree

3 files changed

+37
-17
lines changed

3 files changed

+37
-17
lines changed

src/AsyncKeyLock.Tests/AsyncLockTest.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public async Task ThreeParallelReadersClosed()
3434
using var r1 = await lockEntity.ReaderLockAsync();
3535
using var r2 = await lockEntity.ReaderLockAsync();
3636
using var r3 = await lockEntity.ReaderLockAsync();
37-
}
37+
}
3838

3939
Assert.Equal(0, lockEntity.CountRunningReaders);
4040
}

src/AsyncKeyLock/AsyncLock.cs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ public sealed class AsyncLock
1111
{
1212
public AsyncLock()
1313
{
14-
_syncObj = _waitingWriters;
14+
SyncObj = _waitingWriters;
1515

1616
_readerReleaserTask = Task.FromResult(new ReaderReleaser(this, true));
1717
_writerReleaserTask = Task.FromResult(new WriterReleaser(this, true));
@@ -20,10 +20,10 @@ public AsyncLock()
2020
internal AsyncLock(object syncObject)
2121
: this()
2222
{
23-
_syncObj = syncObject;
23+
SyncObj = syncObject;
2424
}
2525

26-
private readonly object _syncObj;
26+
internal readonly object SyncObj;
2727

2828
private readonly Queue<TaskCompletionSource<ReaderReleaser>> _waitingReaders = new Queue<TaskCompletionSource<ReaderReleaser>>();
2929
private readonly Queue<TaskCompletionSource<WriterReleaser>> _waitingWriters = new Queue<TaskCompletionSource<WriterReleaser>>();
@@ -86,7 +86,7 @@ public Task<ReaderReleaser> ReaderLockAsync(CancellationToken cancellation = def
8686
return Task.FromCanceled<ReaderReleaser>(cancellation);
8787
}
8888

89-
lock (_syncObj)
89+
lock (SyncObj)
9090
{
9191
//no running or waiting write lock?
9292
if (_isWriterRunning == false && _waitingWriters.Count == 0)
@@ -110,7 +110,7 @@ public Task<WriterReleaser> WriterLockAsync(CancellationToken cancellation = def
110110
return Task.FromCanceled<WriterReleaser>(cancellation);
111111
}
112112

113-
lock (_syncObj)
113+
lock (SyncObj)
114114
{
115115
if (_isWriterRunning == false && _readersRunning == 0)
116116
{
@@ -126,9 +126,9 @@ public Task<WriterReleaser> WriterLockAsync(CancellationToken cancellation = def
126126
}
127127
}
128128

129-
internal void Release(AsyncLockType type)
129+
internal void Release(AsyncLockType type, bool sendReleaseEvent = true)
130130
{
131-
lock (_syncObj)
131+
lock (SyncObj)
132132
{
133133
try
134134
{
@@ -143,7 +143,7 @@ internal void Release(AsyncLockType type)
143143
}
144144
finally
145145
{
146-
if (State == AsyncLockState.Idle)
146+
if (sendReleaseEvent && State == AsyncLockState.Idle)
147147
{
148148
Released?.Invoke(this);
149149
}

src/AsyncKeyLock/Releaser/ReaderReleaser.cs

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,21 +29,41 @@ public async Task UseWriterLockAsync(Func<Task> func, CancellationToken cancella
2929
{
3030
cancellation.ThrowIfCancellationRequested();
3131

32-
//release reader lock
33-
AsyncLock.Release(AsyncLockType.Read);
32+
Task<WriterReleaser> taskWriter;
33+
WriterReleaser? writerReleaser = null;
34+
35+
lock (AsyncLock.SyncObj)
36+
{
37+
//release reader lock
38+
AsyncLock.Release(AsyncLockType.Read, false);
39+
40+
taskWriter = AsyncLock.WriterLockAsync(cancellation);
41+
}
3442

3543
try
3644
{
3745
//create new writer lock
38-
using (await AsyncLock.WriterLockAsync(cancellation))
39-
{
40-
await func();
41-
}
46+
writerReleaser = await taskWriter;
47+
48+
await func();
49+
4250
}
4351
finally
4452
{
45-
//restore reader lock
46-
await AsyncLock.ReaderLockAsync();
53+
Task<ReaderReleaser> taskReader;
54+
55+
lock (AsyncLock.SyncObj)
56+
{
57+
if (writerReleaser != null)
58+
{
59+
AsyncLock.Release(AsyncLockType.Write, false);
60+
}
61+
62+
//restore reader lock
63+
taskReader = AsyncLock.ReaderLockAsync();
64+
}
65+
66+
await taskReader;
4767
}
4868
}
4969

0 commit comments

Comments
 (0)