Behaviour for scheduling strategies.
A strategy is a pure module + state. The controller threads the state through every interaction.
Built-in strategies:
Lockstep.Strategy.Random— uniform random over the ready set.Lockstep.Strategy.PCT— Burckhardt et al., ASPLOS 2010 (Probabilistic Concurrency Testing).Lockstep.Strategy.FairPCT— PCT then random, for liveness.Lockstep.Strategy.POS— Yuan et al., OOPSLA 2025 (Partial- Order Sampling).Lockstep.Strategy.IDPCT— Iterative-deepening PCT: cyclesbug_depthacross iterations so unknown-depth bugs are covered without manual tuning.Lockstep.Strategy.Replay— picks per a recorded trace.
Choosing a strategy
Different races are most reliably caught by different strategies. In our experience:
:pct(the default) is the all-purpose choice. PCT samples schedules according to a bug-depth bound d: a bug requiring at most d well-placed priority swaps is found with probability1 / (n * k^(d-1))per iteration, where n is the step count and k the number of procs. Strong on classic data races (lost-update, TOCTOU) and partial-order-shaped bugs.:posuniformly samples partial orders. Better than PCT when the bug requires the strategy to consistently pick a specific proc over several consecutive sync points — PCT's priority-shuffle under-explores those long runs, but POS's uniform sampling hits them. The leader-follower async replication race intest/leader_follower_register_test.exsis a real example: PCT didn't find it in 100 iterations, POS found it on iteration 1.Rule of thumb: if PCT can't find your race, try POS before cranking iteration counts.
:idpctruns PCT but cyclesbug_depthacross iterations (default[2, 6]). Use when you don't know how deep your bug is. Iterations at depth 2 are fast and find shallow bugs quickly; iterations at depth 6 catch deeper races. Costs nothing extra per iteration vs. fixed-depth PCT, but amortizes coverage across the depth range. Particularly nice for runs of 1000+ iterations.:fair_pctis PCT for the first K steps, then a fair random walk. Use when your test has long-running infrastructure (heartbeats, timers, supervisors) that would otherwise dominate the schedule under pure PCT.:randomis the baseline — uniform random over ready procs each step. Useful as a sanity check or for stress workloads where the bug is high-probability under random scheduling.:replayisn't really for hunting bugs — it follows a saved trace exactly, with optional random fallback for partial schedules. Used by the shrinker.
Strategies are deterministic given a seed, so any iteration that finds a bug is reproducible (and shrinkable).