diff --git a/bcc.py b/bcc.py index 5886a28..1779909 100644 --- a/bcc.py +++ b/bcc.py @@ -13,6 +13,7 @@ from __future__ import annotations import sys import time from pathlib import Path +from typing import ClassVar from PySide6.QtCore import QRect, QSettings, QSize, Qt, QThread, QTimer, QUrl, Signal from PySide6.QtGui import ( @@ -172,10 +173,30 @@ class UpdateCheckWorker(QThread): running binary. Fails quiet — emits None on any network problem — so it's safe to fire unattended from a silent startup check as well as from the About dialog's "Check for updates" button. + + Lifetime: the class keeps every instance alive in `_live` until its + thread has finished. Without this, a caller whose own reference dies + early (the About dialog is a temporary — closing it mid-check used to + GC the dialog and the running QThread with it) crashes the process with + "QThread: Destroyed while thread is still running". Callers may drop + their reference at any time; signal connections to a destroyed receiver + are disconnected by Qt, so a late result is simply discarded. """ done = Signal(object) # dict | None + _live: ClassVar[set[UpdateCheckWorker]] = set() + + def __init__(self): + super().__init__() + UpdateCheckWorker._live.add(self) + self.finished.connect(self._release_keepalive) + + def _release_keepalive(self): + # Delivered on the main thread after run() has returned; only now is + # it safe for the last reference to drop. + UpdateCheckWorker._live.discard(self) + def run(self): self.done.emit(core.fetch_latest_release())