Skip to content

Commit 2a8cc96

Browse files
committed
* IAcquire now implements IAsyncDisposable
* MongoDB.Driver updated to 2.25.0
1 parent 93fd039 commit 2a8cc96

File tree

9 files changed

+133
-124
lines changed

9 files changed

+133
-124
lines changed

DistributedLock.Mongo.Tests/DistributedLock.Mongo.Tests.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<TargetFramework>netcoreapp3.1</TargetFramework>
4+
<TargetFramework>net8.0</TargetFramework>
55

66
<IsPackable>false</IsPackable>
77
</PropertyGroup>

DistributedLock.Mongo.Tests/LockTests.cs

Lines changed: 31 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -11,23 +11,26 @@ namespace DistributedLock.Mongo.Tests
1111
[TestClass]
1212
public class LockTests
1313
{
14-
private const string ConnectionString = "--mognodb connection string--";
15-
private static readonly IMongoCollection<LockAcquire<string>> Locks;
16-
private static readonly IMongoCollection<ReleaseSignal> Signals;
14+
private const string ConnectionString = "mongodb://localhost:27017/";
15+
private const string TestDb = "sample-db";
1716

18-
static LockTests()
17+
private IMongoCollection<LockAcquire<string>> _locks;
18+
private IMongoCollection<ReleaseSignal> _signals;
19+
20+
[TestInitialize]
21+
public async Task Initialize()
1922
{
20-
MongoClient client = new MongoClient(ConnectionString);
21-
IMongoDatabase database = client.GetDatabase("dbname");
23+
var client = new MongoClient(ConnectionString);
24+
var database = client.GetDatabase(TestDb);
2225

23-
Locks = database.GetCollection<LockAcquire<string>>("locks");
24-
Signals = database.GetCollection<ReleaseSignal>("signals");
26+
_locks = database.GetCollection<LockAcquire<string>>("locks");
27+
_signals = database.GetCollection<ReleaseSignal>("signals");
2528
}
2629

2730
[TestMethod]
2831
public async Task AcquireLock()
2932
{
30-
MongoLock<string> mongoLock = new MongoLock<string>(Locks, Signals, Guid.NewGuid().ToString());
33+
MongoLock<string> mongoLock = new MongoLock<string>(_locks, _signals, Guid.NewGuid().ToString());
3134

3235
IAcquire acq = await mongoLock.AcquireAsync(TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(0));
3336
Assert.IsTrue(acq.Acquired);
@@ -36,7 +39,7 @@ public async Task AcquireLock()
3639
[TestMethod]
3740
public async Task Acquire_And_Block()
3841
{
39-
MongoLock<string> mongoLock = new MongoLock<string>(Locks, Signals, Guid.NewGuid().ToString());
42+
MongoLock<string> mongoLock = new MongoLock<string>(_locks, _signals, Guid.NewGuid().ToString());
4043

4144
IAcquire acq1 = await mongoLock.AcquireAsync(TimeSpan.FromSeconds(30), TimeSpan.FromSeconds(0));
4245
Assert.IsTrue(acq1.Acquired);
@@ -48,24 +51,26 @@ public async Task Acquire_And_Block()
4851
[TestMethod]
4952
public async Task Acquire_Block_Release_And_Acquire()
5053
{
51-
MongoLock<string> mongoLock = new MongoLock<string>(Locks, Signals, Guid.NewGuid().ToString());
54+
MongoLock<string> mongoLock = new MongoLock<string>(_locks, _signals, Guid.NewGuid().ToString());
5255

53-
IAcquire acq1 = await mongoLock.AcquireAsync(TimeSpan.FromSeconds(30), TimeSpan.FromSeconds(0));
54-
Assert.IsTrue(acq1.Acquired);
56+
await using (IAcquire acq1 = await mongoLock.AcquireAsync(TimeSpan.FromSeconds(30), TimeSpan.FromSeconds(0)))
57+
{
58+
Assert.IsTrue(acq1.Acquired);
5559

56-
IAcquire acq2 = await mongoLock.AcquireAsync(TimeSpan.FromSeconds(30), TimeSpan.FromSeconds(0));
57-
Assert.IsFalse(acq2.Acquired);
60+
IAcquire acq2 = await mongoLock.AcquireAsync(TimeSpan.FromSeconds(30), TimeSpan.FromSeconds(0));
61+
Assert.IsFalse(acq2.Acquired);
62+
}
5863

59-
await mongoLock.ReleaseAsync(acq1);
64+
// await mongoLock.ReleaseAsync(acq1);
6065

6166
IAcquire acq3 = await mongoLock.AcquireAsync(TimeSpan.FromSeconds(30), TimeSpan.FromSeconds(0));
6267
Assert.IsTrue(acq3.Acquired);
6368
}
6469

6570
[TestMethod]
66-
public async Task Acquire_BlockFor5Secounds_Release_Acquire()
71+
public async Task Acquire_BlockFor5Seconds_Release_Acquire()
6772
{
68-
MongoLock<string> mongoLock = new MongoLock<string>(Locks, Signals, Guid.NewGuid().ToString());
73+
MongoLock<string> mongoLock = new MongoLock<string>(_locks, _signals, Guid.NewGuid().ToString());
6974

7075
IAcquire acq1 = await mongoLock.AcquireAsync(TimeSpan.FromSeconds(30), TimeSpan.FromSeconds(0));
7176
Assert.IsTrue(acq1.Acquired);
@@ -82,7 +87,7 @@ public async Task Acquire_BlockFor5Secounds_Release_Acquire()
8287
[TestMethod]
8388
public async Task Acquire_WaitUntilExpire_Acquire()
8489
{
85-
MongoLock<string> mongoLock = new MongoLock<string>(Locks, Signals, Guid.NewGuid().ToString());
90+
MongoLock<string> mongoLock = new MongoLock<string>(_locks, _signals, Guid.NewGuid().ToString());
8691

8792
IAcquire acq1 = await mongoLock.AcquireAsync(TimeSpan.FromSeconds(10), TimeSpan.FromSeconds(0));
8893
Assert.IsTrue(acq1.Acquired);
@@ -99,7 +104,7 @@ public async Task Acquire_WaitUntilExpire_Acquire()
99104
[TestMethod]
100105
public void Synchronize_CriticalSection_For_4_Threads()
101106
{
102-
MongoLock<string> mongoLock = new MongoLock<string>(Locks, Signals, Guid.NewGuid().ToString());
107+
MongoLock<string> mongoLock = new MongoLock<string>(_locks, _signals, Guid.NewGuid().ToString());
103108

104109
List<Task> tasks = new List<Task>();
105110
List<int> bucket = new List<int>() { 0 };
@@ -111,20 +116,13 @@ public void Synchronize_CriticalSection_For_4_Threads()
111116
{
112117
for (int j = 0; j < 100; j++)
113118
{
114-
IAcquire acq = await mongoLock.AcquireAsync(TimeSpan.FromSeconds(60), TimeSpan.FromSeconds(10));
115-
try
116-
{
117-
int count = bucket.Count;
118-
Thread.Sleep(random.Next(0, 10));
119-
120-
int value = bucket[count - 1];
121-
bucket.Add(value + 1);
122-
}
123-
finally
124-
{
125-
await mongoLock.ReleaseAsync(acq);
126-
}
119+
await using IAcquire acq = await mongoLock.AcquireAsync(TimeSpan.FromSeconds(60), TimeSpan.FromSeconds(10));
120+
121+
int count = bucket.Count;
122+
Thread.Sleep(random.Next(0, 10));
127123

124+
int value = bucket[count - 1];
125+
bucket.Add(value + 1);
128126
}
129127
}));
130128
}

DistributedLock.Mongo/AcquireResult.cs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
using System;
2+
using System.Threading.Tasks;
23

34
namespace DistributedLock.Mongo
45
{
56
internal class AcquireResult : IAcquire
67
{
7-
public AcquireResult(Guid acquireId)
8+
private readonly IDistributedLock _distributedLock;
9+
10+
public AcquireResult(Guid acquireId, IDistributedLock distributedLock)
811
{
12+
_distributedLock = distributedLock;
913
Acquired = true;
1014
AcquireId = acquireId;
1115
}
@@ -18,5 +22,13 @@ public AcquireResult()
1822
public bool Acquired { get; private set; }
1923

2024
public Guid AcquireId { get; private set; }
25+
26+
public async ValueTask DisposeAsync()
27+
{
28+
if (Acquired && _distributedLock != null)
29+
{
30+
await _distributedLock.ReleaseAsync(this);
31+
}
32+
}
2133
}
2234
}
Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<VersionPrefix>2.0.0</VersionPrefix>
4+
<VersionPrefix>2.1.0</VersionPrefix>
55
<TargetFrameworks>netstandard2.0;netstandard2.1</TargetFrameworks>
66
<Authors>gritse</Authors>
77
<PackageTags>lock,mongodb,mongo,distributed,distributedlock,mutex</PackageTags>
@@ -13,13 +13,13 @@
1313
<Copyright>gritse</Copyright>
1414
<Description>Exclusive distributed lock library for C#/.NET using MongoDB</Description>
1515
<PackageReleaseNotes>
16-
* Changed Target Framework to netstandard2.0 and netstandard2.1
17-
* MongoDB.Driver updated to 2.14.1
16+
* IAcquire now implements IAsyncDisposable
17+
* MongoDB.Driver updated to 2.25.0
1818
</PackageReleaseNotes>
1919
</PropertyGroup>
2020

2121
<ItemGroup>
22-
<PackageReference Include="MongoDB.Driver" Version="2.14.1" />
22+
<PackageReference Include="MongoDB.Driver" Version="2.25.0" />
2323
</ItemGroup>
2424

2525
</Project>

DistributedLock.Mongo/IAcquire.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
namespace DistributedLock.Mongo
44
{
5-
public interface IAcquire
5+
public interface IAcquire : IAsyncDisposable
66
{
77
/// <summary>
88
/// true if lock successfully acquired; otherwise, false

DistributedLock.Mongo/MongoLock.cs

Lines changed: 26 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -36,29 +36,47 @@ public async Task<IAcquire> AcquireAsync(TimeSpan lifetime, TimeSpan timeout)
3636
if (lifetime < TimeSpan.Zero || lifetime > TimeSpan.MaxValue) throw new ArgumentOutOfRangeException(nameof(lifetime), "The value of lifetime in milliseconds is negative or is greater than MaxValue");
3737
if (timeout < TimeSpan.Zero || timeout > TimeSpan.MaxValue) throw new ArgumentOutOfRangeException(nameof(timeout), "The value of timeout in milliseconds is negative or is greater than MaxValue");
3838

39-
Guid acquireId = Guid.NewGuid();
39+
var acquireId = Guid.NewGuid();
4040

4141
while (await TryUpdate(lifetime, acquireId) == false)
4242
{
43-
using (IAsyncCursor<LockAcquire<T>> cursor = await _locks.FindAsync(_builder.Eq(x => x.Id, _id)))
43+
using (var cursor = await _locks.FindAsync(_builder.Eq(x => x.Id, _id)))
4444
{
45-
LockAcquire<T> acquire = await cursor.FirstOrDefaultAsync();
45+
var acquire = await cursor.FirstOrDefaultAsync();
4646

4747
if (acquire != null && await WaitSignal(acquire.AcquireId, timeout) == false)
4848
{
49-
return await TryUpdate(lifetime, acquireId) == false ? new AcquireResult() : new AcquireResult(acquireId);
49+
return await TryUpdate(lifetime, acquireId) == false ? new AcquireResult() : new AcquireResult(acquireId, this);
5050
}
5151
}
5252
}
5353

54-
return new AcquireResult(acquireId);
54+
return new AcquireResult(acquireId, this);
55+
}
56+
57+
/// <summary>
58+
/// Releases an exclusive lock for the specified acquire. If lock isn't exist or already released, there will be no exceptions throwed
59+
/// </summary>
60+
/// <param name="acquire">IAcquire object returned by AcquireAsync</param>
61+
/// <returns></returns>
62+
public async Task ReleaseAsync(IAcquire acquire)
63+
{
64+
if (acquire == null) throw new ArgumentNullException(nameof(acquire));
65+
if (acquire.Acquired == false) return;
66+
67+
var updateResult = await _locks.UpdateOneAsync(
68+
filter: _builder.And(_builder.Eq(x => x.Id, _id), _builder.Eq(x => x.AcquireId, acquire.AcquireId)), // x => x.Id == _id && x.AcquireId == acquire.AcquireId,
69+
update: new UpdateDefinitionBuilder<LockAcquire<T>>().Set(x => x.Acquired, false));
70+
71+
if (updateResult.IsAcknowledged && updateResult.ModifiedCount > 0)
72+
await _signals.InsertOneAsync(new ReleaseSignal { AcquireId = acquire.AcquireId });
5573
}
5674

5775
private async Task<bool> WaitSignal(Guid acquireId, TimeSpan timeout)
5876
{
59-
using (IAsyncCursor<ReleaseSignal> cursor = await _signals.Find(x => x.AcquireId == acquireId, new FindOptions { MaxAwaitTime = timeout, CursorType = CursorType.TailableAwait }).ToCursorAsync())
77+
using (var cursor = await _signals.Find(x => x.AcquireId == acquireId, new FindOptions { MaxAwaitTime = timeout, CursorType = CursorType.TailableAwait }).ToCursorAsync())
6078
{
61-
DateTime started = DateTime.UtcNow;
79+
var started = DateTime.UtcNow;
6280

6381
while (await cursor.MoveNextAsync())
6482
{
@@ -80,7 +98,7 @@ private async Task<bool> TryUpdate(TimeSpan lifetime, Guid acquireId)
8098
.Set(x => x.AcquireId, acquireId)
8199
.SetOnInsert(x => x.Id, _id);
82100

83-
FilterDefinition<LockAcquire<T>> filter = _builder.And(
101+
var filter = _builder.And(
84102
_builder.Eq(x => x.Id, _id),
85103
_builder.Or(
86104
_builder.Eq(x => x.Acquired, false),
@@ -102,24 +120,5 @@ private async Task<bool> TryUpdate(TimeSpan lifetime, Guid acquireId)
102120
throw;
103121
}
104122
}
105-
106-
/// <summary>
107-
/// Releases an exclusive lock for the specified acquire. If lock isn't exist or already released, there will be no exceptions throwed
108-
/// </summary>
109-
/// <param name="acquire">IAcquire object returned by AcquireAsync</param>
110-
/// <returns></returns>
111-
public async Task ReleaseAsync(IAcquire acquire)
112-
{
113-
if (acquire == null) throw new ArgumentNullException(nameof(acquire));
114-
if (acquire.Acquired == false) return;
115-
116-
var updateResult = await _locks.UpdateOneAsync(
117-
filter: _builder.And(_builder.Eq(x => x.Id, _id), _builder.Eq(x => x.AcquireId, acquire.AcquireId)), // x => x.Id == _id && x.AcquireId == acquire.AcquireId,
118-
update: new UpdateDefinitionBuilder<LockAcquire<T>>().Set(x => x.Acquired, false));
119-
120-
if (updateResult.IsAcknowledged && updateResult.ModifiedCount > 0)
121-
await _signals.InsertOneAsync(new ReleaseSignal { AcquireId = acquire.AcquireId });
122-
}
123-
124123
}
125124
}

DistributedLock.Sample/DistributedLock.Sample.csproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@
22

33
<PropertyGroup>
44
<OutputType>Exe</OutputType>
5-
<TargetFramework>netcoreapp3.1</TargetFramework>
5+
<TargetFramework>net8.0</TargetFramework>
66
<LangVersion>latest</LangVersion>
77
</PropertyGroup>
88

99
<ItemGroup>
10-
<PackageReference Include="MongoDB.Driver" Version="2.14.1" />
10+
<PackageReference Include="MongoDB.Driver" Version="2.25.0" />
1111
</ItemGroup>
1212

1313
<ItemGroup>

0 commit comments

Comments
 (0)