Problem
The .unwrap_or_else(|e| { log::warn!(...); e.into_inner() }) poison-recovery pattern is copy-pasted across 19 lock sites in two files. The canonical wrapper already exists — EngineCell::lock (engine.rs:60-65) — but is only applied to one mutex:
// engine.rs:60-65 — the one site that uses a helper
fn lock(&self) -> std::sync::MutexGuard<'_, EngineSlot> {
self.slot.lock().unwrap_or_else(|e| {
log::warn!("engine slot mutex poisoned, recovering: {e}");
e.into_inner()
})
}
The remaining 18 call sites repeat the pattern inline, each with its own log string:
| Location |
Mutex |
Lines |
IdleUnloadCoordinator::touch |
state |
180-183 |
IdleUnloadCoordinator::cancel |
state |
193-196 |
IdleUnloadCoordinator::spawn_worker (initial wait) |
state |
212-215 |
spawn_worker condvar wait |
state (via condvar) |
224-229 |
spawn_worker pre-timeout lock |
state |
236-239 |
spawn_worker condvar wait_timeout |
state (via condvar) |
240-245 |
spawn_worker post-fire relock |
state |
282-285 |
IdleUnloadCoordinator::drop — state |
state |
317-320 |
IdleUnloadCoordinator::drop — worker |
worker |
326-329 |
TranscriptionEngineState::set_accelerator |
accelerator |
390-393 |
TranscriptionEngineState::set_model |
model |
409-412 |
TranscriptionEngineState::clear_model |
model |
427-430 |
checkout condvar wait (silent) |
engine.slot |
451 |
warm_up condvar wait (silent) |
engine.slot |
565 |
model_supports_streaming |
model |
605-608 |
streaming_chunk_ms |
model |
621-624 |
EngineRegistry::register |
factories (RwLock write) |
engine_trait.rs:112-115 |
EngineRegistry::factory_for |
factories (RwLock read) |
engine_trait.rs:121-124 |
Two of the condvar-wait sites (lines 451, 565) silently swallow the error without logging — an inconsistency introduced by the repetition.
Why it matters
- Lock-ordering audits are painful. Each inline recovery block is syntactic noise in the already-dense concurrency paths (
spawn_worker inner loop, checkout, warm_up). Reviewers must read past the boilerplate to trace actual flow.
- Inconsistency hides bugs. Lines 451 and 565 silently recover (
e.into_inner() with no log) while the others warn — different behaviors for the same condition, with no documented rationale.
- Hot path inflation. Several sites sit on the transcription hot path (
checkout, model_supports_streaming, streaming_chunk_ms). Dead-code boilerplate makes the happy path harder to read and profile.
Proposed change
-
Introduce one generic helper (or a macro if the condvar variant needs covering) analogous to EngineCell::lock, e.g.:
fn lock_recovering<T>(mutex: &Mutex<T>, label: &str) -> MutexGuard<'_, T> {
mutex.lock().unwrap_or_else(|e| {
log::warn!("{label} mutex poisoned, recovering: {e}");
e.into_inner()
})
}
The condvar .wait/.wait_timeout variants also need coverage — either a second helper or a macro capturing the label.
-
Replace all 18 inline sites with calls to the helper. EngineCell::lock (the existing wrapper) can delegate to the same helper internally or stay as-is since it already encapsulates the pattern.
-
Fix the two silent condvar sites (lines 451, 565): add the warn log to match the policy of every other site, or deliberately expect if poisoning there is considered unreachable.
-
No behavior change — this is a pure refactor. All recovery semantics stay identical; only the duplication is removed.
Tests to check: No test currently injects a poisoned mutex, so the test suite will pass unchanged. If a poison-recovery test is added in future, it should target the helper, not individual sites.
Recommendation / triage
DO — mechanical, behavior-identical, removes boilerplate from the trickiest code. Generalize the EngineCell::lock-style wrapper into one helper reused at every site. Broad but low-risk; keep it a pure refactor (no behavior change) in its own PR.
Acceptance criteria
Traceability
clawpatch finding ID: fnd_sig-feat-service-25552574c9-8c3e_f8b3f1b6ef
Revalidate command:
clawpatch revalidate --finding fnd_sig-feat-service-25552574c9-8c3e_f8b3f1b6ef --reasoning-effort high
No coupled issues at time of filing.
Problem
The
.unwrap_or_else(|e| { log::warn!(...); e.into_inner() })poison-recovery pattern is copy-pasted across 19 lock sites in two files. The canonical wrapper already exists —EngineCell::lock(engine.rs:60-65) — but is only applied to one mutex:The remaining 18 call sites repeat the pattern inline, each with its own log string:
IdleUnloadCoordinator::touchstateIdleUnloadCoordinator::cancelstateIdleUnloadCoordinator::spawn_worker(initial wait)statespawn_workercondvarwaitstate(via condvar)spawn_workerpre-timeout lockstatespawn_workercondvarwait_timeoutstate(via condvar)spawn_workerpost-fire relockstateIdleUnloadCoordinator::drop— statestateIdleUnloadCoordinator::drop— workerworkerTranscriptionEngineState::set_acceleratoracceleratorTranscriptionEngineState::set_modelmodelTranscriptionEngineState::clear_modelmodelcheckoutcondvarwait(silent)engine.slotwarm_upcondvarwait(silent)engine.slotmodel_supports_streamingmodelstreaming_chunk_msmodelEngineRegistry::registerfactories(RwLock write)EngineRegistry::factory_forfactories(RwLock read)Two of the condvar-wait sites (lines 451, 565) silently swallow the error without logging — an inconsistency introduced by the repetition.
Why it matters
spawn_workerinner loop,checkout,warm_up). Reviewers must read past the boilerplate to trace actual flow.e.into_inner()with no log) while the others warn — different behaviors for the same condition, with no documented rationale.checkout,model_supports_streaming,streaming_chunk_ms). Dead-code boilerplate makes the happy path harder to read and profile.Proposed change
Introduce one generic helper (or a macro if the condvar variant needs covering) analogous to
EngineCell::lock, e.g.:The condvar
.wait/.wait_timeoutvariants also need coverage — either a second helper or a macro capturing the label.Replace all 18 inline sites with calls to the helper.
EngineCell::lock(the existing wrapper) can delegate to the same helper internally or stay as-is since it already encapsulates the pattern.Fix the two silent condvar sites (lines 451, 565): add the warn log to match the policy of every other site, or deliberately
expectif poisoning there is considered unreachable.No behavior change — this is a pure refactor. All recovery semantics stay identical; only the duplication is removed.
Tests to check: No test currently injects a poisoned mutex, so the test suite will pass unchanged. If a poison-recovery test is added in future, it should target the helper, not individual sites.
Recommendation / triage
DO — mechanical, behavior-identical, removes boilerplate from the trickiest code. Generalize the
EngineCell::lock-style wrapper into one helper reused at every site. Broad but low-risk; keep it a pure refactor (no behavior change) in its own PR.Acceptance criteria
.unwrap_or_elselock sites inengine.rsandengine_trait.rsdelegate to it.cargo test -p ansible-corepasses with no changes to test logic or assertions.cargo clippy -p ansible-core -- -D warningsis clean.log::warn!message wording changes that would break any log-scraping in CI.Traceability
clawpatch finding ID:
fnd_sig-feat-service-25552574c9-8c3e_f8b3f1b6efRevalidate command:
No coupled issues at time of filing.