2021-01-21 01:22:18 +00:00
|
|
|
using System;
|
|
|
|
using System.Collections.Generic;
|
|
|
|
using System.Threading;
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
2021-01-21 23:33:53 +00:00
|
|
|
using KellermanSoftware.CompareNetObjects;
|
|
|
|
|
2021-01-21 01:22:18 +00:00
|
|
|
using Xunit;
|
2021-01-21 23:33:53 +00:00
|
|
|
using Xunit.Sdk;
|
2018-07-17 18:13:08 +00:00
|
|
|
|
|
|
|
namespace MfGames.Locking.Tests
|
|
|
|
{
|
|
|
|
public class LockTests
|
|
|
|
{
|
|
|
|
private readonly ReaderWriterLockSlim locker;
|
|
|
|
|
2021-01-21 23:33:53 +00:00
|
|
|
private readonly List<(int, LockAction)> sequenceRecord;
|
2018-07-17 18:13:08 +00:00
|
|
|
|
2021-01-21 23:33:53 +00:00
|
|
|
private readonly object syncLock;
|
2018-07-17 18:13:08 +00:00
|
|
|
|
2021-01-21 23:33:53 +00:00
|
|
|
public LockTests()
|
2018-07-17 18:13:08 +00:00
|
|
|
{
|
2021-01-21 23:33:53 +00:00
|
|
|
this.syncLock = new object();
|
|
|
|
this.sequenceRecord = new List<(int, LockAction)>();
|
2018-07-17 18:13:08 +00:00
|
|
|
this.locker = new ReaderWriterLockSlim();
|
|
|
|
}
|
|
|
|
|
|
|
|
[Fact]
|
|
|
|
public void ReadBlocksWrite()
|
|
|
|
{
|
|
|
|
Task.WaitAll(
|
2021-01-21 23:33:53 +00:00
|
|
|
Task.Run(() => this.TestRead(1, 5, 1)),
|
|
|
|
Task.Run(() => this.TestWrite(2, 2, 2)));
|
|
|
|
|
|
|
|
this.VerifySequenceRecord(
|
|
|
|
(1, LockAction.BeforeLock),
|
|
|
|
(1, LockAction.InLock),
|
|
|
|
(2, LockAction.BeforeLock),
|
|
|
|
(1, LockAction.AfterLock),
|
|
|
|
(2, LockAction.InLock),
|
|
|
|
(2, LockAction.AfterLock));
|
2018-07-17 18:13:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
[Fact]
|
|
|
|
public void ReadsDoNotBlockReads()
|
|
|
|
{
|
|
|
|
Task.WaitAll(
|
2021-01-22 00:38:24 +00:00
|
|
|
Task.Run(() => this.TestRead(1, 8, 1)),
|
2021-01-21 23:33:53 +00:00
|
|
|
Task.Run(() => this.TestRead(2, 1, 2)),
|
|
|
|
Task.Run(() => this.TestRead(4, 1, 3)));
|
|
|
|
|
|
|
|
this.VerifySequenceRecord(
|
|
|
|
(1, LockAction.BeforeLock),
|
|
|
|
(1, LockAction.InLock),
|
|
|
|
(2, LockAction.BeforeLock),
|
|
|
|
(2, LockAction.InLock),
|
|
|
|
(2, LockAction.AfterLock),
|
|
|
|
(3, LockAction.BeforeLock),
|
|
|
|
(3, LockAction.InLock),
|
|
|
|
(3, LockAction.AfterLock),
|
|
|
|
(1, LockAction.AfterLock));
|
2018-07-17 18:13:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
[Fact]
|
|
|
|
public void UpgradableBlocksUpgradable()
|
|
|
|
{
|
|
|
|
Task.WaitAll(
|
2021-01-22 00:45:09 +00:00
|
|
|
Task.Run(() => this.TestUpgradable(1, 10, 1)),
|
2021-01-21 23:33:53 +00:00
|
|
|
Task.Run(() => this.TestUpgradable(2, 2, 2)),
|
2021-01-22 00:45:09 +00:00
|
|
|
Task.Run(() => this.TestUpgradable(5, 1, 3)));
|
2021-01-21 23:33:53 +00:00
|
|
|
|
|
|
|
this.VerifySequenceRecord(
|
|
|
|
(1, LockAction.BeforeLock),
|
|
|
|
(1, LockAction.InLock),
|
|
|
|
(2, LockAction.BeforeLock),
|
|
|
|
(3, LockAction.BeforeLock),
|
|
|
|
(1, LockAction.AfterLock),
|
|
|
|
(2, LockAction.InLock),
|
|
|
|
(2, LockAction.AfterLock),
|
|
|
|
(3, LockAction.InLock),
|
|
|
|
(3, LockAction.AfterLock));
|
2018-07-17 18:13:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
[Fact]
|
|
|
|
public void UpgradableDoesNotBlockReads()
|
|
|
|
{
|
|
|
|
Task.WaitAll(
|
2021-01-21 23:33:53 +00:00
|
|
|
Task.Run(() => this.TestUpgradable(1, 6, 1)),
|
|
|
|
Task.Run(() => this.TestRead(2, 1, 2)),
|
|
|
|
Task.Run(() => this.TestRead(4, 1, 3)));
|
|
|
|
|
|
|
|
this.VerifySequenceRecord(
|
|
|
|
(1, LockAction.BeforeLock),
|
|
|
|
(1, LockAction.InLock),
|
|
|
|
(2, LockAction.BeforeLock),
|
|
|
|
(2, LockAction.InLock),
|
|
|
|
(2, LockAction.AfterLock),
|
|
|
|
(3, LockAction.BeforeLock),
|
|
|
|
(3, LockAction.InLock),
|
|
|
|
(3, LockAction.AfterLock),
|
|
|
|
(1, LockAction.AfterLock));
|
2018-07-17 18:13:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
[Fact]
|
|
|
|
public void WriteBlockRead()
|
|
|
|
{
|
|
|
|
Task.WaitAll(
|
2021-01-21 23:33:53 +00:00
|
|
|
Task.Run(() => this.TestWrite(1, 6, 1)),
|
|
|
|
Task.Run(() => this.TestRead(2, 1, 2)));
|
|
|
|
|
|
|
|
this.VerifySequenceRecord(
|
|
|
|
(1, LockAction.BeforeLock),
|
|
|
|
(1, LockAction.InLock),
|
|
|
|
(2, LockAction.BeforeLock),
|
|
|
|
(1, LockAction.AfterLock),
|
|
|
|
(2, LockAction.InLock),
|
|
|
|
(2, LockAction.AfterLock));
|
2018-07-17 18:13:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
[Fact]
|
|
|
|
public void WriteBlockReads()
|
|
|
|
{
|
|
|
|
Task.WaitAll(
|
2021-01-21 23:33:53 +00:00
|
|
|
Task.Run(() => this.TestWrite(1, 6, 1)),
|
|
|
|
Task.Run(() => this.TestRead(2, 1, 2)));
|
|
|
|
|
|
|
|
this.VerifySequenceRecord(
|
|
|
|
(1, LockAction.BeforeLock),
|
|
|
|
(1, LockAction.InLock),
|
|
|
|
(2, LockAction.BeforeLock),
|
|
|
|
(1, LockAction.AfterLock),
|
|
|
|
(2, LockAction.InLock),
|
|
|
|
(2, LockAction.AfterLock));
|
2018-07-17 18:13:08 +00:00
|
|
|
}
|
|
|
|
|
2021-01-21 23:33:53 +00:00
|
|
|
private void RecordSequenceAction(int sequence, LockAction action)
|
2018-07-17 18:13:08 +00:00
|
|
|
{
|
2021-01-21 23:33:53 +00:00
|
|
|
lock (this.syncLock)
|
2018-07-17 18:13:08 +00:00
|
|
|
{
|
2021-01-21 23:33:53 +00:00
|
|
|
this.sequenceRecord.Add((sequence, action));
|
2018-07-17 18:13:08 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-21 23:33:53 +00:00
|
|
|
private void Test(
|
|
|
|
Func<ReaderWriterLockSlim, IDisposable> getLock,
|
|
|
|
int settleTocks,
|
|
|
|
int waitTocks,
|
2018-07-17 18:13:08 +00:00
|
|
|
int sequence)
|
|
|
|
{
|
2021-01-22 00:45:09 +00:00
|
|
|
const int TocksInMilliseconds = 100;
|
|
|
|
|
|
|
|
Thread.Sleep(TocksInMilliseconds * settleTocks);
|
2018-07-17 18:13:08 +00:00
|
|
|
|
2021-01-21 23:33:53 +00:00
|
|
|
this.RecordSequenceAction(sequence, LockAction.BeforeLock);
|
2018-07-17 18:13:08 +00:00
|
|
|
|
2021-01-21 23:33:53 +00:00
|
|
|
using (getLock(this.locker))
|
|
|
|
{
|
|
|
|
Thread.Sleep(10);
|
2018-07-17 18:13:08 +00:00
|
|
|
|
2021-01-21 23:33:53 +00:00
|
|
|
this.RecordSequenceAction(sequence, LockAction.InLock);
|
2018-07-17 18:13:08 +00:00
|
|
|
|
2021-01-22 00:45:09 +00:00
|
|
|
Thread.Sleep(TocksInMilliseconds * waitTocks);
|
2018-07-17 18:13:08 +00:00
|
|
|
|
2021-01-21 23:33:53 +00:00
|
|
|
this.RecordSequenceAction(sequence, LockAction.AfterLock);
|
2018-07-17 18:13:08 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-21 23:33:53 +00:00
|
|
|
private void TestRead(
|
|
|
|
int settleTocks,
|
|
|
|
int waitTocks,
|
2018-07-17 18:13:08 +00:00
|
|
|
int sequence)
|
|
|
|
{
|
2021-01-21 23:33:53 +00:00
|
|
|
this.Test(
|
|
|
|
x => new ReadLock(x),
|
|
|
|
settleTocks,
|
|
|
|
waitTocks,
|
|
|
|
sequence);
|
2018-07-17 18:13:08 +00:00
|
|
|
}
|
|
|
|
|
2021-01-21 23:33:53 +00:00
|
|
|
private void TestUpgradable(
|
|
|
|
int settleTocks,
|
|
|
|
int waitTocks,
|
2018-07-17 18:13:08 +00:00
|
|
|
int sequence)
|
|
|
|
{
|
2021-01-21 23:33:53 +00:00
|
|
|
this.Test(
|
|
|
|
x => new UpgradableLock(x),
|
|
|
|
settleTocks,
|
|
|
|
waitTocks,
|
|
|
|
sequence);
|
2018-07-17 18:13:08 +00:00
|
|
|
}
|
|
|
|
|
2021-01-21 23:33:53 +00:00
|
|
|
private void TestWrite(
|
|
|
|
int settleTocks,
|
|
|
|
int waitTocks,
|
|
|
|
int sequence)
|
2018-07-17 18:13:08 +00:00
|
|
|
{
|
2021-01-21 23:33:53 +00:00
|
|
|
this.Test(
|
|
|
|
x => new WriteLock(x),
|
|
|
|
settleTocks,
|
|
|
|
waitTocks,
|
|
|
|
sequence);
|
|
|
|
}
|
2018-07-17 18:13:08 +00:00
|
|
|
|
2021-01-21 23:33:53 +00:00
|
|
|
private void VerifySequenceRecord(
|
|
|
|
params (int, LockAction BeforeLock)[] values)
|
|
|
|
{
|
|
|
|
lock (this.syncLock)
|
2018-07-17 18:13:08 +00:00
|
|
|
{
|
2021-01-21 23:33:53 +00:00
|
|
|
var compareLogic = new CompareLogic
|
|
|
|
{
|
|
|
|
Config =
|
|
|
|
{
|
|
|
|
IgnoreObjectTypes = true,
|
|
|
|
MaxDifferences = 2,
|
|
|
|
}
|
|
|
|
};
|
|
|
|
ComparisonResult result = compareLogic.Compare(
|
|
|
|
values,
|
|
|
|
this.sequenceRecord);
|
|
|
|
|
|
|
|
if (!result.AreEqual)
|
|
|
|
{
|
|
|
|
throw new XunitException(result.DifferencesString);
|
|
|
|
}
|
2018-07-17 18:13:08 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|