From 005f887bf93768f8fa691015d5e8e7e36837cc5f Mon Sep 17 00:00:00 2001 From: MattJackson <1085847+MattJackson@users.noreply.github.com> Date: Wed, 13 May 2026 11:55:52 -0700 Subject: [PATCH] v0.20.3: add halt check to Disc::patch backtrack inner loop MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit WO-5 (partial): the patch backtrack inner loop ('while bt_pos < backtrack_end' in disc/patch.rs) issues per-sector reads to fill the gap created by a damage-window skip. A long backtrack span can run minutes; without an inline halt poll, the outer halt only takes effect when control returns to the per-range loop. Adds a halt poll at the top of each iteration so cancellation propagates inside the backtrack span. Per-sector read failures inside the backtrack already drop through to the main fail path; this only changes the cancellation latency between an /api/stop call and the producer actually unwinding. Drops worst-case unwind from 'whole backtrack span × per-sector recovery timeout' (minutes) to 'one in-flight SCSI command' (seconds). The broader Arc → Halt migration on CopyOptions / SweepOptions / PatchOptions / Drive::halt and Pipeline::send halt- awareness is deferred — separate cycle, larger API impact. --- Cargo.toml | 2 +- src/disc/patch.rs | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 2eeaf91..38c0c37 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "libfreemkv" -version = "0.20.2" +version = "0.20.3" edition = "2024" rust-version = "1.86" license = "AGPL-3.0-only" diff --git a/src/disc/patch.rs b/src/disc/patch.rs index 1805d1c..622503e 100644 --- a/src/disc/patch.rs +++ b/src/disc/patch.rs @@ -895,6 +895,18 @@ impl Disc { ); let mut bt_pos = backtrack_start; while bt_pos < backtrack_end { + // Honor cancellation inside the + // backtrack inner loop. A long + // backtrack span can run minutes + // of single-sector reads; without + // this check the outer halt only + // takes effect when control + // returns to the per-range loop. + if let Some(h) = &opts.halt { + if h.load(std::sync::atomic::Ordering::Relaxed) { + return Err(crate::error::Error::Halted); + } + } let span = // Backtrack always at count=1: this path // fills a gap that the main loop's damage-