Skip to content

Commit

Permalink
Merge pull request #135 from madelson/release-2.3.1
Browse files Browse the repository at this point in the history
Release 2.3.1
  • Loading branch information
madelson authored Jul 9, 2022
2 parents f3a819d + f4a3401 commit 7fb18b1
Show file tree
Hide file tree
Showing 20 changed files with 233 additions and 28 deletions.
5 changes: 4 additions & 1 deletion DistributedLock.Core/DistributedLock.Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
</PropertyGroup>

<PropertyGroup>
<Version>1.0.4</Version>
<Version>1.0.5</Version>
<AssemblyVersion>1.0.0.0</AssemblyVersion>
<Authors>Michael Adelson</Authors>
<Description>Core interfaces and utilities that support the DistributedLock.* family of packages</Description>
Expand All @@ -32,6 +32,9 @@
<TreatSpecificWarningsAsErrors />
<!-- see https://github.com/dotnet/sdk/issues/2679 -->
<DebugType>embedded</DebugType>
<!-- see https://mitchelsellers.com/blog/article/net-5-deterministic-builds-source-linking -->
<ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>
<EmbedUntrackedSources>true</EmbedUntrackedSources>
</PropertyGroup>

<PropertyGroup Condition="'$(Configuration)' == 'Debug'">
Expand Down
11 changes: 6 additions & 5 deletions DistributedLock.Core/Internal/Data/ConnectionMonitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -253,9 +253,10 @@ private async ValueTask StopOrDisposeAsync(bool isDispose)
// the state to disposed above which the monitoring loop will check if it
// takes over the Cancel() thread.
this._monitorStateChangedTokenSource?.Cancel();

// unsubscribe from state change tracking
if (this._stateChangedHandler != null

// If disposing, unsubscribe from state change tracking.
if (isDispose
&& this._stateChangedHandler != null
&& this._weakConnection.TryGetTarget(out var connection))
{
((DbConnection)connection.InnerConnection).StateChange -= this._stateChangedHandler;
Expand Down Expand Up @@ -424,7 +425,7 @@ public MonitoringHandle(ConnectionMonitor keepaliveHelper, CancellationToken can

private sealed class AlreadyCanceledHandle : IDatabaseConnectionMonitoringHandle
{
private readonly CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource();
private readonly CancellationTokenSource _cancellationTokenSource = new();

public AlreadyCanceledHandle()
{
Expand All @@ -438,7 +439,7 @@ public AlreadyCanceledHandle()

private sealed class NullHandle : IDatabaseConnectionMonitoringHandle
{
public static readonly NullHandle Instance = new NullHandle();
public static readonly NullHandle Instance = new();

private NullHandle() { }

Expand Down
3 changes: 3 additions & 0 deletions DistributedLock.FileSystem/DistributedLock.FileSystem.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@
<TreatSpecificWarningsAsErrors />
<!-- see https://github.com/dotnet/sdk/issues/2679 -->
<DebugType>embedded</DebugType>
<!-- see https://mitchelsellers.com/blog/article/net-5-deterministic-builds-source-linking -->
<ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>
<EmbedUntrackedSources>true</EmbedUntrackedSources>
</PropertyGroup>

<PropertyGroup Condition="'$(Configuration)' == 'Debug'">
Expand Down
5 changes: 4 additions & 1 deletion DistributedLock.MySql/DistributedLock.MySql.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
</PropertyGroup>

<PropertyGroup>
<Version>1.0.0</Version>
<Version>1.0.1</Version>
<AssemblyVersion>1.0.0.0</AssemblyVersion>
<Authors>Michael Adelson</Authors>
<Description>Provides a distributed lock implementation based on MySql</Description>
Expand All @@ -32,6 +32,9 @@
<TreatSpecificWarningsAsErrors />
<!-- see https://github.com/dotnet/sdk/issues/2679 -->
<DebugType>embedded</DebugType>
<!-- see https://mitchelsellers.com/blog/article/net-5-deterministic-builds-source-linking -->
<ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>
<EmbedUntrackedSources>true</EmbedUntrackedSources>
</PropertyGroup>

<PropertyGroup Condition="'$(Configuration)' == 'Debug'">
Expand Down
5 changes: 4 additions & 1 deletion DistributedLock.Oracle/DistributedLock.Oracle.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
</PropertyGroup>

<PropertyGroup>
<Version>1.0.0</Version>
<Version>1.0.1</Version>
<AssemblyVersion>1.0.0.0</AssemblyVersion>
<Authors>Michael Adelson</Authors>
<Description>Provides a distributed lock implementation based on Oracle Database</Description>
Expand All @@ -32,6 +32,9 @@
<TreatSpecificWarningsAsErrors />
<!-- see https://github.com/dotnet/sdk/issues/2679 -->
<DebugType>embedded</DebugType>
<!-- see https://mitchelsellers.com/blog/article/net-5-deterministic-builds-source-linking -->
<ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>
<EmbedUntrackedSources>true</EmbedUntrackedSources>
</PropertyGroup>

<PropertyGroup Condition="'$(Configuration)' == 'Debug'">
Expand Down
5 changes: 4 additions & 1 deletion DistributedLock.Postgres/DistributedLock.Postgres.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
</PropertyGroup>

<PropertyGroup>
<Version>1.0.2</Version>
<Version>1.0.3</Version>
<AssemblyVersion>1.0.0.0</AssemblyVersion>
<Authors>Michael Adelson</Authors>
<Description>Provides a distributed lock implementation based on Postgresql</Description>
Expand All @@ -32,6 +32,9 @@
<TreatSpecificWarningsAsErrors />
<!-- see https://github.com/dotnet/sdk/issues/2679 -->
<DebugType>embedded</DebugType>
<!-- see https://mitchelsellers.com/blog/article/net-5-deterministic-builds-source-linking -->
<ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>
<EmbedUntrackedSources>true</EmbedUntrackedSources>
</PropertyGroup>

<PropertyGroup Condition="'$(Configuration)' == 'Debug'">
Expand Down
5 changes: 4 additions & 1 deletion DistributedLock.Redis/DistributedLock.Redis.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
</PropertyGroup>

<PropertyGroup>
<Version>1.0.1</Version>
<Version>1.0.2</Version>
<AssemblyVersion>1.0.0.0</AssemblyVersion>
<Authors>Michael Adelson</Authors>
<Description>Provides distributed locking primitives based on Redis</Description>
Expand All @@ -32,6 +32,9 @@
<TreatSpecificWarningsAsErrors />
<!-- see https://github.com/dotnet/sdk/issues/2679 -->
<DebugType>embedded</DebugType>
<!-- see https://mitchelsellers.com/blog/article/net-5-deterministic-builds-source-linking -->
<ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>
<EmbedUntrackedSources>true</EmbedUntrackedSources>
</PropertyGroup>

<PropertyGroup Condition="'$(Configuration)' == 'Debug'">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -145,12 +145,15 @@ internal static RedisDistributedLockOptions GetOptions(Action<RedisDistributedSy
TimeoutValue extensionCadence;
if (options?._extensionCadence is { } specifiedExtensionCadence)
{
// Note: we do not allow for disabling auto-extension here because it leads to traps
// where people might abandon the handle and then have it be closed due to GC.
// See discussion here: https://github.com/madelson/DistributedLock/issues/130.
if (specifiedExtensionCadence.CompareTo(minValidityTime) >= 0)
{
throw new ArgumentOutOfRangeException(
nameof(extensionCadence),
specifiedExtensionCadence.TimeSpan,
$"{nameof(extensionCadence)} must be less than {nameof(expiry)} ({expiry.TimeSpan}). To disable auto-extension, specify {nameof(Timeout)}.{nameof(Timeout.InfiniteTimeSpan)}"
$"{nameof(extensionCadence)} must be less than {nameof(expiry)} ({expiry.TimeSpan})"
);
}
extensionCadence = specifiedExtensionCadence;
Expand Down
5 changes: 4 additions & 1 deletion DistributedLock.SqlServer/DistributedLock.SqlServer.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
</PropertyGroup>

<PropertyGroup>
<Version>1.0.1</Version>
<Version>1.0.2</Version>
<AssemblyVersion>1.0.0.0</AssemblyVersion>
<Authors>Michael Adelson</Authors>
<Description>Provides a distributed lock implementation based on SQL Server</Description>
Expand All @@ -32,6 +32,9 @@
<TreatSpecificWarningsAsErrors />
<!-- see https://github.com/dotnet/sdk/issues/2679 -->
<DebugType>embedded</DebugType>
<!-- see https://mitchelsellers.com/blog/article/net-5-deterministic-builds-source-linking -->
<ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>
<EmbedUntrackedSources>true</EmbedUntrackedSources>
</PropertyGroup>

<PropertyGroup Condition="'$(Configuration)' == 'Debug'">
Expand Down
38 changes: 38 additions & 0 deletions DistributedLock.Tests/Tests/Core/Data/DatabaseConnectionTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using Medallion.Threading.Internal.Data;
using Medallion.Threading.SqlServer;
using Medallion.Threading.Tests.SqlServer;
using NUnit.Framework;
using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Medallion.Threading.Tests.Core.Data
{
public class DatabaseConnectionTest
{
/// <summary>
/// Reproduces the root cause of https://github.com/madelson/DistributedLock/issues/133
/// </summary>
[Test]
public async Task TestConnectionMonitorStaysSubscribedAfterClose()
{
var db = new TestingSqlServerDb { ApplicationName = nameof(TestConnectionMonitorStaysSubscribedAfterClose) };

await using var connection = new SqlDatabaseConnection(db.ConnectionString);

await connection.OpenAsync(CancellationToken.None);
connection.ConnectionMonitor.GetMonitoringHandle().Dispose(); // initialize monitoring
await connection.CloseAsync();

await connection.OpenAsync(CancellationToken.None);
using var handle = connection.ConnectionMonitor.GetMonitoringHandle();
Assert.IsFalse(handle.ConnectionLostToken.IsCancellationRequested);
await db.KillSessionsAsync(db.ApplicationName, idleSince: null);
Assert.IsTrue(await TestHelper.WaitForAsync(() => new(handle.ConnectionLostToken.IsCancellationRequested), timeout: TimeSpan.FromSeconds(5)));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

namespace Medallion.Threading.Tests.MySql
{
[Category("CI")]
public class MySqlConnectionOptionsBuilderTest
{
[Test]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

namespace Medallion.Threading.Tests.Oracle
{
[Category("CI")]
public class OracleConnectionOptionsBuilderTest
{
[Test]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

namespace Medallion.Threading.Tests.Postgres
{
[Category("CI")]
public class PostgresConnectionOptionsBuilderTest
{
[Test]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

namespace Medallion.Threading.Tests.SqlServer
{
[Category("CI")]
public class SqlConnectionOptionsBuilderTest
{
[Test]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Medallion.Threading.Tests.WaitHandles
Expand Down Expand Up @@ -47,7 +48,7 @@ public void TestMaxLengthNames()
public async Task TestGarbageCollection()
{
var @lock = CreateAsLock("gc_test", NameStyle.AddPrefix);
WeakReference AbandonLock() => new WeakReference(@lock.Acquire());
WeakReference AbandonLock() => new(@lock.Acquire());

var weakHandle = AbandonLock();
GC.Collect();
Expand Down Expand Up @@ -78,8 +79,70 @@ public void TestGetSafeLockNameCompat()
.ShouldEqual(@"Global\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxsrCnXZ1XHiT//dOSBfAU0iC4Gtnlr0dQACBUK8Ev2OdRYJ9jcvbiqVCv/rjyPemTW9AvOonkdr0B2bG04gmeYA==");
}

/// <summary>
/// Attempts to reproduce https://github.com/madelson/DistributedLock/issues/120.
///
/// NOTE: in practice this race condition is so slim that to reproduce with any reliability requires
/// adding a call to Thread.Sleep(1) at the start of WaitHandleExtensions.Resignal().
/// </summary>
[Test]
public async Task TestCancellationDoesNotLeadToLostSignal([Values] bool async)
{
var semaphore = new WaitHandleDistributedSemaphore(nameof(this.TestCancellationDoesNotLeadToLostSignal), 2);
await using var _ = await semaphore.AcquireAsync(TimeSpan.FromSeconds(1));

Random random = new();
for (var i = 0; i < 50; ++i)
{
using var blockingHandle = semaphore.TryAcquire(TimeSpan.Zero); // claim the last slot on the semaphore
Assert.IsNotNull(blockingHandle);

using CancellationTokenSource source = new();

using SemaphoreSlim acquiringEvent = new(initialCount: 0, maxCount: 1);
var acquireTask = Task.Run(async () =>
{
try
{
if (async)
{
var acquireHandleTask = semaphore.AcquireAsync(TimeSpan.FromSeconds(30), source.Token);
acquiringEvent.Release();
(await acquireHandleTask).Dispose();
}
else
{
acquiringEvent.Release();
semaphore.Acquire(TimeSpan.FromSeconds(30), source.Token).Dispose();
}
}
catch (OperationCanceledException) { }
});
await acquiringEvent.WaitAsync();
Assert.IsFalse(acquireTask.IsCompleted);

using Barrier barrier = new(participantCount: 2);
var releaseTask = Task.Run(() =>
{
barrier.SignalAndWait();
blockingHandle!.Dispose();
});
var cancelTask = Task.Run(() =>
{
barrier.SignalAndWait();
var yieldCount = random.Next(5, 25);
for (var i = 0; i < yieldCount; ++i) { Thread.Yield(); }
source.Cancel();
});
await Task.WhenAll(acquireTask, releaseTask, cancelTask);
}

await using var handle = await semaphore.TryAcquireAsync();
Assert.IsNotNull(handle); // if we lost even a single signal due to cancellation in the loop above, this will fail
}

private static WaitHandleDistributedSemaphore CreateAsLock(string name, NameStyle nameStyle) =>
new WaitHandleDistributedSemaphore(
new(
nameStyle == NameStyle.AddPrefix ? DistributedWaitHandleHelpers.GlobalPrefix + name : name,
maxCount: 1,
abandonmentCheckCadence: TimeSpan.FromSeconds(.3),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
</PropertyGroup>

<PropertyGroup>
<Version>1.0.0</Version>
<Version>1.0.1</Version>
<AssemblyVersion>1.0.0.0</AssemblyVersion>
<Authors>Michael Adelson</Authors>
<Description>Provides a distributed lock implementation based on global WaitHandle objects in Windows</Description>
Expand All @@ -32,6 +32,9 @@
<TreatSpecificWarningsAsErrors />
<!-- see https://github.com/dotnet/sdk/issues/2679 -->
<DebugType>embedded</DebugType>
<!-- see https://mitchelsellers.com/blog/article/net-5-deterministic-builds-source-linking -->
<ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>
<EmbedUntrackedSources>true</EmbedUntrackedSources>
</PropertyGroup>

<PropertyGroup Condition="'$(Configuration)' == 'Debug'">
Expand Down
Loading

0 comments on commit 7fb18b1

Please sign in to comment.