-
-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added a simple, non-keyed async lock through AsyncNonKeyedLocker.
- Loading branch information
1 parent
cb7355c
commit 6585036
Showing
6 changed files
with
601 additions
and
4 deletions.
There are no files selected for viewing
166 changes: 166 additions & 0 deletions
166
AsyncKeyedLock.Tests/AsyncNonKeyedLockerTests/OriginalTests.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,166 @@ | ||
using FluentAssertions; | ||
using Xunit; | ||
|
||
namespace AsyncKeyedLock.Tests.AsyncNonKeyedLockerTests | ||
{ | ||
public class OriginalTests | ||
{ | ||
[Fact] | ||
public void TestLock() | ||
{ | ||
var asyncNonKeyedLocker = new AsyncNonKeyedLocker(); | ||
Assert.Equal(0, asyncNonKeyedLocker.GetRemainingCount()); | ||
Assert.Equal(1, asyncNonKeyedLocker.GetCurrentCount()); | ||
using (asyncNonKeyedLocker.Lock()) | ||
{ | ||
Assert.Equal(1, asyncNonKeyedLocker.GetRemainingCount()); | ||
Assert.Equal(0, asyncNonKeyedLocker.GetCurrentCount()); | ||
} | ||
Assert.Equal(0, asyncNonKeyedLocker.GetRemainingCount()); | ||
Assert.Equal(1, asyncNonKeyedLocker.GetCurrentCount()); | ||
} | ||
|
||
[Fact] | ||
public void TestLockAndCancellationToken() | ||
{ | ||
var asyncNonKeyedLocker = new AsyncNonKeyedLocker(); | ||
Assert.Equal(0, asyncNonKeyedLocker.GetRemainingCount()); | ||
Assert.Equal(1, asyncNonKeyedLocker.GetCurrentCount()); | ||
using (asyncNonKeyedLocker.Lock(CancellationToken.None)) | ||
{ | ||
Assert.Equal(1, asyncNonKeyedLocker.GetRemainingCount()); | ||
Assert.Equal(0, asyncNonKeyedLocker.GetCurrentCount()); | ||
} | ||
Assert.Equal(0, asyncNonKeyedLocker.GetRemainingCount()); | ||
Assert.Equal(1, asyncNonKeyedLocker.GetCurrentCount()); | ||
} | ||
|
||
[Fact] | ||
public void TestLockAndCancelledCancellationToken() | ||
{ | ||
Action action = () => | ||
{ | ||
var asyncNonKeyedLocker = new AsyncNonKeyedLocker(); | ||
using (asyncNonKeyedLocker.Lock(new CancellationToken(true))) | ||
{ } | ||
}; | ||
action.Should().Throw<OperationCanceledException>(); | ||
} | ||
|
||
[Fact] | ||
public void TestLockAndMillisecondsTimeout() | ||
{ | ||
var asyncNonKeyedLocker = new AsyncNonKeyedLocker(); | ||
Assert.Equal(0, asyncNonKeyedLocker.GetRemainingCount()); | ||
Assert.Equal(1, asyncNonKeyedLocker.GetCurrentCount()); | ||
using (var myLock = asyncNonKeyedLocker.Lock(0, out bool entered)) | ||
{ | ||
Assert.True(entered); | ||
Assert.True(myLock.EnteredSemaphore); | ||
Assert.Equal(1, asyncNonKeyedLocker.GetRemainingCount()); | ||
Assert.Equal(0, asyncNonKeyedLocker.GetCurrentCount()); | ||
using (var myLock2 = asyncNonKeyedLocker.Lock(0, out entered)) | ||
{ | ||
Assert.False(entered); | ||
Assert.False(myLock2.EnteredSemaphore); | ||
} | ||
} | ||
Assert.Equal(0, asyncNonKeyedLocker.GetRemainingCount()); | ||
Assert.Equal(1, asyncNonKeyedLocker.GetCurrentCount()); | ||
} | ||
|
||
[Fact] | ||
public void TestLockAndTimeout() | ||
{ | ||
var asyncNonKeyedLocker = new AsyncNonKeyedLocker(); | ||
Assert.Equal(0, asyncNonKeyedLocker.GetRemainingCount()); | ||
Assert.Equal(1, asyncNonKeyedLocker.GetCurrentCount()); | ||
using (var myLock = asyncNonKeyedLocker.Lock(TimeSpan.FromMilliseconds(0), out bool entered)) | ||
{ | ||
Assert.True(entered); | ||
Assert.True(myLock.EnteredSemaphore); | ||
Assert.Equal(1, asyncNonKeyedLocker.GetRemainingCount()); | ||
Assert.Equal(0, asyncNonKeyedLocker.GetCurrentCount()); | ||
using (var myLock2 = asyncNonKeyedLocker.Lock(TimeSpan.FromMilliseconds(0), out entered)) | ||
{ | ||
Assert.False(entered); | ||
Assert.False(myLock2.EnteredSemaphore); | ||
} | ||
} | ||
Assert.Equal(0, asyncNonKeyedLocker.GetRemainingCount()); | ||
Assert.Equal(1, asyncNonKeyedLocker.GetCurrentCount()); | ||
} | ||
|
||
[Fact] | ||
public void TestLockAndMillisecondsTimeoutAndCancellationToken() | ||
{ | ||
var asyncNonKeyedLocker = new AsyncNonKeyedLocker(); | ||
Assert.Equal(0, asyncNonKeyedLocker.GetRemainingCount()); | ||
Assert.Equal(1, asyncNonKeyedLocker.GetCurrentCount()); | ||
using (var myLock = asyncNonKeyedLocker.Lock(0, CancellationToken.None, out bool entered)) | ||
{ | ||
Assert.True(entered); | ||
Assert.True(myLock.EnteredSemaphore); | ||
Assert.Equal(1, asyncNonKeyedLocker.GetRemainingCount()); | ||
Assert.Equal(0, asyncNonKeyedLocker.GetCurrentCount()); | ||
using (var myLock2 = asyncNonKeyedLocker.Lock(0, CancellationToken.None, out entered)) | ||
{ | ||
Assert.False(entered); | ||
Assert.False(myLock2.EnteredSemaphore); | ||
} | ||
} | ||
Assert.Equal(0, asyncNonKeyedLocker.GetRemainingCount()); | ||
Assert.Equal(1, asyncNonKeyedLocker.GetCurrentCount()); | ||
} | ||
|
||
[Fact] | ||
public void TestLockAndMillisecondsTimeoutAndCancelledCancellationToken() | ||
{ | ||
bool entered = false; | ||
Action action = () => | ||
{ | ||
var asyncNonKeyedLocker = new AsyncNonKeyedLocker(); | ||
using (asyncNonKeyedLocker.Lock(0, new CancellationToken(true), out entered)) | ||
{ } | ||
}; | ||
action.Should().Throw<OperationCanceledException>(); | ||
entered.Should().BeFalse(); | ||
} | ||
|
||
[Fact] | ||
public void TestLockAndTimeoutAndCancellationToken() | ||
{ | ||
var asyncNonKeyedLocker = new AsyncNonKeyedLocker(); | ||
Assert.Equal(0, asyncNonKeyedLocker.GetRemainingCount()); | ||
Assert.Equal(1, asyncNonKeyedLocker.GetCurrentCount()); | ||
using (var myLock = asyncNonKeyedLocker.Lock(TimeSpan.FromMilliseconds(0), CancellationToken.None, out bool entered)) | ||
{ | ||
Assert.True(entered); | ||
Assert.True(myLock.EnteredSemaphore); | ||
Assert.Equal(1, asyncNonKeyedLocker.GetRemainingCount()); | ||
Assert.Equal(0, asyncNonKeyedLocker.GetCurrentCount()); | ||
using (var myLock2 = asyncNonKeyedLocker.Lock(TimeSpan.FromMilliseconds(0), CancellationToken.None, out entered)) | ||
{ | ||
Assert.False(entered); | ||
Assert.False(myLock2.EnteredSemaphore); | ||
} | ||
} | ||
Assert.Equal(0, asyncNonKeyedLocker.GetRemainingCount()); | ||
Assert.Equal(1, asyncNonKeyedLocker.GetCurrentCount()); | ||
} | ||
|
||
[Fact] | ||
public void TestLockAndTimeoutAndCancelledCancellationToken() | ||
{ | ||
bool entered = false; | ||
Action action = () => | ||
{ | ||
var asyncNonKeyedLocker = new AsyncNonKeyedLocker(); | ||
using (asyncNonKeyedLocker.Lock(TimeSpan.FromMilliseconds(0), new CancellationToken(true), out entered)) | ||
{ } | ||
}; | ||
action.Should().Throw<OperationCanceledException>(); | ||
entered.Should().BeFalse(); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
using System; | ||
using System.Runtime.CompilerServices; | ||
using System.Threading; | ||
|
||
namespace AsyncKeyedLock | ||
{ | ||
/// <summary> | ||
/// Represents an <see cref="IDisposable"/> for AsyncNonKeyedLocker. | ||
/// </summary> | ||
public readonly struct AsyncNonKeyedLockReleaser : IDisposable | ||
{ | ||
private readonly SemaphoreSlim _semaphoreSlim; | ||
|
||
/// <summary> | ||
/// The exposed <see cref="SemaphoreSlim"/> instance used to limit the number of threads that can access the lock concurrently. | ||
/// </summary> | ||
public readonly SemaphoreSlim SemaphoreSlim => _semaphoreSlim; | ||
|
||
internal AsyncNonKeyedLockReleaser(SemaphoreSlim semaphoreSlim) | ||
{ | ||
_semaphoreSlim = semaphoreSlim; | ||
} | ||
|
||
/// <summary> | ||
/// Releases the <see cref="SemaphoreSlim"/> object once. | ||
/// </summary> | ||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | ||
public readonly void Dispose() | ||
{ | ||
_semaphoreSlim.Release(); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
using System; | ||
using System.Runtime.CompilerServices; | ||
using System.Threading; | ||
|
||
namespace AsyncKeyedLock | ||
{ | ||
/// <summary> | ||
/// Represents an <see cref="IDisposable"/> for AsyncNonKeyedLocker with timeouts. | ||
/// </summary> | ||
public readonly struct AsyncNonKeyedLockTimeoutReleaser : IDisposable | ||
{ | ||
private readonly bool _enteredSemaphore; | ||
|
||
/// <summary> | ||
/// True if the timeout was reached, false if not. | ||
/// </summary> | ||
public readonly bool EnteredSemaphore => _enteredSemaphore; | ||
|
||
private readonly SemaphoreSlim _semaphoreSlim; | ||
|
||
/// <summary> | ||
/// The exposed <see cref="SemaphoreSlim"/> instance used to limit the number of threads that can access the lock concurrently. | ||
/// </summary> | ||
public readonly SemaphoreSlim SemaphoreSlim => _semaphoreSlim; | ||
|
||
internal AsyncNonKeyedLockTimeoutReleaser(SemaphoreSlim semaphoreSlim, bool enteredSemaphore) | ||
{ | ||
_enteredSemaphore = enteredSemaphore; | ||
_semaphoreSlim = semaphoreSlim; | ||
} | ||
|
||
/// <summary> | ||
/// Releases the <see cref="SemaphoreSlim"/> object once, depending on whether or not the semaphore was entered. | ||
/// </summary> | ||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | ||
public readonly void Dispose() | ||
{ | ||
if (_enteredSemaphore) | ||
{ | ||
_semaphoreSlim.Release(); | ||
} | ||
} | ||
} | ||
} |
Oops, something went wrong.