diag(audio): trace ranged-stream supersedion + abort paths

Skipping tracks while a RangedHttpSource is still in initial probe leaves
no diagnostic trail today: RangedHttpSource::read returns Ok(0) on
gen-mismatch (silent), ranged_download_task drops out the same way, and
the `dl done` summary only fires under app_deprintln so release users
never see partial/aborted downloads either.

Add focused logs at the bail points without changing behaviour:

- RangedHttpSource::read — log on each Ok(0) return that isn't the normal
  pos>=total_size EOF (superseded before/during wait, download done with
  no bytes ahead of cursor). Symphonia stops reading after Ok(0), so at
  most one log per source, no spam.
- ranged_download_task — log on the gen-mismatch bail with track id +
  gen transition + downloaded/total bytes so we can tell "user skipped
  while downloading" apart from "stream stalled".
- track_download_task — same gen-mismatch log for the legacy
  non-seekable path (consistency with ranged).
- dl-done summary — split into release-visible `[stream] ranged dl
  ABORTED: …` (downloaded < total_size) vs the existing dev-only
  `[stream] dl done` for full completions.
- audio_play — log on both supersedion-bail points around
  select_play_input so a silently-ending audio_play call leaves a trace.

No semantics changed: every existing return / store / branch is intact.
Pure additive logging to make the next reproduction of cucadmuh's
ranged-stream toast diagnosable.
This commit is contained in:
Psychotoxical
2026-05-09 01:47:02 +02:00
parent 176382e0b6
commit ff4271181c
2 changed files with 50 additions and 8 deletions
+11 -1
View File
@@ -147,11 +147,21 @@ pub async fn audio_play(
&app,
).await? {
Some(input) => input,
None => return Ok(()), // superseded — bail
None => {
crate::app_deprintln!(
"[audio] audio_play superseded inside select_play_input: gen={} cur={} track_id={:?}",
gen, state.generation.load(Ordering::SeqCst), cache_id_for_tasks
);
return Ok(());
}
};
if state.generation.load(Ordering::SeqCst) != gen {
crate::app_deprintln!(
"[audio] audio_play superseded after select_play_input: gen={} cur={} track_id={:?}",
gen, state.generation.load(Ordering::SeqCst), cache_id_for_tasks
);
return Ok(());
}
+32
View File
@@ -289,6 +289,10 @@ pub(crate) struct RangedHttpSource {
impl Read for RangedHttpSource {
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
if self.gen_arc.load(Ordering::SeqCst) != self.gen {
crate::app_deprintln!(
"[stream] ranged-stream read EOF: superseded before first read (gen={} cur={} pos={}/{})",
self.gen, self.gen_arc.load(Ordering::SeqCst), self.pos, self.total_size
);
return Ok(0);
}
if self.pos >= self.total_size {
@@ -303,6 +307,11 @@ impl Read for RangedHttpSource {
let deadline = Instant::now() + Duration::from_secs(RADIO_READ_TIMEOUT_SECS);
loop {
if self.gen_arc.load(Ordering::SeqCst) != self.gen {
crate::app_deprintln!(
"[stream] ranged-stream read EOF: superseded mid-wait (gen={} cur={} pos={}/{} dl={})",
self.gen, self.gen_arc.load(Ordering::SeqCst), self.pos, self.total_size,
self.downloaded_to.load(Ordering::SeqCst)
);
return Ok(0);
}
let dl = self.downloaded_to.load(Ordering::SeqCst) as u64;
@@ -321,6 +330,10 @@ impl Read for RangedHttpSource {
self.pos += avail as u64;
return Ok(avail);
}
crate::app_deprintln!(
"[stream] ranged-stream read EOF: download done with no data ahead of cursor (pos={}/{} dl={})",
self.pos, self.total_size, dl
);
return Ok(0);
}
if Instant::now() >= deadline {
@@ -654,6 +667,10 @@ pub(crate) async fn track_download_task(
let mut byte_stream = response.bytes_stream();
while let Some(chunk) = byte_stream.next().await {
if gen_arc.load(Ordering::SeqCst) != gen {
crate::app_deprintln!(
"[stream] track-stream dl superseded by skip: track_id={:?} gen={}→{}",
cache_track_id, gen, gen_arc.load(Ordering::SeqCst)
);
done.store(true, Ordering::SeqCst);
return;
}
@@ -842,6 +859,10 @@ pub(crate) async fn ranged_download_task(
let mut byte_stream = response.bytes_stream();
while let Some(chunk) = byte_stream.next().await {
if gen_arc.load(Ordering::SeqCst) != gen {
crate::app_deprintln!(
"[stream] ranged dl superseded by skip: track_id={:?} gen={}→{} downloaded={}/{} bytes",
cache_track_id, gen, gen_arc.load(Ordering::SeqCst), downloaded, total_size
);
done.store(true, Ordering::SeqCst);
return;
}
@@ -928,6 +949,16 @@ pub(crate) async fn ranged_download_task(
done.store(true, Ordering::SeqCst);
if downloaded < total_size {
crate::app_eprintln!(
"[stream] ranged dl ABORTED: {} / {} bytes in {:.2}s ({} reconnects, track_id={:?})",
downloaded,
total_size,
dl_started.elapsed().as_secs_f64(),
reconnects,
cache_track_id
);
} else {
crate::app_deprintln!(
"[stream] dl done: {} / {} bytes in {:.2}s ({} reconnects)",
downloaded,
@@ -935,6 +966,7 @@ pub(crate) async fn ranged_download_task(
dl_started.elapsed().as_secs_f64(),
reconnects
);
}
if downloaded == total_size && total_size > 0 && total_size <= TRACK_STREAM_PROMOTE_MAX_BYTES {
if let Some(ref tid) = cache_track_id {