From 93e71dd1dce1b1d80dcc72d2dcc62fe4a7accbb8 Mon Sep 17 00:00:00 2001 From: Danielku15 Date: Thu, 9 Apr 2026 21:02:18 +0200 Subject: [PATCH] fix: ensure cleanup of workers on termination --- .../CSharp/ManagedThreadAlphaSynthWorker.cs | 27 +++++++++++++----- .../platform/android/JavaThreadWorkers.kt | 28 +++++++++++++------ 2 files changed, 39 insertions(+), 16 deletions(-) diff --git a/packages/csharp/src/AlphaTab/Platform/CSharp/ManagedThreadAlphaSynthWorker.cs b/packages/csharp/src/AlphaTab/Platform/CSharp/ManagedThreadAlphaSynthWorker.cs index 3971f2997..8d0bddad7 100644 --- a/packages/csharp/src/AlphaTab/Platform/CSharp/ManagedThreadAlphaSynthWorker.cs +++ b/packages/csharp/src/AlphaTab/Platform/CSharp/ManagedThreadAlphaSynthWorker.cs @@ -8,13 +8,14 @@ namespace AlphaTab.Platform.CSharp; internal abstract class ManagedThreadWorkerBase : IAlphaTabWorker { private readonly Action _postToMain; - private readonly Thread _workerThread; private readonly BlockingCollection _workerQueue; private readonly CancellationTokenSource _workerCancellationToken; private readonly ManualResetEventSlim? _threadStartedEvent; private readonly ConcurrentDictionary>,Action>> _listenerInsideWorker = new(); private readonly ConcurrentDictionary>,Action>> _listenerOutsideWorker = new(); + protected Thread WorkerThread { get; } + protected ManagedThreadWorkerBase(Action postToMain) { _postToMain = postToMain; @@ -22,11 +23,11 @@ protected ManagedThreadWorkerBase(Action postToMain) _workerQueue = new BlockingCollection(); _workerCancellationToken = new CancellationTokenSource(); - _workerThread = new Thread(DoWork) + WorkerThread = new Thread(DoWork) { IsBackground = true }; - _workerThread.Start(); + WorkerThread.Start(); _threadStartedEvent.Wait(); _threadStartedEvent.Dispose(); @@ -55,7 +56,7 @@ private void DoWork() public void PostMessage(T message) { var ev = new MessageEvent(message); - if (Thread.CurrentThread.ManagedThreadId == _workerThread.ManagedThreadId) + if (Thread.CurrentThread.ManagedThreadId == WorkerThread.ManagedThreadId) { // Inside Worker -> Post to main _postToMain(() => @@ -87,7 +88,7 @@ public void PostToWorker(Action action) public void AddEventListener(string @event, Action> handler) { if (@event != "message") return; - var listeners = Thread.CurrentThread.ManagedThreadId == _workerThread.ManagedThreadId + var listeners = Thread.CurrentThread.ManagedThreadId == WorkerThread.ManagedThreadId ? _listenerInsideWorker : _listenerOutsideWorker; listeners[handler] = handler; @@ -96,7 +97,7 @@ public void AddEventListener(string @event, Action> handler) public void RemoveEventListener(string @event, Action> handler) { if (@event != "message") return; - var listeners = Thread.CurrentThread.ManagedThreadId == _workerThread.ManagedThreadId + var listeners = Thread.CurrentThread.ManagedThreadId == WorkerThread.ManagedThreadId ? _listenerInsideWorker : _listenerOutsideWorker; listeners.TryRemove(handler, out _); @@ -105,7 +106,7 @@ public void RemoveEventListener(string @event, Action> handler) public virtual void Terminate() { _workerCancellationToken.Cancel(); - _workerThread.Join(); + WorkerThread.Join(); while (_workerQueue.Count > 0) { _workerQueue.Take(); @@ -133,6 +134,12 @@ protected override void OnStartInsideWorker() WorkerLookup[Thread.CurrentThread.ManagedThreadId] = this; AlphaTabWebWorker.Init(); } + + public override void Terminate() + { + base.Terminate(); + WorkerLookup.TryRemove(WorkerThread.ManagedThreadId, out _); + } } internal class ManagedThreadAlphaSynthWorker : @@ -155,4 +162,10 @@ protected override void OnStartInsideWorker() WorkerLookup[Thread.CurrentThread.ManagedThreadId] = this; AlphaSynthWebWorker.Init(); } + + public override void Terminate() + { + base.Terminate(); + WorkerLookup.TryRemove(WorkerThread.ManagedThreadId, out _); + } } diff --git a/packages/kotlin/src/android/src/main/java/alphaTab/platform/android/JavaThreadWorkers.kt b/packages/kotlin/src/android/src/main/java/alphaTab/platform/android/JavaThreadWorkers.kt index 4a638d342..8e99ac5ec 100644 --- a/packages/kotlin/src/android/src/main/java/alphaTab/platform/android/JavaThreadWorkers.kt +++ b/packages/kotlin/src/android/src/main/java/alphaTab/platform/android/JavaThreadWorkers.kt @@ -17,7 +17,7 @@ import kotlin.contracts.ExperimentalContracts @OptIn(ExperimentalContracts::class, ExperimentalUnsignedTypes::class) internal abstract class JavaThreadWorkerBase : IAlphaTabWorker, Runnable { private val _postToMain: (action: () -> Unit) -> Unit - private val _workerThread: Thread + protected val workerThread: Thread private val _workerQueue = LinkedBlockingQueue<() -> Unit>() private var _isCancelled = false private val _threadStartedEvent = Semaphore(1) @@ -29,9 +29,9 @@ internal abstract class JavaThreadWorkerBase : IAlphaTabWorker, Runnable { protected constructor(postToMain: (action: () -> Unit) -> Unit) { _postToMain = postToMain; - _workerThread = Thread(this) - _workerThread.isDaemon = true - _workerThread.start() + workerThread = Thread(this) + workerThread.isDaemon = true + workerThread.start() _threadStartedEvent.acquire() } @@ -56,7 +56,7 @@ internal abstract class JavaThreadWorkerBase : IAlphaTabWorker, Runnable { override fun postMessage(message: T) { val ev = MessageEvent(message); - if (Thread.currentThread().id == _workerThread.id) { + if (Thread.currentThread().id == workerThread.id) { // Inside Worker -> Post to main _postToMain( { @@ -83,7 +83,7 @@ internal abstract class JavaThreadWorkerBase : IAlphaTabWorker, Runnable { if (event != "message") { return; } - val listeners = if (Thread.currentThread().id == _workerThread.id) { + val listeners = if (Thread.currentThread().id == workerThread.id) { _listenerInsideWorker } else { _listenerOutsideWorker @@ -95,7 +95,7 @@ internal abstract class JavaThreadWorkerBase : IAlphaTabWorker, Runnable { if (event != "message") { return; } - val listeners = if (Thread.currentThread().id == _workerThread.id) { + val listeners = if (Thread.currentThread().id == workerThread.id) { _listenerInsideWorker } else { _listenerOutsideWorker @@ -105,8 +105,8 @@ internal abstract class JavaThreadWorkerBase : IAlphaTabWorker, Runnable { override fun terminate() { _isCancelled = true - _workerThread.interrupt() - _workerThread.join() + workerThread.interrupt() + workerThread.join() _workerQueue.clear() } } @@ -130,6 +130,11 @@ internal class JavaThreadAlphaTabRendererWorker(postToMain: (action: () -> Unit) workerLookup[Thread.currentThread().id] = this; AlphaTabWebWorker.init(); } + + override fun terminate() { + super.terminate() + workerLookup.remove(workerThread.id) + } } @OptIn(ExperimentalContracts::class, ExperimentalUnsignedTypes::class) @@ -151,4 +156,9 @@ internal class JavaThreadAlphaSynthWorker(postToMain: (action: () -> Unit) -> Un workerLookup[Thread.currentThread().id] = this; AlphaSynthWebWorker.init(); } + + override fun terminate() { + super.terminate() + workerLookup.remove(workerThread.id) + } }