Fixes a deadlock that hung call-graph tools like trace_to and callers forever when the first request after opening a project arrived while background index loading waited for a build slot.
Runs admission predicates after releasing the limiter lock instead of under it; the permit is installed first and dropped if refused.
Reserves a new slot's class turn when its permit is installed, so a request arriving while a predicate runs leaves the last free slot to a queued request of another class.
Withdraws a pending turn when its predicate refuses or panics, leaving the class turn and admission events as if the request had never happened.
Adds regression tests covering the deadlock on the new-slot and shared-root paths plus the class-turn reservation and refusal behavior.
Confidence Score: 5/5
The PR appears safe to merge; no actionable issues remain.
Summary
Moves admission predicates outside the limiter lock to prevent the cold-build deadlock.
Cold-build requests no longer deadlock while checking lifecycle state.
No new actionable issues were found. Tests were inspected, not run.
Diagram
sequenceDiagram
participant Request
participant Limiter
participant Predicate
Request->>Limiter: Install permit and reserve class turn under lock
Limiter-->>Request: Release lock
Request->>Predicate: Check admission
alt Accepted
Request->>Limiter: Settle turn and record event under lock
else Refused
Request->>Limiter: Withdraw turn, then drop permit
end