Skip to content

Commit 759bd78

Browse files
authored
fix: ensure cleanup of workers on termination (#2652)
1 parent 4d820ed commit 759bd78

File tree

2 files changed

+39
-16
lines changed

2 files changed

+39
-16
lines changed

packages/csharp/src/AlphaTab/Platform/CSharp/ManagedThreadAlphaSynthWorker.cs

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,25 +8,26 @@ namespace AlphaTab.Platform.CSharp;
88
internal abstract class ManagedThreadWorkerBase<T> : IAlphaTabWorker<T>
99
{
1010
private readonly Action<Action> _postToMain;
11-
private readonly Thread _workerThread;
1211
private readonly BlockingCollection<Action> _workerQueue;
1312
private readonly CancellationTokenSource _workerCancellationToken;
1413
private readonly ManualResetEventSlim? _threadStartedEvent;
1514
private readonly ConcurrentDictionary<Action<MessageEvent<T>>,Action<MessageEvent<T>>> _listenerInsideWorker = new();
1615
private readonly ConcurrentDictionary<Action<MessageEvent<T>>,Action<MessageEvent<T>>> _listenerOutsideWorker = new();
1716

17+
protected Thread WorkerThread { get; }
18+
1819
protected ManagedThreadWorkerBase(Action<Action> postToMain)
1920
{
2021
_postToMain = postToMain;
2122
_threadStartedEvent = new ManualResetEventSlim(false);
2223
_workerQueue = new BlockingCollection<Action>();
2324
_workerCancellationToken = new CancellationTokenSource();
2425

25-
_workerThread = new Thread(DoWork)
26+
WorkerThread = new Thread(DoWork)
2627
{
2728
IsBackground = true
2829
};
29-
_workerThread.Start();
30+
WorkerThread.Start();
3031

3132
_threadStartedEvent.Wait();
3233
_threadStartedEvent.Dispose();
@@ -55,7 +56,7 @@ private void DoWork()
5556
public void PostMessage(T message)
5657
{
5758
var ev = new MessageEvent<T>(message);
58-
if (Thread.CurrentThread.ManagedThreadId == _workerThread.ManagedThreadId)
59+
if (Thread.CurrentThread.ManagedThreadId == WorkerThread.ManagedThreadId)
5960
{
6061
// Inside Worker -> Post to main
6162
_postToMain(() =>
@@ -87,7 +88,7 @@ public void PostToWorker(Action action)
8788
public void AddEventListener(string @event, Action<MessageEvent<T>> handler)
8889
{
8990
if (@event != "message") return;
90-
var listeners = Thread.CurrentThread.ManagedThreadId == _workerThread.ManagedThreadId
91+
var listeners = Thread.CurrentThread.ManagedThreadId == WorkerThread.ManagedThreadId
9192
? _listenerInsideWorker
9293
: _listenerOutsideWorker;
9394
listeners[handler] = handler;
@@ -96,7 +97,7 @@ public void AddEventListener(string @event, Action<MessageEvent<T>> handler)
9697
public void RemoveEventListener(string @event, Action<MessageEvent<T>> handler)
9798
{
9899
if (@event != "message") return;
99-
var listeners = Thread.CurrentThread.ManagedThreadId == _workerThread.ManagedThreadId
100+
var listeners = Thread.CurrentThread.ManagedThreadId == WorkerThread.ManagedThreadId
100101
? _listenerInsideWorker
101102
: _listenerOutsideWorker;
102103
listeners.TryRemove(handler, out _);
@@ -105,7 +106,7 @@ public void RemoveEventListener(string @event, Action<MessageEvent<T>> handler)
105106
public virtual void Terminate()
106107
{
107108
_workerCancellationToken.Cancel();
108-
_workerThread.Join();
109+
WorkerThread.Join();
109110
while (_workerQueue.Count > 0)
110111
{
111112
_workerQueue.Take();
@@ -133,6 +134,12 @@ protected override void OnStartInsideWorker()
133134
WorkerLookup[Thread.CurrentThread.ManagedThreadId] = this;
134135
AlphaTabWebWorker.Init();
135136
}
137+
138+
public override void Terminate()
139+
{
140+
base.Terminate();
141+
WorkerLookup.TryRemove(WorkerThread.ManagedThreadId, out _);
142+
}
136143
}
137144

138145
internal class ManagedThreadAlphaSynthWorker :
@@ -155,4 +162,10 @@ protected override void OnStartInsideWorker()
155162
WorkerLookup[Thread.CurrentThread.ManagedThreadId] = this;
156163
AlphaSynthWebWorker.Init();
157164
}
165+
166+
public override void Terminate()
167+
{
168+
base.Terminate();
169+
WorkerLookup.TryRemove(WorkerThread.ManagedThreadId, out _);
170+
}
158171
}

packages/kotlin/src/android/src/main/java/alphaTab/platform/android/JavaThreadWorkers.kt

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import kotlin.contracts.ExperimentalContracts
1717
@OptIn(ExperimentalContracts::class, ExperimentalUnsignedTypes::class)
1818
internal abstract class JavaThreadWorkerBase<T> : IAlphaTabWorker<T>, Runnable {
1919
private val _postToMain: (action: () -> Unit) -> Unit
20-
private val _workerThread: Thread
20+
protected val workerThread: Thread
2121
private val _workerQueue = LinkedBlockingQueue<() -> Unit>()
2222
private var _isCancelled = false
2323
private val _threadStartedEvent = Semaphore(1)
@@ -29,9 +29,9 @@ internal abstract class JavaThreadWorkerBase<T> : IAlphaTabWorker<T>, Runnable {
2929
protected constructor(postToMain: (action: () -> Unit) -> Unit) {
3030
_postToMain = postToMain;
3131

32-
_workerThread = Thread(this)
33-
_workerThread.isDaemon = true
34-
_workerThread.start()
32+
workerThread = Thread(this)
33+
workerThread.isDaemon = true
34+
workerThread.start()
3535

3636
_threadStartedEvent.acquire()
3737
}
@@ -56,7 +56,7 @@ internal abstract class JavaThreadWorkerBase<T> : IAlphaTabWorker<T>, Runnable {
5656

5757
override fun postMessage(message: T) {
5858
val ev = MessageEvent(message);
59-
if (Thread.currentThread().id == _workerThread.id) {
59+
if (Thread.currentThread().id == workerThread.id) {
6060
// Inside Worker -> Post to main
6161
_postToMain(
6262
{
@@ -83,7 +83,7 @@ internal abstract class JavaThreadWorkerBase<T> : IAlphaTabWorker<T>, Runnable {
8383
if (event != "message") {
8484
return;
8585
}
86-
val listeners = if (Thread.currentThread().id == _workerThread.id) {
86+
val listeners = if (Thread.currentThread().id == workerThread.id) {
8787
_listenerInsideWorker
8888
} else {
8989
_listenerOutsideWorker
@@ -95,7 +95,7 @@ internal abstract class JavaThreadWorkerBase<T> : IAlphaTabWorker<T>, Runnable {
9595
if (event != "message") {
9696
return;
9797
}
98-
val listeners = if (Thread.currentThread().id == _workerThread.id) {
98+
val listeners = if (Thread.currentThread().id == workerThread.id) {
9999
_listenerInsideWorker
100100
} else {
101101
_listenerOutsideWorker
@@ -105,8 +105,8 @@ internal abstract class JavaThreadWorkerBase<T> : IAlphaTabWorker<T>, Runnable {
105105

106106
override fun terminate() {
107107
_isCancelled = true
108-
_workerThread.interrupt()
109-
_workerThread.join()
108+
workerThread.interrupt()
109+
workerThread.join()
110110
_workerQueue.clear()
111111
}
112112
}
@@ -130,6 +130,11 @@ internal class JavaThreadAlphaTabRendererWorker(postToMain: (action: () -> Unit)
130130
workerLookup[Thread.currentThread().id] = this;
131131
AlphaTabWebWorker.init();
132132
}
133+
134+
override fun terminate() {
135+
super.terminate()
136+
workerLookup.remove(workerThread.id)
137+
}
133138
}
134139

135140
@OptIn(ExperimentalContracts::class, ExperimentalUnsignedTypes::class)
@@ -151,4 +156,9 @@ internal class JavaThreadAlphaSynthWorker(postToMain: (action: () -> Unit) -> Un
151156
workerLookup[Thread.currentThread().id] = this;
152157
AlphaSynthWebWorker.init();
153158
}
159+
160+
override fun terminate() {
161+
super.terminate()
162+
workerLookup.remove(workerThread.id)
163+
}
154164
}

0 commit comments

Comments
 (0)