Fix store crash when filtering after load (loader thread cleanup)
StorePage cover/date loaders were never removed from tracking once their thread finished, and _populate/_begin_scan dropped the whole list while threads were still running. That garbage-collected QObjects whose QThread had already been deleted, leaving a dangling thread affinity that crashed Qt (SIGSEGV) when searching the store after it fully loaded. Loaders now remove themselves and deleteLater on their finished signal, and the tracking list is kept until each thread winds down.
This commit is contained in:
+21
-3
@@ -2044,7 +2044,6 @@ class StorePage(QWidget):
|
||||
self._cover_attempted.clear()
|
||||
for loader in self._loaders:
|
||||
loader.cancel()
|
||||
self._loaders = []
|
||||
self.grid.clear()
|
||||
self.table.setRowCount(0)
|
||||
self.status.setText("Loading…")
|
||||
@@ -2220,10 +2219,12 @@ class StorePage(QWidget):
|
||||
self._cover_attempted.clear()
|
||||
self._placeholder = QIcon(_placeholder_pixmap(self.ICON_W, self.ICON_H))
|
||||
|
||||
# Cancel any in-flight cover loaders (stale generation).
|
||||
# Cancel any in-flight cover loaders (stale generation). Each loader
|
||||
# removes itself from ``_loaders`` via its own ``finished`` handler once
|
||||
# its thread winds down — dropping the list here would garbage-collect
|
||||
# live QObjects and crash Qt.
|
||||
for loader in self._loaders:
|
||||
loader.cancel()
|
||||
self._loaders = []
|
||||
|
||||
self.grid.clear()
|
||||
self.grid.setUpdatesEnabled(False)
|
||||
@@ -2401,6 +2402,7 @@ class StorePage(QWidget):
|
||||
thread.started.connect(loader.run)
|
||||
loader.loaded.connect(self._on_date_loaded)
|
||||
loader.finished.connect(self._on_date_batch_done)
|
||||
loader.finished.connect(self._on_loader_finished)
|
||||
loader.finished.connect(thread.quit)
|
||||
thread.finished.connect(self._on_thread_finished)
|
||||
self._threads.append(thread)
|
||||
@@ -2425,6 +2427,21 @@ class StorePage(QWidget):
|
||||
if cell is not None:
|
||||
cell.setText(date)
|
||||
|
||||
def _on_loader_finished(self):
|
||||
"""Drop a finished cover/date loader from tracking.
|
||||
|
||||
Loaders stay in ``_loaders`` until their thread finishes so their
|
||||
QObject wrapper is never garbage-collected while its thread is still
|
||||
winding down (that left a dangling thread affinity and crashed Qt on
|
||||
filter/refresh).
|
||||
"""
|
||||
loader = self.sender()
|
||||
if loader is None:
|
||||
return
|
||||
if loader in self._loaders:
|
||||
self._loaders.remove(loader)
|
||||
loader.deleteLater()
|
||||
|
||||
def _start_cover_loader(self, gen, tasks):
|
||||
if self._closing or not tasks:
|
||||
return
|
||||
@@ -2434,6 +2451,7 @@ class StorePage(QWidget):
|
||||
thread.started.connect(loader.run)
|
||||
loader.loaded.connect(self._on_cover_loaded)
|
||||
loader.finished.connect(self._on_cover_batch_done)
|
||||
loader.finished.connect(self._on_loader_finished)
|
||||
loader.finished.connect(thread.quit)
|
||||
thread.finished.connect(self._on_thread_finished)
|
||||
self._threads.append(thread)
|
||||
|
||||
Reference in New Issue
Block a user