Class: Henitai::SlotScheduler
- Inherits:
-
Object
- Object
- Henitai::SlotScheduler
- Includes:
- Draining, ProcessControl
- Defined in:
- lib/henitai/slot_scheduler.rb,
lib/henitai/slot_scheduler/draining.rb,
lib/henitai/slot_scheduler/slot_table.rb,
lib/henitai/slot_scheduler/retry_policy.rb,
lib/henitai/slot_scheduler/drain_verdict.rb,
lib/henitai/slot_scheduler/slot_deadline.rb,
lib/henitai/slot_scheduler/process_control.rb,
lib/henitai/slot_scheduler/test_file_selection.rb,
sig/henitai.rbs
Overview
Owns the process-slot table for a single parallel mutation run.
ProcessWorkerRunner drives the event loop and OS signal handling and delegates every slot operation here: filling idle slots, reaping completed children, retrying flaky survivors, detecting timeouts and running the drain/broadcast state machine. Keeping the table behind one collaborator means Process.wait* has a single caller, so there are no races between threads reaping the same child.
The drain/timeout state machine lives in Draining; the low-level process
and signal primitives live in ProcessControl. host is the owning
ProcessWorkerRunner, which supplies runtime, wakeup, worker_count
and the shutdown flag.
Defined Under Namespace
Modules: Draining, ProcessControl Classes: DrainVerdict, RetryPolicy, SlotDeadline, SlotTable, TestFileSelection
Constant Summary collapse
- PROCESS_DRAIN_WINDOW =
0.2- WORKER_SLOT_ENV =
Environment variable exposing a stable worker-slot index (0..jobs-1) to each forked child so test suites can isolate shared resources per slot (e.g. "myapp_test_#ENV['HENITAI_WORKER_SLOT']"). A flaky-retry respawn keeps the original attempt's value.
"HENITAI_WORKER_SLOT"- Slot =
Instance Attribute Summary collapse
- #flaky_retry_count ⇒ Integer, Array<ScenarioExecutionResult> readonly
- #results ⇒ Integer, Array<ScenarioExecutionResult> readonly
Instance Method Summary collapse
- #check_timeouts ⇒ void
- #done? ⇒ Boolean
- #drain_draining_slots ⇒ void
- #draining_slots? ⇒ Boolean
-
#enqueue(mutants) ⇒ Array[Mutant]
Queues the mutants to be scheduled into worker slots.
- #fill_idle_slots ⇒ void
-
#initialize(integration:, config:, progress_reporter:, options:, host:, slot_table: SlotTable.new) ⇒ SlotScheduler
constructor
slot_tableis injectable so a spec can seed mid-run state, or assert against the table through its own public interface, without reaching a private reader here. - #interrupt_active_slots ⇒ void
- #next_event_timeout ⇒ Float?
- #reap_all_completed_children ⇒ void
Constructor Details
#initialize(integration:, config:, progress_reporter:, options:, host:, slot_table: SlotTable.new) ⇒ SlotScheduler
slot_table is injectable so a spec can seed mid-run state, or assert
against the table through its own public interface, without reaching a
private reader here.
rubocop:disable Metrics/ParameterLists -- every one of these is a distinct
collaborator supplied by ProcessWorkerRunner; bundling them into a context
object would only move the list.
54 55 56 57 58 59 60 61 62 63 64 65 |
# File 'lib/henitai/slot_scheduler.rb', line 54 def initialize(integration:, config:, progress_reporter:, options:, host:, slot_table: SlotTable.new) # rubocop:enable Metrics/ParameterLists @integration = integration @config = config @progress_reporter = progress_reporter @options = @host = host @slot_table = slot_table @pending = [] @results = [] @flaky_retry_count = 0 end |
Instance Attribute Details
#flaky_retry_count ⇒ Integer, Array<ScenarioExecutionResult> (readonly)
46 47 48 |
# File 'lib/henitai/slot_scheduler.rb', line 46 def flaky_retry_count @flaky_retry_count end |
#results ⇒ Integer, Array<ScenarioExecutionResult> (readonly)
46 47 48 |
# File 'lib/henitai/slot_scheduler.rb', line 46 def results @results end |
Instance Method Details
#check_timeouts ⇒ void
This method returns an undefined value.
645 |
# File 'sig/henitai.rbs', line 645
def check_timeouts: () -> void
|
#done? ⇒ Boolean
72 73 74 |
# File 'lib/henitai/slot_scheduler.rb', line 72 def done? pending.empty? && slot_table.empty? end |
#drain_draining_slots ⇒ void
This method returns an undefined value.
647 |
# File 'sig/henitai.rbs', line 647
def drain_draining_slots: () -> void
|
#draining_slots? ⇒ Boolean
646 |
# File 'sig/henitai.rbs', line 646
def draining_slots?: () -> bool
|
#enqueue(mutants) ⇒ Array[Mutant]
Queues the mutants to be scheduled into worker slots.
68 69 70 |
# File 'lib/henitai/slot_scheduler.rb', line 68 def enqueue(mutants) @pending = mutants.dup end |
#fill_idle_slots ⇒ void
This method returns an undefined value.
76 77 78 79 80 81 |
# File 'lib/henitai/slot_scheduler.rb', line 76 def fill_idle_slots while slot_table.size < worker_count && !pending.empty? mutant = pending.shift spawn_into_slot(mutant) end end |
#interrupt_active_slots ⇒ void
This method returns an undefined value.
648 |
# File 'sig/henitai.rbs', line 648
def interrupt_active_slots: () -> void
|
#next_event_timeout ⇒ Float?
94 95 96 97 98 99 100 101 |
# File 'lib/henitai/slot_scheduler.rb', line 94 def next_event_timeout now = monotonic_time slot_timeouts = slot_table.each_value.filter_map do |slot| slot_deadline.remaining(slot, now) end slot_timeouts.min end |
#reap_all_completed_children ⇒ void
This method returns an undefined value.
83 84 85 86 87 88 89 90 91 92 |
# File 'lib/henitai/slot_scheduler.rb', line 83 def reap_all_completed_children loop do pid, status = runtime.wait2(-1, Process::WNOHANG) break unless pid complete_slot(pid, status) end rescue Errno::ECHILD nil end |