v0.12.1: halt-aware sleeps in Drive::read recovery
Stop was waiting up to 30 s to register when the drive hit L-EC
recovery mid-read — the recovery phase does 30 s sleeps between
retries and the halt flag was only checked at the start of each sleep.
UI feels broken ("stop isnt working") even though the halt was set.
halt_aware_sleep breaks each wait into 100 ms slices and returns
early on halt. Applied to all 4 sleeps in the recovery path (both
30 s retry delays, both 5 s reset-phase delays).
This commit is contained in:
+29
-4
@@ -128,6 +128,23 @@ impl Drive {
|
|||||||
self.halt.load(Ordering::Relaxed)
|
self.halt.load(Ordering::Relaxed)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sleep for `total`, but wake within ~100 ms if the halt flag fires.
|
||||||
|
/// Returns `true` if halted during sleep. Used by the recovery path
|
||||||
|
/// where a single 30 s sleep would otherwise swallow a Stop request
|
||||||
|
/// and make the UI feel frozen.
|
||||||
|
fn halt_aware_sleep(&self, total: std::time::Duration) -> bool {
|
||||||
|
let slice = std::time::Duration::from_millis(100);
|
||||||
|
let deadline = std::time::Instant::now() + total;
|
||||||
|
while std::time::Instant::now() < deadline {
|
||||||
|
if self.is_halted() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
let remaining = deadline.saturating_duration_since(std::time::Instant::now());
|
||||||
|
std::thread::sleep(remaining.min(slice));
|
||||||
|
}
|
||||||
|
self.is_halted()
|
||||||
|
}
|
||||||
|
|
||||||
/// Close the drive cleanly. Unlocks tray, flushes SCSI state, closes fd.
|
/// Close the drive cleanly. Unlocks tray, flushes SCSI state, closes fd.
|
||||||
/// Also runs automatically on Drop as a safety net.
|
/// Also runs automatically on Drop as a safety net.
|
||||||
pub fn close(self) {
|
pub fn close(self) {
|
||||||
@@ -562,7 +579,9 @@ impl Drive {
|
|||||||
return Err(Error::Halted);
|
return Err(Error::Halted);
|
||||||
}
|
}
|
||||||
self.emit(EventKind::Retry { attempt });
|
self.emit(EventKind::Retry { attempt });
|
||||||
std::thread::sleep(std::time::Duration::from_secs(30));
|
if self.halt_aware_sleep(std::time::Duration::from_secs(30)) {
|
||||||
|
return Err(Error::Halted);
|
||||||
|
}
|
||||||
|
|
||||||
if let Ok(result) = self.scsi.as_mut().execute(
|
if let Ok(result) = self.scsi.as_mut().execute(
|
||||||
&cdb,
|
&cdb,
|
||||||
@@ -582,9 +601,13 @@ impl Drive {
|
|||||||
|
|
||||||
// Phase 2: fresh start — close, reset, open, init.
|
// Phase 2: fresh start — close, reset, open, init.
|
||||||
let device = std::path::PathBuf::from(&self.device_path);
|
let device = std::path::PathBuf::from(&self.device_path);
|
||||||
std::thread::sleep(std::time::Duration::from_secs(5));
|
if self.halt_aware_sleep(std::time::Duration::from_secs(5)) {
|
||||||
|
return Err(Error::Halted);
|
||||||
|
}
|
||||||
let _ = crate::scsi::reset(&device);
|
let _ = crate::scsi::reset(&device);
|
||||||
std::thread::sleep(std::time::Duration::from_secs(5));
|
if self.halt_aware_sleep(std::time::Duration::from_secs(5)) {
|
||||||
|
return Err(Error::Halted);
|
||||||
|
}
|
||||||
self.scsi = crate::scsi::open(&device)?;
|
self.scsi = crate::scsi::open(&device)?;
|
||||||
let _ = self.init();
|
let _ = self.init();
|
||||||
let _ = self.wait_ready();
|
let _ = self.wait_ready();
|
||||||
@@ -600,7 +623,9 @@ impl Drive {
|
|||||||
return Err(Error::Halted);
|
return Err(Error::Halted);
|
||||||
}
|
}
|
||||||
self.emit(EventKind::Retry { attempt });
|
self.emit(EventKind::Retry { attempt });
|
||||||
std::thread::sleep(std::time::Duration::from_secs(30));
|
if self.halt_aware_sleep(std::time::Duration::from_secs(30)) {
|
||||||
|
return Err(Error::Halted);
|
||||||
|
}
|
||||||
|
|
||||||
if let Ok(result) = self.scsi.as_mut().execute(
|
if let Ok(result) = self.scsi.as_mut().execute(
|
||||||
&cdb,
|
&cdb,
|
||||||
|
|||||||
Reference in New Issue
Block a user