Skip to content

Commit b0f43f4

Browse files
committed
enable cancelling pending thread queue actions on dispose
1 parent a807477 commit b0f43f4

File tree

4 files changed

+66
-1
lines changed

4 files changed

+66
-1
lines changed

Framework/Intersect.Framework/Threading/ActionQueue.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ protected ActionQueue(Action<TActionQueue>? beginInvokePending, Action<TActionQu
2323

2424
protected abstract Action<TEnqueueState> PostInvocationAction { get; }
2525

26+
public virtual void ClearPending() { }
27+
2628
/// <summary>
2729
///
2830
/// </summary>

Framework/Intersect.Framework/Threading/ThreadQueue.cs

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ public sealed partial class ThreadQueue : ActionQueue<ThreadQueue, ManualResetEv
88

99
private readonly object _lock = new();
1010
private readonly Stack<ManualResetEventSlim> _resetEventPool = [];
11+
private readonly HashSet<CancellationTokenSource> _pendingCancellationTokenSources = [];
1112
private readonly int? _spinCount;
1213

1314
private int _mainThreadId;
@@ -34,12 +35,55 @@ public bool IsOnMainThread
3435
get => _mainThreadId == Environment.CurrentManagedThreadId;
3536
}
3637

38+
public override void ClearPending()
39+
{
40+
base.ClearPending();
41+
42+
CancellationTokenSource[] pendingCancellationTokenSources;
43+
lock (_pendingCancellationTokenSources)
44+
{
45+
pendingCancellationTokenSources = _pendingCancellationTokenSources.ToArray();
46+
_pendingCancellationTokenSources.Clear();
47+
}
48+
49+
foreach (var cancellationTokenSource in pendingCancellationTokenSources)
50+
{
51+
cancellationTokenSource.Cancel();
52+
}
53+
}
54+
3755
[MethodImpl(MethodImplOptions.AggressiveInlining)]
3856
private static void BeginInvokePending(ThreadQueue @this) => @this.ThrowIfNotOnMainThread();
3957

4058
protected override ManualResetEventSlim EnqueueCreateState() => ResetEventPoolPop();
4159

42-
protected override void EnqueueSuccessful(ManualResetEventSlim resetEvent) => resetEvent.Wait();
60+
protected override void EnqueueSuccessful(ManualResetEventSlim resetEvent)
61+
{
62+
CancellationTokenSource cancellationTokenSource = new();
63+
64+
lock (_pendingCancellationTokenSources)
65+
{
66+
_pendingCancellationTokenSources.Add(cancellationTokenSource);
67+
}
68+
69+
try
70+
{
71+
resetEvent.Wait(cancellationTokenSource.Token);
72+
}
73+
catch (OperationCanceledException operationCanceledException)
74+
{
75+
if (!cancellationTokenSource.IsCancellationRequested ||
76+
!operationCanceledException.CancellationToken.Equals(cancellationTokenSource.Token))
77+
{
78+
throw;
79+
}
80+
}
81+
82+
lock (_pendingCancellationTokenSources)
83+
{
84+
_pendingCancellationTokenSources.Remove(cancellationTokenSource);
85+
}
86+
}
4387

4488
protected override void EnqueueFinally(ManualResetEventSlim resetEvent) => ResetEventPoolPush(resetEvent);
4589

Intersect.Client.Framework/Gwen/Control/Base.Threading.cs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
using Intersect.Core;
12
using Intersect.Framework.Threading;
3+
using Microsoft.Extensions.Logging;
24

35
namespace Intersect.Client.Framework.Gwen.Control;
46

@@ -60,6 +62,21 @@ public TReturn RunOnMainThread<TState, TReturn>(
6062

6163
public void RunOnMainThread<TState0, TState1>(Action<TState0, TState1> action, TState0 state0, TState1 state1)
6264
{
65+
if (_disposed)
66+
{
67+
if (_disposeCompleted)
68+
{
69+
// TODO: Turn this into a LogError() after we see some reports
70+
ObjectDisposedException.ThrowIf(true, this);
71+
}
72+
73+
ApplicationContext.CurrentContext.Logger.LogTrace(
74+
"RunOnMainThread() called after {NodeName} was disposed",
75+
CanonicalName
76+
);
77+
return;
78+
}
79+
6380
Invalidate();
6481
_threadQueue.RunOnMainThread(action, state0, state1);
6582
}

Intersect.Client.Framework/Gwen/Control/Base.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1287,6 +1287,8 @@ public void Dispose()
12871287

12881288
_disposed = true;
12891289

1290+
_threadQueue.ClearPending();
1291+
12901292
Dispose(disposing: true);
12911293

12921294
ICacheToTexture? cache = default;

0 commit comments

Comments
 (0)