fix(src/services): handle race condition in artwork generation and daemon cleanup

This commit is contained in:
sankalp committed 2026-07-16 12:12:15 +05:45
1 parent 19046cd1ee
commit 843c33786f
2 files changed
+16 -5

No files matched your search

+12 -5
View File
@@ -175,6 +175,7 @@ class PlayerService(Service):
self._current_artwork_hash = ""
self._current_artwork_path = ""
self._is_cleaning_up = False
self._artwork_generation = 0
self._signal_ids = []
self._last_emitted_status = ""
@@ -449,9 +450,12 @@ class PlayerService(Service):
if existing and Path(existing).exists():
self._set_artwork(existing)
return
# Capture generation so the thread can bail out if cleanup runs
# while the download is in-flight.
gen = self._artwork_generation
threading.Thread(
target=self._download_artwork,
args=(art_url, artwork_hash, metadata),
args=(art_url, artwork_hash, metadata, gen),
daemon=True,
).start()
@@ -472,9 +476,7 @@ class PlayerService(Service):
cache_dir.mkdir(parents=True, exist_ok=True)
return cache_dir / f"{artwork_hash}{suffix}"
def _download_artwork(self, art_url: str, artwork_hash: str, metadata):
if self._is_cleaning_up:
return
def _download_artwork(self, art_url: str, artwork_hash: str, metadata, gen: int):
try:
with urllib.request.urlopen(art_url, timeout=5) as response:
data = response.read()
@@ -487,7 +489,9 @@ class PlayerService(Service):
tmp.write_bytes(data)
tmp.replace(target)
if not self._is_cleaning_up:
# Bail out if cleanup ran while we were downloading — the idle
# callback would reference a stale service.
if not self._is_cleaning_up and gen == self._artwork_generation:
GLib.idle_add(self._set_artwork, str(target))
except Exception as e:
@@ -497,6 +501,9 @@ class PlayerService(Service):
if self._is_cleaning_up:
return
self._is_cleaning_up = True
# Invalidate any in-flight artwork download threads so they won't
# call _set_artwork on a stale service.
self._artwork_generation += 1
try:
if hasattr(self, "pos_fabricator"):
+4
View File
@@ -134,6 +134,10 @@ class NightLightService(Service):
self._process.wait_async(None, self._on_daemon_exit)
def _on_daemon_exit(self, process: Gio.Subprocess, task: Gio.Task):
# Ignore exit from a stale subprocess — a new daemon may have been
# started since this one was spawned.
if self._process is not process:
return
self._process = None
self._daemon_running = False
self._set_active(False)