Preemption & Priority Inheritance
Priority inheritance stops a low-priority plan holding a shared resource from blocking a high-priority safety action.
The priority-inversion trap
Suppose a low-priority diagnostic campaign holds a shared calibration resource, and a high-priority safety action needs it. Without care, the high-priority action waits on the low-priority holder while medium-priority work runs ahead of both, a classic priority inversion. On a machine where the high-priority action may be a quench abort, that delay is unacceptable.
Inheritance rule
def request(resource, requester):
holder = resource.holder
if holder and requester.priority < holder.priority:
holder.effective_priority = requester.priority # inherit, temporarily
# holder now runs at high priority to release the resource fast
return resource.acquire_when_free(requester)
Graceful release, not seizure
Inheritance raises the holder's effective priority so it finishes or safely yields the resource quickly, rather than being violently seized mid-operation. For a breeder procedure that means running its release compensation at elevated priority; for the burner it means completing an atomic sub-step before yielding. This preserves the machine's safe-state guarantees during preemption (see scheduler).
Safety actions bypass
- P0 safety actions do not queue behind resource holders; they use pre-authorized emergency paths.
- For non-safety preemption, inheritance bounds the maximum blocking time to one holder's release.
- Nested inheritance is bounded so a chain of holders cannot compound the delay unboundedly.
Anti-starvation pairing
Inheritance solves inversion; aging (raising the priority of long-waiting plans) solves starvation. Together they keep both the breeder and burner responsive to urgent needs while ensuring opportunistic work eventually runs. Every inheritance and preemption event is logged for replay so scheduling behavior is reproducible.