fix: keep UpdateCheckWorker alive until its thread finishes (#32)
Closing the About dialog mid-check GC'd the dialog and the running QThread with it -> 'QThread: Destroyed while thread is still running'. A class-level keepalive set now holds each worker until finished; stale results to a destroyed receiver are dropped by Qt. Closes #32 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -13,6 +13,7 @@ from __future__ import annotations
|
|||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from typing import ClassVar
|
||||||
|
|
||||||
from PySide6.QtCore import QRect, QSettings, QSize, Qt, QThread, QTimer, QUrl, Signal
|
from PySide6.QtCore import QRect, QSettings, QSize, Qt, QThread, QTimer, QUrl, Signal
|
||||||
from PySide6.QtGui import (
|
from PySide6.QtGui import (
|
||||||
@@ -172,10 +173,30 @@ class UpdateCheckWorker(QThread):
|
|||||||
running binary. Fails quiet — emits None on any network problem — so
|
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
|
it's safe to fire unattended from a silent startup check as well as from
|
||||||
the About dialog's "Check for updates" button.
|
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
|
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):
|
def run(self):
|
||||||
self.done.emit(core.fetch_latest_release())
|
self.done.emit(core.fetch_latest_release())
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user