Fix ContentStore locking exceptions in async code (#17246)

* Add ContentCache test

* Use SemaphoreSlim as write lock

* Apply lock imrpovements to SnapDictionary

* Obsolete unused MonitorLock

(cherry picked from commit c3db3457e7)
This commit is contained in:
Ronald Barendse
2024-10-11 09:45:01 +02:00
committed by Bjarke Berg
parent b814608c06
commit 30b114d538
4 changed files with 136 additions and 51 deletions

View File

@@ -52,9 +52,9 @@ public class ContentStore
// SnapDictionary has unit tests to ensure it all works correctly
// For locking information, see SnapDictionary
private readonly IPublishedSnapshotAccessor _publishedSnapshotAccessor;
private readonly object _rlocko = new();
private readonly IVariationContextAccessor _variationContextAccessor;
private readonly object _wlocko = new();
private readonly object _rlocko = new();
private readonly SemaphoreSlim _writeLock = new(1);
private Task? _collectTask;
private GenObj? _genObj;
private long _liveGen;
@@ -319,7 +319,7 @@ public class ContentStore
private void EnsureLocked()
{
if (!Monitor.IsEntered(_wlocko))
if (_writeLock.CurrentCount != 0)
{
throw new InvalidOperationException("Write lock must be acquried.");
}
@@ -327,14 +327,16 @@ public class ContentStore
private void Lock(WriteLockInfo lockInfo, bool forceGen = false)
{
if (Monitor.IsEntered(_wlocko))
if (_writeLock.CurrentCount == 0)
{
throw new InvalidOperationException("Recursive locks not allowed");
}
Monitor.TryEnter(_wlocko, _monitorTimeout, ref lockInfo.Taken);
if (Monitor.IsEntered(_wlocko) is false)
if (_writeLock.Wait(_monitorTimeout))
{
lockInfo.Taken = true;
}
else
{
throw new TimeoutException("Could not enter monitor before timeout in content store");
}
@@ -344,6 +346,7 @@ public class ContentStore
// see SnapDictionary
try
{
// Run all code in finally to ensure ThreadAbortException does not interrupt execution
}
finally
{
@@ -374,6 +377,7 @@ public class ContentStore
// see SnapDictionary
try
{
// Run all code in finally to ensure ThreadAbortException does not interrupt execution
}
finally
{
@@ -409,7 +413,7 @@ public class ContentStore
{
if (lockInfo.Taken)
{
Monitor.Exit(_wlocko);
_writeLock.Release();
}
}
}
@@ -1817,7 +1821,7 @@ public class ContentStore
// else we need to try to create a new gen ref
// whether we are wlocked or not, noone can rlock while we do,
// so _liveGen and _nextGen are safe
if (Monitor.IsEntered(_wlocko))
if (_writeLock.CurrentCount == 0)
{
// write-locked, cannot use latest gen (at least 1) so use previous
var snapGen = _nextGen ? _liveGen - 1 : _liveGen;
@@ -1829,8 +1833,7 @@ public class ContentStore
}
else if (_genObj.Gen != snapGen)
{
throw new PanicException(
$"The generation {_genObj.Gen} does not equal the snapshot generation {snapGen}");
throw new PanicException($"The generation {_genObj.Gen} does not equal the snapshot generation {snapGen}");
}
}
else