Fix killing Analyzis worker threads during shutdown, possible data loss. - #16064
Conversation
| ~Worker() { | ||
| if (m_pThread) { | ||
| m_pThread->stop(); | ||
| m_pThread->wait(); |
There was a problem hiding this comment.
Should we add a timeout(+assert) to prevent risk of infinite wait?
… active during application exit. This happens because the deleteLater() signal might not reach the thread early enough before the event cue is destroyed.
… active during application exit. This happens because the deleteLater() signal might not reach the thread early enough before the event cue is destroyed.
765eacd to
649614c
Compare
|
I have debugged this with extra qDebug() messages and it seems to work now. However the is still a related memory left over, slightly different |
|
The remaining memory leak is unrelated. It happens because Waveform is created as a QSharedPointer in the Analyzer thread, but then distributed in Mixxx. So there is one owner left that is not deleted. It is no longer the Analyzer thread. |
6966808 to
cf66ba4
Compare
cf66ba4 to
0e51f94
Compare
28b1f0c to
e869551
Compare
|
Done. |
There was a problem hiding this comment.
Pull request overview
This PR updates analyzer shutdown handling so analysis worker threads are stopped, waited for, and deleted synchronously instead of relying entirely on Qt deferred deletion during application shutdown. It also includes related cleanup enabled by the adjusted release-build behavior of DEBUG_ASSERT.
Changes:
- Adds synchronous cleanup of
TrackAnalysisSchedulerand its workerAnalyzerThreads in shutdown paths. - Updates
DEBUG_ASSERTrelease behavior to type-check/mark conditions as used, then removes several now-unneededQ_UNUSED/#ifdefworkarounds. - Applies small cleanup changes such as moving waveform pointers and simplifying includes.
Reviewed changes
Copilot reviewed 13 out of 13 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
src/analyzer/trackanalysisscheduler.h |
Adds Worker destructor that stops, waits for, and deletes analyzer threads synchronously. |
src/analyzer/trackanalysisscheduler.cpp |
Updates worker thread state check and adds destructor comment. |
src/mixer/playermanager.cpp |
Deletes the track analysis scheduler synchronously during player manager shutdown. |
src/library/analysis/analysisfeature.h |
Declares a custom destructor for synchronous scheduler cleanup. |
src/library/analysis/analysisfeature.cpp |
Implements synchronous deletion of the analysis scheduler. |
src/util/assert.h |
Changes release-build DEBUG_ASSERT to compile-check the condition without evaluating it. |
src/util/parented_ptr.h |
Simplifies includes around QObject usage. |
src/track/track.cpp |
Moves waveform shared pointer into the track field. |
src/track/beats.cpp |
Removes debug-only guards around values now referenced by release DEBUG_ASSERT. |
src/sources/soundsourcemp3.cpp |
Removes debug-assertion-only unused annotations/guards. |
src/qml/qmlwaveformoverview.cpp |
Removes now-unneeded Q_UNUSED. |
src/library/dao/autodjcratesdao.cpp |
Unguards constants used by DEBUG_ASSERT. |
src/control/controlobject.cpp |
Removes now-unneeded Q_UNUSED. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| bool success = m_pThread->wait(5000); // 5 s | ||
| DEBUG_ASSERT(success); | ||
| m_pThread->wait(); |
There was a problem hiding this comment.
That only happen with DEBUG_ASSERTIONS_FATAL and is no issue.
| /// In release builds, it marks cond as used and checks if it can be converted | ||
| /// to bool. Be careful of the common mistake with assertions: | ||
| /// DEBUG_ASSERT(doSomething()); | ||
| /// | ||
| /// In release builds, doSomething() is never called! | ||
| /// doSomething() is never called, in In release builds. |
| m_pTrackAnalysisScheduler.reset(); | ||
| } | ||
| // We need to delete m_pTrackAnalysisScheduler here immediately synchronously, | ||
| // waiting for pending threads to have finished, to not killthem during exit |
|
|
||
| AnalysisFeature::~AnalysisFeature() { | ||
| // We need to delete m_pTrackAnalysisScheduler here immediately synchronously, | ||
| // waiting for pending threads to have finished, to not killthem during exit |
|
|
||
| TrackAnalysisScheduler::~TrackAnalysisScheduler() { | ||
| kLogger.debug() << "Destroying"; | ||
| // Here the workers in m_workers are deleted after waiting for the associated thread is finished |
caa3aa6 to
9fbbf7b
Compare
|
Done. |
The issue was that they where auto deleted after finish via deleteLater() however it can happen that the Qt event cue is already down and the object is never deleted.
The fix is to add a synchronous delete call in the owners destructor that stops the workers, waits for finish and deletes the threads manually.
The issue has been found by Valgrind: