Skip to content

Commit

Permalink
Add IAsyncLock implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
georgii-borovinskikh-sonarsource committed Feb 16, 2024
1 parent 45b898d commit 205963a
Show file tree
Hide file tree
Showing 3 changed files with 183 additions and 0 deletions.
48 changes: 48 additions & 0 deletions src/Infrastructure.VS.UnitTests/AsyncLockFactoryTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* SonarLint for Visual Studio
* Copyright (C) 2016-2024 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

using FluentAssertions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using SonarLint.VisualStudio.Core.Synchronization;
using SonarLint.VisualStudio.TestInfrastructure;

namespace SonarLint.VisualStudio.Infrastructure.VS.UnitTests;

[TestClass]
public class AsyncLockFactoryTests
{
[TestMethod]
public void MefCtor_CheckIsExported()
{
MefTestHelpers.CheckTypeCanBeImported<AsyncLockFactory, IAsyncLockFactory>();
}

[TestMethod]
public void MefCtor_CheckIsSingleton()
{
MefTestHelpers.CheckIsSingletonMefComponent<AsyncLockFactory>();
}

[TestMethod]
public void Create_ReturnsValue()
{
new AsyncLockFactory().Create().Should().NotBeNull();
}
}
58 changes: 58 additions & 0 deletions src/Infrastructure.VS.UnitTests/AsyncLockTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* SonarLint for Visual Studio
* Copyright (C) 2016-2024 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using FluentAssertions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using SonarLint.VisualStudio.Core.Synchronization;

namespace SonarLint.VisualStudio.Infrastructure.VS.UnitTests;

[TestClass]
public class AsyncLockTests
{
[TestMethod]
public async Task SmokeTest()
{
var testSubject = new AsyncLock();

var numbers = new List<int> { 0 };
var threadNumbers = new List<int>();

await Task.WhenAll(Enumerable.Range(0, 10).Select(number => Task.Run(() => AddIncrement(numbers, threadNumbers, number, testSubject))));

threadNumbers.Should().NotBeAscendingInOrder(); // checks multiple threads actually ran in parallel and not in sequence
numbers.Should().BeInAscendingOrder(); // checks threads were correctly synchronized
}

private static async Task AddIncrement(List<int> numbers, List<int> threadNumbers, int assignedThreadNumber, IAsyncLock asyncLock)
{
for (var i = 0; i < 10000; i++)
{
using (await asyncLock.AcquireAsync())
{
threadNumbers.Add(assignedThreadNumber);
numbers.Add(numbers[numbers.Count - 1] + 1);
}
}
}
}
77 changes: 77 additions & 0 deletions src/Infrastructure.VS/AsyncLock.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*
* SonarLint for Visual Studio
* Copyright (C) 2016-2024 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

using System.ComponentModel.Composition;
using System.Diagnostics.CodeAnalysis;
using System.Threading;
using System.Threading.Tasks;
using SonarLint.VisualStudio.Core.Synchronization;

namespace SonarLint.VisualStudio.Infrastructure.VS;


[Export(typeof(IAsyncLockFactory))]
[PartCreationPolicy(CreationPolicy.Shared)]
internal class AsyncLockFactory : IAsyncLockFactory
{
public IAsyncLock Create()
{
return new AsyncLock();
}
}

[ExcludeFromCodeCoverage]
internal sealed class AsyncLock : IAsyncLock
{
private readonly SemaphoreSlim semaphoreSlim = new (1, 1);

public IReleaseAsyncLock Acquire()
{
semaphoreSlim.Wait();

return new AsyncLockToken(this);
}

public async Task<IReleaseAsyncLock> AcquireAsync()
{
await semaphoreSlim.WaitAsync();

return new AsyncLockToken(this);
}

private void Release() => semaphoreSlim.Release();

public void Dispose() => semaphoreSlim.Dispose();

private sealed class AsyncLockToken : IReleaseAsyncLock
{
private readonly AsyncLock asyncLock;

public AsyncLockToken(AsyncLock asyncLock)
{
this.asyncLock = asyncLock;
}

public void Dispose()
{
asyncLock.Release();
}
}
}

0 comments on commit 205963a

Please sign in to comment.