Skip to content

Commit 9b539a3

Browse files
committed
Make AsyncLock code return IDisposables
1 parent 198ddd3 commit 9b539a3

File tree

7 files changed

+52
-49
lines changed

7 files changed

+52
-49
lines changed

src/ImageSharp.Web/Middleware/ImageSharpMiddleware.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -289,7 +289,7 @@ private async Task ProcessRequestAsync(
289289
RecyclableMemoryStream outStream = null;
290290
try
291291
{
292-
Task<AsyncReaderWriterLock.Releaser> takeLockTask = this.asyncKeyLock.WriterLockAsync(key);
292+
Task<IDisposable> takeLockTask = this.asyncKeyLock.WriterLockAsync(key);
293293
bool lockWasAlreadyHeld = takeLockTask.Status != TaskStatus.RanToCompletion;
294294
using (await takeLockTask)
295295
{

src/ImageSharp.Web/Synchronization/AsyncKeyLock.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,9 @@ public AsyncKeyLock(int maxPoolSize = 64)
3939
/// </summary>
4040
/// <param name="key">The key identifying the specific object to lock against.</param>
4141
/// <returns>
42-
/// The <see cref="AsyncLock.Releaser"/> that will release the lock.
42+
/// The <see cref="IDisposable"/> that will release the lock.
4343
/// </returns>
44-
public Task<AsyncLock.Releaser> LockAsync(TKey key)
44+
public Task<IDisposable> LockAsync(TKey key)
4545
=> this.activeLocks.Get(key).LockAsync();
4646

4747
/// <summary>

src/ImageSharp.Web/Synchronization/AsyncKeyReaderWriterLock.cs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Copyright (c) Six Labors.
22
// Licensed under the Apache License, Version 2.0.
33

4+
using System;
45
using System.Collections.Concurrent;
56
using System.Threading.Tasks;
67

@@ -38,19 +39,19 @@ public AsyncKeyReaderWriterLock(int maxPoolSize = 64)
3839
/// </summary>
3940
/// <param name="key">The key identifying the specific object to lock against.</param>
4041
/// <returns>
41-
/// The <see cref="AsyncReaderWriterLock.Releaser"/> that will release the lock.
42+
/// The <see cref="IDisposable"/> that will release the lock.
4243
/// </returns>
43-
public Task<AsyncReaderWriterLock.Releaser> ReaderLockAsync(TKey key)
44+
public Task<IDisposable> ReaderLockAsync(TKey key)
4445
=> this.activeLocks.Get(key).ReaderLockAsync();
4546

4647
/// <summary>
4748
/// Locks the current thread in write mode asynchronously.
4849
/// </summary>
4950
/// <param name="key">The key identifying the specific object to lock against.</param>
5051
/// <returns>
51-
/// The <see cref="AsyncReaderWriterLock.Releaser"/> that will release the lock.
52+
/// The <see cref="IDisposable"/> that will release the lock.
5253
/// </returns>
53-
public Task<AsyncReaderWriterLock.Releaser> WriterLockAsync(TKey key)
54+
public Task<IDisposable> WriterLockAsync(TKey key)
5455
=> this.activeLocks.Get(key).WriterLockAsync();
5556

5657
private AsyncReaderWriterLock CreateLeasedLock(TKey key)

src/ImageSharp.Web/Synchronization/AsyncLock.cs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@ public class AsyncLock : IDisposable
1616
{
1717
private readonly SemaphoreSlim semaphore;
1818
#pragma warning disable IDE0069 // Disposable fields should be disposed
19-
private readonly Releaser releaser;
19+
private readonly IDisposable releaser;
2020
#pragma warning restore IDE0069 // Disposable fields should be disposed
21-
private readonly Task<Releaser> releaserTask;
21+
private readonly Task<IDisposable> releaserTask;
2222

2323
/// <summary>
2424
/// Initializes a new instance of the <see cref="AsyncLock"/> class.
@@ -36,12 +36,12 @@ public AsyncLock()
3636
public Action? OnRelease { get; set; }
3737

3838
/// <summary>
39-
/// Asynchronously obtains the lock. Dispose the returned <see cref="Releaser"/> to release the lock.
39+
/// Asynchronously obtains the lock. Dispose the returned <see cref="IDisposable"/> to release the lock.
4040
/// </summary>
4141
/// <returns>
42-
/// The <see cref="Releaser"/> that will release the lock.
42+
/// The <see cref="IDisposable"/> that will release the lock.
4343
/// </returns>
44-
public Task<Releaser> LockAsync()
44+
public Task<IDisposable> LockAsync()
4545
{
4646
Task wait = this.semaphore.WaitAsync();
4747

@@ -50,7 +50,7 @@ public Task<Releaser> LockAsync()
5050
? this.releaserTask
5151
: AwaitThenReturn(wait, this.releaser);
5252

53-
static async Task<Releaser> AwaitThenReturn(Task t, Releaser r)
53+
static async Task<IDisposable> AwaitThenReturn(Task t, IDisposable r)
5454
{
5555
await t;
5656
return r;
@@ -77,7 +77,7 @@ private void Release()
7777
/// <summary>
7878
/// Utility class that releases an <see cref="AsyncLock"/> on disposal.
7979
/// </summary>
80-
public sealed class Releaser : IDisposable
80+
private sealed class Releaser : IDisposable
8181
{
8282
private readonly AsyncLock toRelease;
8383

src/ImageSharp.Web/Synchronization/AsyncReaderWriterLock.cs

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,12 @@ namespace SixLabors.ImageSharp.Web.Synchronization
1919
public class AsyncReaderWriterLock
2020
{
2121
private readonly object stateLock;
22-
private readonly Releaser writerReleaser;
23-
private readonly Releaser readerReleaser;
24-
private readonly Task<Releaser> writerReleaserTask;
25-
private readonly Task<Releaser> readerReleaserTask;
26-
private readonly Queue<TaskCompletionSource<Releaser>> waitingWriters;
27-
private TaskCompletionSource<Releaser>? waitingReaders;
22+
private readonly IDisposable writerReleaser;
23+
private readonly IDisposable readerReleaser;
24+
private readonly Task<IDisposable> writerReleaserTask;
25+
private readonly Task<IDisposable> readerReleaserTask;
26+
private readonly Queue<TaskCompletionSource<IDisposable>> waitingWriters;
27+
private TaskCompletionSource<IDisposable>? waitingReaders;
2828
private int readersWaiting;
2929

3030
/// <summary>
@@ -45,7 +45,7 @@ public AsyncReaderWriterLock()
4545
this.readerReleaser = new Releaser(this, false);
4646
this.writerReleaserTask = Task.FromResult(this.writerReleaser);
4747
this.readerReleaserTask = Task.FromResult(this.readerReleaser);
48-
this.waitingWriters = new Queue<TaskCompletionSource<Releaser>>();
48+
this.waitingWriters = new Queue<TaskCompletionSource<IDisposable>>();
4949
this.waitingReaders = null;
5050
this.readersWaiting = 0;
5151
this.status = 0;
@@ -57,13 +57,13 @@ public AsyncReaderWriterLock()
5757
public Action? OnRelease { get; set; }
5858

5959
/// <summary>
60-
/// Asynchronously obtains the lock in shared reader mode. Dispose the returned <see cref="Releaser"/>
60+
/// Asynchronously obtains the lock in shared reader mode. Dispose the returned <see cref="IDisposable"/>
6161
/// to release the lock.
6262
/// </summary>
6363
/// <returns>
64-
/// The <see cref="Releaser"/> that will release the lock.
64+
/// The <see cref="IDisposable"/> that will release the lock.
6565
/// </returns>
66-
public Task<Releaser> ReaderLockAsync()
66+
public Task<IDisposable> ReaderLockAsync()
6767
{
6868
lock (this.stateLock)
6969
{
@@ -82,7 +82,7 @@ public Task<Releaser> ReaderLockAsync()
8282

8383
if (this.waitingReaders == null)
8484
{
85-
this.waitingReaders = new TaskCompletionSource<Releaser>(TaskCreationOptions.RunContinuationsAsynchronously);
85+
this.waitingReaders = new TaskCompletionSource<IDisposable>(TaskCreationOptions.RunContinuationsAsynchronously);
8686
}
8787

8888
return this.waitingReaders.Task;
@@ -91,13 +91,13 @@ public Task<Releaser> ReaderLockAsync()
9191
}
9292

9393
/// <summary>
94-
/// Asynchronously obtains the lock in exclusive writer mode. Dispose the returned <see cref="Releaser"/>
94+
/// Asynchronously obtains the lock in exclusive writer mode. Dispose the returned <see cref="IDisposable"/>
9595
/// to release the lock.
9696
/// </summary>
9797
/// <returns>
98-
/// The <see cref="Releaser"/> that will release the lock.
98+
/// The <see cref="IDisposable"/> that will release the lock.
9999
/// </returns>
100-
public Task<Releaser> WriterLockAsync()
100+
public Task<IDisposable> WriterLockAsync()
101101
{
102102
lock (this.stateLock)
103103
{
@@ -112,7 +112,7 @@ public Task<Releaser> WriterLockAsync()
112112
{
113113
// This writer has to wait to obtain the lock. Create a new tcs for this writer, add it to the
114114
// queue of waiting writers, and return the task.
115-
var waiter = new TaskCompletionSource<Releaser>(TaskCreationOptions.RunContinuationsAsynchronously);
115+
var waiter = new TaskCompletionSource<IDisposable>(TaskCreationOptions.RunContinuationsAsynchronously);
116116
this.waitingWriters.Enqueue(waiter);
117117
return waiter.Task;
118118
}
@@ -123,7 +123,7 @@ private void ReaderRelease()
123123
{
124124
try
125125
{
126-
TaskCompletionSource<Releaser>? nextLockHolder = null;
126+
TaskCompletionSource<IDisposable>? nextLockHolder = null;
127127

128128
lock (this.stateLock)
129129
{
@@ -148,8 +148,8 @@ private void WriterRelease()
148148
{
149149
try
150150
{
151-
TaskCompletionSource<Releaser>? nextLockHolder;
152-
Releaser releaser;
151+
TaskCompletionSource<IDisposable>? nextLockHolder;
152+
IDisposable releaser;
153153

154154
lock (this.stateLock)
155155
{
@@ -188,7 +188,7 @@ private void WriterRelease()
188188
/// <summary>
189189
/// Utility class that releases an <see cref="AsyncReaderWriterLock"/> on disposal.
190190
/// </summary>
191-
public sealed class Releaser : IDisposable
191+
private sealed class Releaser : IDisposable
192192
{
193193
private readonly AsyncReaderWriterLock toRelease;
194194
private readonly bool writer;

tests/ImageSharp.Web.Tests/Synchronization/AsyncLockTests.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Copyright (c) Six Labors.
22
// Licensed under the Apache License, Version 2.0.
33

4+
using System;
45
using System.Threading.Tasks;
56
using SixLabors.ImageSharp.Web.Synchronization;
67
using Xunit;
@@ -14,10 +15,10 @@ public class AsyncLockTests
1415
[Fact]
1516
public async Task OneAtATime()
1617
{
17-
Task<AsyncLock.Releaser> first = this.l.LockAsync();
18+
Task<IDisposable> first = this.l.LockAsync();
1819
Assert.True(first.IsCompletedSuccessfully);
1920

20-
Task<AsyncLock.Releaser> second = this.l.LockAsync();
21+
Task<IDisposable> second = this.l.LockAsync();
2122
Assert.False(second.IsCompleted);
2223

2324
// Release first hold on the lock and then await the second task to confirm it completes.

tests/ImageSharp.Web.Tests/Synchronization/AsyncReaderWriterLockTests.cs

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Copyright (c) Six Labors.
22
// Licensed under the Apache License, Version 2.0.
33

4+
using System;
45
using System.Threading.Tasks;
56
using SixLabors.ImageSharp.Web.Synchronization;
67
using Xunit;
@@ -14,10 +15,10 @@ public class AsyncReaderWriterLockTests
1415
[Fact]
1516
public void OneWriterAtATime()
1617
{
17-
Task<AsyncReaderWriterLock.Releaser> first = this.l.WriterLockAsync();
18+
Task<IDisposable> first = this.l.WriterLockAsync();
1819
Assert.True(first.IsCompletedSuccessfully);
1920

20-
Task<AsyncReaderWriterLock.Releaser> second = this.l.WriterLockAsync();
21+
Task<IDisposable> second = this.l.WriterLockAsync();
2122
Assert.False(second.IsCompleted);
2223

2324
first.Result.Dispose();
@@ -27,10 +28,10 @@ public void OneWriterAtATime()
2728
[Fact]
2829
public void WriterBlocksReaders()
2930
{
30-
Task<AsyncReaderWriterLock.Releaser> first = this.l.WriterLockAsync();
31+
Task<IDisposable> first = this.l.WriterLockAsync();
3132
Assert.True(first.IsCompletedSuccessfully);
3233

33-
Task<AsyncReaderWriterLock.Releaser> second = this.l.ReaderLockAsync();
34+
Task<IDisposable> second = this.l.ReaderLockAsync();
3435
Assert.False(second.IsCompleted);
3536

3637
first.Result.Dispose();
@@ -40,13 +41,13 @@ public void WriterBlocksReaders()
4041
[Fact]
4142
public void WaitingWriterBlocksReaders()
4243
{
43-
Task<AsyncReaderWriterLock.Releaser> first = this.l.ReaderLockAsync();
44+
Task<IDisposable> first = this.l.ReaderLockAsync();
4445
Assert.True(first.IsCompletedSuccessfully);
4546

46-
Task<AsyncReaderWriterLock.Releaser> second = this.l.WriterLockAsync();
47+
Task<IDisposable> second = this.l.WriterLockAsync();
4748
Assert.False(second.IsCompleted);
4849

49-
Task<AsyncReaderWriterLock.Releaser> third = this.l.ReaderLockAsync();
50+
Task<IDisposable> third = this.l.ReaderLockAsync();
5051
Assert.False(third.IsCompleted);
5152

5253
first.Result.Dispose();
@@ -60,29 +61,29 @@ public void WaitingWriterBlocksReaders()
6061
[Fact]
6162
public void MultipleReadersAtOnce()
6263
{
63-
Task<AsyncReaderWriterLock.Releaser> first = this.l.ReaderLockAsync();
64+
Task<IDisposable> first = this.l.ReaderLockAsync();
6465
Assert.True(first.IsCompletedSuccessfully);
6566

66-
Task<AsyncReaderWriterLock.Releaser> second = this.l.ReaderLockAsync();
67+
Task<IDisposable> second = this.l.ReaderLockAsync();
6768
Assert.True(second.IsCompletedSuccessfully);
6869

69-
Task<AsyncReaderWriterLock.Releaser> third = this.l.ReaderLockAsync();
70+
Task<IDisposable> third = this.l.ReaderLockAsync();
7071
Assert.True(third.IsCompletedSuccessfully);
7172
}
7273

7374
[Fact]
7475
public void AllWaitingReadersReleasedConcurrently()
7576
{
76-
Task<AsyncReaderWriterLock.Releaser> writer = this.l.WriterLockAsync();
77+
Task<IDisposable> writer = this.l.WriterLockAsync();
7778
Assert.True(writer.IsCompletedSuccessfully);
7879

79-
Task<AsyncReaderWriterLock.Releaser> reader1 = this.l.ReaderLockAsync();
80+
Task<IDisposable> reader1 = this.l.ReaderLockAsync();
8081
Assert.False(reader1.IsCompleted);
8182

82-
Task<AsyncReaderWriterLock.Releaser> reader2 = this.l.ReaderLockAsync();
83+
Task<IDisposable> reader2 = this.l.ReaderLockAsync();
8384
Assert.False(reader2.IsCompleted);
8485

85-
Task<AsyncReaderWriterLock.Releaser> reader3 = this.l.ReaderLockAsync();
86+
Task<IDisposable> reader3 = this.l.ReaderLockAsync();
8687
Assert.False(reader3.IsCompleted);
8788

8889
writer.Result.Dispose();

0 commit comments

Comments
 (0)