From Fairness to Search: Temporal Isolation Inside SECT
SECT’s distinctive scheduling policy is temporal isolation: inside one test domain, it allows one eligible task to run at a time and turns the moments between tasks into explicit choices. This chapter focuses on what that rule actually serializes, why a sequence of choices is more useful than uncontrolled parallel timing for concurrency exploration, and how SECT keeps separate test instances from blocking one another. The result is not a globally paused Linux system. It is domain-local control layered on the sched_ext dispatch boundary.
One domain, one active task, one selectable event
A conventional scheduler sees several runnable tasks as work that can share available CPUs. A concurrency explorer sees those same tasks as sources of events whose order may determine the result. If two tasks access related state, allowing both to run at once can produce an execution that is fast but difficult to characterize: several operations may overlap, and the tester may not know which ordering was responsible for the observed outcome. SECT changes that unit of control. For the tasks assigned to one test domain, it dispatches one eligible task and withholds another until the running task reaches a boundary such as a block, a voluntary yield, or the end of its available execution slice.
This is temporal isolation. It does not mean that the kernel executes one instruction globally, or that every task on the machine is frozen. It means that the controlled tasks in a domain are not allowed to accumulate multiple active executions at the same time. The scheduler can therefore observe a sequence: task A runs until its next scheduling opportunity, then task B is selected, then perhaps task A or task C. The concurrent program still contains multiple tasks and shared state; what changes is that their relevant execution is presented as an ordered series of choices rather than as an uncontrolled mixture of parallel progress.
That change makes an interleaving a test input in its own right. Suppose two tasks each have several points at which they can affect shared state. Under ordinary scheduling, the relative order of those points is an indirect consequence of CPU availability, wakeups, and timing. Under temporal isolation, the explorer can choose which runnable task receives the next interval. A sequence such as A–A–B–B differs from A–B–A–B even if both eventually complete the same system-call program. The scheduler is not proving that either sequence is the only meaningful one; it is making the difference available for deliberate sampling.
The choice still occurs at scheduling boundaries, not at every machine instruction. A running task proceeds until the conditions for scheduler intervention are met. Some boundaries already exist in the kernel, such as blocking and slice expiration; SECT later adds selected points around concurrency-relevant operations. This distinction is important. Temporal isolation supplies the rule for choosing among tasks, but its resolution is limited by the places where the system can safely regain control. A domain can be serialized at one granularity and still miss a bug whose decisive operations occur between two unobserved points.
The policy objective is also different from ordinary fairness. Fairness tries to prevent a runnable task from being delayed indefinitely while keeping the system responsive. Exploration may intentionally favor one task, revisit a previous task, or pause a low-weight task because that choice creates a more informative ordering. SECT does not abandon progress altogether: its policy tracks which tasks are enabled and updates its choices as tasks block, wake, or exit. But it evaluates a scheduling decision by the interleaving it exposes, not only by how evenly it distributes CPU time.
This gives the scheduler a new kind of feedback. Under a production policy, a task that has run recently may be favored because it is cache-friendly or because fairness requires it to receive service. Under an exploration policy, the same history may be relevant for a different reason: the task’s last event may interfere with another task’s next event, or repeating it may test whether a state transition is stable. The policy is not optimizing a single universal notion of “good scheduling.” It is choosing a sequence that makes a concurrency hypothesis easier to exercise.
The task-state picture is simple enough to state and subtle enough to matter. A task begins as runnable within a domain. The policy dispatches it, and it becomes the one running task for that domain. When it yields, blocks, or exhausts its current opportunity, it becomes quiescent and is no longer eligible for immediate selection. When it becomes runnable again, it re-enters the candidate set. If it exits, it is removed rather than assigned another turn.
The self-transition in this model represents a new decision, not uninterrupted execution: the current task reaches a scheduling point, the policy updates the candidate set, and the next task may be the same task or another one. That detail prevents a common misunderstanding. Temporal isolation does not require a context switch after every event, nor does it guarantee that every decision changes the running task. It requires that the policy own the decision about which eligible task receives the next opportunity inside the domain.
The benefit is causal visibility. If a failure appears after the sequence A–B–A, the tester has a schedule description that can be replayed or varied. If the sequence A–A–B does not fail, that negative result helps narrow the ordering that matters. The result is still probabilistic and bounded by the available scheduling points, but it is more informative than a timing accident whose internal order is only inferred after the fact.
There is a cost. Serializing the controlled tasks gives up some parallel execution inside a test instance. A workload that depends on genuine simultaneous progress may behave differently when its tasks take turns. A deliberately unfair policy can also delay a task that kernel code expects to make progress. Temporal isolation is therefore an exploration primitive, not a claim that serialization is a better production scheduling policy. Its value appears when the cost of reducing parallelism is outweighed by the ability to expose an ordering that ordinary scheduling rarely produces.
Parallel test domains and the shadow state behind each choice
Applying temporal isolation to every task on the machine would waste most of the available hardware. SECT instead groups the tasks belonging to one test instance into a scheduling domain. The scheduler identifies that relationship from task attributes, such as a command name or group identifier, and applies the serialization discipline within the matching domain. Tasks from separate test instances can occupy separate domains and continue to run in parallel. One domain may be exploring a difficult interleaving while another is generating inputs or executing a different seed.
Domain partitioning changes the scalability trade-off without weakening the central rule. Within a domain, the explorer sacrifices parallelism to gain schedule control. Across domains, it retains parallelism to increase campaign throughput. The unit of isolation is therefore the test instance, not the whole kernel. This also clarifies what a result means: a controlled schedule describes the selected tasks and their domain; it does not claim that all unrelated kernel activity was serialized or that the entire machine followed the same policy.
The boundary is useful precisely because test campaigns contain many independent opportunities for progress. One domain can spend time trying a difficult ordering while another runs a different syscall seed. If the scheduler imposed one global serialized sequence, the cost of exploring one stubborn case would be paid by every other case. If it applied no domain discipline, tasks from separate tests could distort one another’s scheduling decisions and make a replayed schedule harder to interpret. Domain membership is therefore both a performance partition and an evidence boundary: it says which tasks the policy is comparing when it makes a choice.
At each scheduling event, SECT first resolves the current task’s domain. If the task is sleeping, it is disabled for selection. If it is exiting, it is removed from the active set. Otherwise it remains eligible, and the policy updates the weights or priorities of tasks in that domain. The scheduler then considers the enabled candidates, selects the one with the highest current weight, dispatches it, records the next event, and repeats when control returns. The exact weight calculation belongs to the selected exploration strategy; the dispatch loop supplies the common place where different strategies can make their choice.
This separation is essential because ordinary sched_ext queues are designed to move runnable tasks efficiently, not to serve as a complete research notebook for a changing exploration policy. A native priority queue can order work, but the paper identifies a mismatch when a concurrency strategy needs to change task attributes after a task has already been enqueued. Policies such as Partial Order Sampling and Probabilistic Concurrency Testing may need to react to the most recent event, interference with another task, or a sampled priority interval. A queue that assumes its ordering metadata is stable cannot express all of those changes cleanly.
SECT therefore keeps a shadow scheduling metadata layer in eBPF maps. “Shadow” does not mean a second execution queue that secretly runs tasks; it means policy state that records how the explorer currently interprets the tasks already known to the kernel. The state includes domain membership, whether a task is eligible, its dynamically adjustable priority or weight, and other runtime attributes required by the active strategy. The native sched_ext machinery still dispatches the task, but the policy consults this additional layer to decide which task should be made runnable next.
The arrangement decouples two responsibilities. Dispatch logic knows how to hand a selected task to the local DSQ and how to respect the core scheduler’s lifecycle. Policy logic knows why one task should outrank another for the current exploration goal. Adding a new strategy can therefore change the weight-assignment function without rewriting the entire task-lifecycle mechanism. That is the concrete meaning of extensibility here: different exploration assumptions can share the same controlled execution path.
It also makes a decision explainable at the right level. The dispatch path can answer whether a task was runnable and whether the core accepted the placement. The shadow policy can answer why that task outranked its peers at that scheduling event. Keeping those questions separate helps distinguish a policy outcome from a kernel failure: a task may have been selected exactly as intended and still reveal a defect, or a task may have been unavailable because its lifecycle changed before the decision could be honored.
The shadow layer also introduces a coupling that ordinary scheduling can hide. Its view of a task is useful only if it follows the kernel’s actual state. A sleeping task must not remain eligible merely because an old weight is still present. An exiting task must be removed rather than selected again. A task that becomes runnable after a state change must re-enter the candidate set with the metadata the policy expects. If the policy’s map and the core’s custody state diverge, SECT could make a choice that is coherent in its own data but impossible for the kernel to honor.
That is why temporal isolation is better understood as a coordinated state machine than as a single “run one task” switch. The domain identifies which tasks are comparable. The lifecycle determines which of them can be selected now. The shadow metadata expresses the strategy’s current preference. The sched_ext dispatch path turns that preference into a runnable task, and the next yield or block supplies new information. Each layer constrains the next one.
The design also makes the limits visible. Domain partitioning preserves parallel campaigns, but it does not recover the execution throughput that was intentionally removed inside a domain. Shadow metadata enables dynamic policies, but it does not guarantee that their choices are fair or that the kernel code under test tolerates their unfairness. Temporal isolation can enumerate more meaningful choices only where control is actually regained. It is a way to make a large, implicit search space addressable—not a promise that the search space has become small or complete.
The central model is now in place: sched_ext supplies a guarded dispatch boundary, while SECT uses domain-local temporal isolation and policy state to decide which controlled task runs next. The remaining judgment is how to choose among those tasks and how to create enough safe scheduling opportunities for the choice to matter. Before turning to those mechanisms, it is worth holding together what this model has already changed: it makes scheduling an explicit exploration layer without pretending that it owns the whole machine.