Use a hashset to keep track of acquired locks

This simplifies disposing/checking for locks greatly.
This commit is contained in:
Mole
2021-03-17 11:10:34 +01:00
parent d1afb6eabc
commit 54a4a76efb
2 changed files with 24 additions and 53 deletions

View File

@@ -37,6 +37,8 @@ namespace Umbraco.Core.Scoping
private IEventDispatcher _eventDispatcher;
private object _dictionaryLocker;
private readonly HashSet<int> _readLocks;
private readonly HashSet<int> _writeLocks;
// ReadLocks and WriteLocks if we're the outer most scope it's those owned by the entire chain
// If we're a child scope it's those that we have requested.
@@ -68,8 +70,10 @@ namespace Umbraco.Core.Scoping
Detachable = detachable;
_dictionaryLocker = new object();
ReadLocks = new Dictionary<Guid, Dictionary<int, int>>();
WriteLocks = new Dictionary<Guid, Dictionary<int, int>>();
ReadLocks = new Dictionary<Guid, Dictionary<int, int>>();
_writeLocks = new HashSet<int>();
_readLocks = new HashSet<int>();
#if DEBUG_SCOPES
_scopeProvider.RegisterScope(this);
@@ -370,8 +374,7 @@ namespace Umbraco.Core.Scoping
{
// We're the parent scope, make sure that locks of all scopes has been cleared
// Since we're only reading we don't have to be in a lock
if (ReadLocks.Values.Any(x => x.Values.Any(value => value != 0))
|| WriteLocks.Values.Any(x => x.Values.Any(value => value != 0)))
if (ReadLocks.Any() || WriteLocks.Any())
{
throw new InvalidOperationException($"All scopes has not been disposed from parent scope: {InstanceId}");
}
@@ -580,13 +583,10 @@ namespace Umbraco.Core.Scoping
{
lock (_dictionaryLocker)
{
// Reset all values to 0 since the scope is being disposed
// Remove the scope from the dictionary because it's getting disposed.
if (ReadLocks.ContainsKey(instanceId))
{
foreach (var key in ReadLocks[instanceId].Keys.ToList())
{
ReadLocks[instanceId][key] = 0;
}
ReadLocks.Remove(instanceId);
}
}
}
@@ -606,13 +606,10 @@ namespace Umbraco.Core.Scoping
{
lock (_dictionaryLocker)
{
// Reset all values to 0 since the scope is being disposed
// Remove the scope from the dictionary because it's getting disposed.
if (WriteLocks.ContainsKey(instanceID))
{
foreach (var key in WriteLocks[instanceID].Keys.ToList())
{
WriteLocks[instanceID][key] = 0;
}
WriteLocks.Remove(instanceID);
}
}
@@ -643,28 +640,6 @@ namespace Umbraco.Core.Scoping
WriteLockInner(InstanceId, timeout, lockId);
}
/// <summary>
/// Determine if a read lock with the specified ID has already been obtained.
/// </summary>
/// <param name="lockId">Id to test.</param>
/// <returns>True if no scopes has obtained a read lock with the specific ID yet.</returns>
private bool HasReadLock(int lockId)
{
// Check if there is any dictionary<int,int> with a key equal to lockId
// And check that the value associated with that key is greater than 0, if not it could be because a lock was requested but it failed.
return ReadLocks.Values.Any(x => x.ContainsKey(lockId));
}
/// <summary>
/// Determine if a write lock with the specified ID has already been obtained.
/// </summary>
/// <param name="lockId">Id to test</param>
/// <returns>>True if no scopes has obtained a write lock with the specific ID yet.</returns>
private bool HasWriteLock(int lockId)
{
return WriteLocks.Values.Any(x => x.ContainsKey(lockId));
}
/// <summary>
/// Handles acquiring a read lock, will delegate it to the parent if there are any.
/// </summary>
@@ -685,9 +660,10 @@ namespace Umbraco.Core.Scoping
foreach (var lockId in lockIds)
{
// Only acquire the lock if we haven't done so yet.
if (!HasReadLock(lockId))
if (!_readLocks.Contains(lockId))
{
IncrementReadLock(lockId, instanceId);
_readLocks.Add(lockId);
try
{
if (timeout is null)
@@ -707,6 +683,7 @@ namespace Umbraco.Core.Scoping
// Since we at this point have determined that we haven't got any key of LockID, it's safe to completely remove it instead of decrementing.
// It needs to be completely removed, because that's how we determine to acquire a lock.
ReadLocks[instanceId].Remove(lockId);
_readLocks.Remove(lockId);
throw;
}
}
@@ -738,9 +715,10 @@ namespace Umbraco.Core.Scoping
foreach (var lockId in lockIds)
{
// Only acquire lock if we haven't yet (WriteLocks not containing the key)
if (!HasWriteLock(lockId))
if (!_writeLocks.Contains(lockId))
{
IncrementWriteLock(lockId, instanceId);
_writeLocks.Add(lockId);
try
{
if (timeout is null)
@@ -758,6 +736,7 @@ namespace Umbraco.Core.Scoping
// Since we at this point have determined that we haven't got any key of LockID, it's safe to completely remove it instead of decrementing.
// It needs to be completely removed, because that's how we determine to acquire a lock.
WriteLocks[instanceId].Remove(lockId);
_writeLocks.Remove(lockId);
throw;
}
}