Skip to content

Consolidate mutex poison-recovery boilerplate in transcription engine #13

Description

@BumpyClock

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

  1. 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.

  2. 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.

  3. 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.

  4. 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

  • A single poison-recovery helper (function or macro) exists; all 18 inline .unwrap_or_else lock sites in engine.rs and engine_trait.rs delegate to it.
  • The two silent condvar-wait sites (engine.rs:451, 565) either log a warning consistent with the policy or carry a comment explaining why silent recovery is intentional.
  • cargo test -p ansible-core passes with no changes to test logic or assertions.
  • cargo clippy -p ansible-core -- -D warnings is clean.
  • No log::warn! message wording changes that would break any log-scraping in CI.
  • Diff is behavior-identical: no changes to lock acquisition order, condvar usage, or error propagation paths.

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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    deslopifyFound by clawpatch deslopify reviewtech-debtCode quality / maintainability cleanup

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions