Skip to content

Commit

Permalink
Better Fallback Handling for Weird Locker Helper Disposed Issue
Browse files Browse the repository at this point in the history
  • Loading branch information
DelnarErsike committed Mar 9, 2024
1 parent dec7d11 commit d7c1864
Show file tree
Hide file tree
Showing 2 changed files with 37 additions and 42 deletions.
71 changes: 35 additions & 36 deletions Chummer/Backend/Helpers/AsyncFriendlyReaderWriterLock.cs
Original file line number Diff line number Diff line change
Expand Up @@ -174,8 +174,8 @@ LinkedAsyncRWLockHelper> GetHelpers(CancellationToken token = default)
{
token.ThrowIfCancellationRequested();
LinkedAsyncRWLockHelper objCurrentHelper = _objTopLevelHelper;
LinkedAsyncRWLockHelper objTopMostHeldUReader;
LinkedAsyncRWLockHelper objTopMostHeldWriter;
LinkedAsyncRWLockHelper objTopMostHeldUReader = null;
LinkedAsyncRWLockHelper objTopMostHeldWriter = null;
LinkedAsyncRWLockHelper objNextHelper;
#if ASYNCLOCALWRITEDEBUG
string strLastWriteStacktrace = string.Empty;
Expand All @@ -189,38 +189,43 @@ LinkedAsyncRWLockHelper> GetHelpers(CancellationToken token = default)
{
// Emergency exit for odd cases where, for some reason, AsyncLocal assignment does not happen (in the right place?) when a locker release is disposed
Utils.BreakIfDebug();
// Let's just get the helpers of the top-level lock. If this causes problems, it's because of the above-mentioned comment around AsyncLocal assignment
#if ASYNCLOCALWRITEDEBUG
// Let's just get the first ancestor lock that is not disposed. If this causes problems, it's because of the above-mentioned comment around AsyncLocal assignment
while (objCurrentHelper != null && objCurrentHelper.IsDisposed)
objCurrentHelper = objCurrentHelper.ParentLinkedHelper;
while (objTopMostHeldUReader != null && objTopMostHeldUReader.IsDisposed)
objTopMostHeldUReader = objTopMostHeldUReader.ParentLinkedHelper;
while (objTopMostHeldWriter != null && objTopMostHeldWriter.IsDisposed)
objTopMostHeldWriter = objTopMostHeldWriter.ParentLinkedHelper;
_objAsyncLocalCurrentsContainer.Value =
new Tuple<LinkedAsyncRWLockHelper, LinkedAsyncRWLockHelper, LinkedAsyncRWLockHelper, string>(_objTopLevelHelper, null, null, Environment.StackTrace);
#if ASYNCLOCALWRITEDEBUG
new Tuple<LinkedAsyncRWLockHelper, LinkedAsyncRWLockHelper, LinkedAsyncRWLockHelper, string>(objCurrentHelper, objTopMostHeldUReader, objTopMostHeldWriter, Environment.StackTrace);
#else
_objAsyncLocalCurrentsContainer.Value =
new Tuple<LinkedAsyncRWLockHelper, LinkedAsyncRWLockHelper, LinkedAsyncRWLockHelper>(_objTopLevelHelper, null, null);
new Tuple<LinkedAsyncRWLockHelper, LinkedAsyncRWLockHelper, LinkedAsyncRWLockHelper>(objCurrentHelper, objTopMostHeldUReader, objTopMostHeldWriter);
#endif
objCurrentHelper = _objTopLevelHelper;
objTopMostHeldUReader = null;
objTopMostHeldWriter = null;
objNextHelper = new LinkedAsyncRWLockHelper(objCurrentHelper);
}
else
{
#if ASYNCLOCALWRITEDEBUG
Tuple<LinkedAsyncRWLockHelper, LinkedAsyncRWLockHelper, LinkedAsyncRWLockHelper, string>
Tuple<LinkedAsyncRWLockHelper, LinkedAsyncRWLockHelper, LinkedAsyncRWLockHelper, string>
#else
Tuple<LinkedAsyncRWLockHelper, LinkedAsyncRWLockHelper, LinkedAsyncRWLockHelper>
Tuple<LinkedAsyncRWLockHelper, LinkedAsyncRWLockHelper, LinkedAsyncRWLockHelper>
#endif
objAsyncLocals = _objAsyncLocalCurrentsContainer.Value;
if (objAsyncLocals != null)
{
objAsyncLocals = _objAsyncLocalCurrentsContainer.Value;
if (objAsyncLocals != null)
{
#if ASYNCLOCALWRITEDEBUG
(objCurrentHelper, objTopMostHeldUReader, objTopMostHeldWriter, strLastWriteStacktrace) = objAsyncLocals;
(objCurrentHelper, objTopMostHeldUReader, objTopMostHeldWriter, strLastWriteStacktrace)
#else
(objCurrentHelper, objTopMostHeldUReader, objTopMostHeldWriter) = objAsyncLocals;
(objCurrentHelper, objTopMostHeldUReader, objTopMostHeldWriter)
#endif
}
else
{
objCurrentHelper = _objTopLevelHelper;
objTopMostHeldUReader = null;
objTopMostHeldWriter = null;
= objAsyncLocals;
}
else
{
objCurrentHelper = _objTopLevelHelper;
objTopMostHeldUReader = null;
objTopMostHeldWriter = null;
}
}

if (objCurrentHelper.IsDisposed)
Expand Down Expand Up @@ -270,12 +275,11 @@ public IDisposable EnterWriteLock(CancellationToken token = default)
objCurrentHelper.TakeWriteLock(objTopMostHeldWriter, token);
try
{
#if ASYNCLOCALWRITEDEBUG
_objAsyncLocalCurrentsContainer.Value =
#if ASYNCLOCALWRITEDEBUG
new Tuple<LinkedAsyncRWLockHelper, LinkedAsyncRWLockHelper, LinkedAsyncRWLockHelper, string>(
objNextHelper, objTopMostHeldUReader, objCurrentHelper, Environment.StackTrace);
#else
_objAsyncLocalCurrentsContainer.Value =
new Tuple<LinkedAsyncRWLockHelper, LinkedAsyncRWLockHelper, LinkedAsyncRWLockHelper>(
objNextHelper, objTopMostHeldUReader, objCurrentHelper);
#endif
Expand All @@ -299,12 +303,11 @@ public IDisposable EnterWriteLock(CancellationToken token = default)
}
catch
{
#if ASYNCLOCALWRITEDEBUG
_objAsyncLocalCurrentsContainer.Value =
#if ASYNCLOCALWRITEDEBUG
new Tuple<LinkedAsyncRWLockHelper, LinkedAsyncRWLockHelper, LinkedAsyncRWLockHelper, string>(
objCurrentHelper, objTopMostHeldUReader, objTopMostHeldWriter, Environment.StackTrace);
#else
_objAsyncLocalCurrentsContainer.Value =
new Tuple<LinkedAsyncRWLockHelper, LinkedAsyncRWLockHelper, LinkedAsyncRWLockHelper>(
objCurrentHelper, objTopMostHeldUReader, objTopMostHeldWriter);
#endif
Expand Down Expand Up @@ -372,12 +375,11 @@ public Task<IAsyncDisposable> EnterWriteLockAsync(CancellationToken token = defa
return Task.FromException<IAsyncDisposable>(e);
}

#if ASYNCLOCALWRITEDEBUG
_objAsyncLocalCurrentsContainer.Value =
#if ASYNCLOCALWRITEDEBUG
new Tuple<LinkedAsyncRWLockHelper, LinkedAsyncRWLockHelper, LinkedAsyncRWLockHelper, string>(
objNextHelper, objTopMostHeldUReader, objCurrentHelper, Environment.StackTrace);
#else
_objAsyncLocalCurrentsContainer.Value =
new Tuple<LinkedAsyncRWLockHelper, LinkedAsyncRWLockHelper, LinkedAsyncRWLockHelper>(
objNextHelper, objTopMostHeldUReader, objCurrentHelper);
#endif
Expand Down Expand Up @@ -465,12 +467,11 @@ public IDisposable EnterUpgradeableReadLock(CancellationToken token = default)
objCurrentHelper.TakeUpgradeableReadLock(token);
try
{
#if ASYNCLOCALWRITEDEBUG
_objAsyncLocalCurrentsContainer.Value =
#if ASYNCLOCALWRITEDEBUG
new Tuple<LinkedAsyncRWLockHelper, LinkedAsyncRWLockHelper, LinkedAsyncRWLockHelper, string>(
objNextHelper, objCurrentHelper, objTopMostHeldWriter, Environment.StackTrace);
#else
_objAsyncLocalCurrentsContainer.Value =
new Tuple<LinkedAsyncRWLockHelper, LinkedAsyncRWLockHelper, LinkedAsyncRWLockHelper>(
objNextHelper, objCurrentHelper, objTopMostHeldWriter);
#endif
Expand All @@ -495,12 +496,11 @@ public IDisposable EnterUpgradeableReadLock(CancellationToken token = default)
}
catch
{
#if ASYNCLOCALWRITEDEBUG
_objAsyncLocalCurrentsContainer.Value =
#if ASYNCLOCALWRITEDEBUG
new Tuple<LinkedAsyncRWLockHelper, LinkedAsyncRWLockHelper, LinkedAsyncRWLockHelper, string>(
objCurrentHelper, objTopMostHeldUReader, objTopMostHeldWriter, Environment.StackTrace);
#else
_objAsyncLocalCurrentsContainer.Value =
new Tuple<LinkedAsyncRWLockHelper, LinkedAsyncRWLockHelper, LinkedAsyncRWLockHelper>(
objCurrentHelper, objTopMostHeldUReader, objTopMostHeldWriter);
#endif
Expand Down Expand Up @@ -566,12 +566,11 @@ public Task<IAsyncDisposable> EnterUpgradeableReadLockAsync(CancellationToken to
return Task.FromException<IAsyncDisposable>(e);
}

#if ASYNCLOCALWRITEDEBUG
_objAsyncLocalCurrentsContainer.Value =
#if ASYNCLOCALWRITEDEBUG
new Tuple<LinkedAsyncRWLockHelper, LinkedAsyncRWLockHelper, LinkedAsyncRWLockHelper, string>(
objNextHelper, objCurrentHelper, objTopMostHeldWriter, Environment.StackTrace);
#else
_objAsyncLocalCurrentsContainer.Value =
new Tuple<LinkedAsyncRWLockHelper, LinkedAsyncRWLockHelper, LinkedAsyncRWLockHelper>(
objNextHelper, objCurrentHelper, objTopMostHeldWriter);
#endif
Expand Down
8 changes: 2 additions & 6 deletions Chummer/Backend/Helpers/LinkedAsyncRWLockHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,7 @@ public sealed class LinkedAsyncRWLockHelper : IDisposable, IAsyncDisposable
private int _intNumChildren;
private DebuggableSemaphoreSlim _objHasChildrenSemaphore; // Used to prevent disposing a helper until it has no more children left

#if LINKEDSEMAPHOREDEBUG
private readonly LinkedAsyncRWLockHelper _objParentLinkedHelper;
#else
private LinkedAsyncRWLockHelper _objParentLinkedHelper;
#endif
private readonly CancellationTokenSource _objDisposalTokenSource = new CancellationTokenSource();
private readonly CancellationToken _objDisposalToken;

Expand Down Expand Up @@ -273,7 +269,7 @@ public void Dispose()
#if LINKEDSEMAPHOREDEBUG
_objParentLinkedHelper?.RemoveChild(this);
#else
Interlocked.Exchange(ref _objParentLinkedHelper, null)?.RemoveChild();
_objParentLinkedHelper?.RemoveChild();
#endif

// Progressively release and dispose of our locks
Expand Down Expand Up @@ -402,7 +398,7 @@ public async ValueTask DisposeAsync()
#if LINKEDSEMAPHOREDEBUG
_objParentLinkedHelper?.RemoveChild(this);
#else
Interlocked.Exchange(ref _objParentLinkedHelper, null)?.RemoveChild();
_objParentLinkedHelper?.RemoveChild();
#endif

// Progressively release and dispose of our locks
Expand Down

0 comments on commit d7c1864

Please sign in to comment.