Class: Henitai::SlotScheduler

Inherits:
Object
  • Object
show all
Includes:
Draining, ProcessControl
Defined in:
lib/henitai/slot_scheduler.rb,
lib/henitai/slot_scheduler/draining.rb,
lib/henitai/slot_scheduler/process_control.rb

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: Slot

Constant Summary collapse

PROCESS_DRAIN_WINDOW =
0.2

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Draining

#check_timeouts, #drain_draining_slots, #draining_slots?, #interrupt_active_slots

Constructor Details

#initialize(integration:, config:, progress_reporter:, options:, host:) ⇒ SlotScheduler

Returns a new instance of SlotScheduler.



37
38
39
40
41
42
43
44
45
46
47
48
49
# File 'lib/henitai/slot_scheduler.rb', line 37

def initialize(integration:, config:, progress_reporter:, options:, host:)
  @integration = integration
  @config = config
  @progress_reporter = progress_reporter
  @options = options
  @host = host
  @pending = []
  @slots = {}
  @pid_to_slot = {}
  @results = []
  @flaky_retry_count = 0
  @next_slot_id = 0
end

Instance Attribute Details

#flaky_retry_countInteger, Array<ScenarioExecutionResult> (readonly)

Returns:

  • (Integer)

    mutants that required at least one retry during the run.

  • (Array<ScenarioExecutionResult>)

    verdicts accumulated so far.



35
36
37
# File 'lib/henitai/slot_scheduler.rb', line 35

def flaky_retry_count
  @flaky_retry_count
end

#resultsInteger, Array<ScenarioExecutionResult> (readonly)

Returns:

  • (Integer)

    mutants that required at least one retry during the run.

  • (Array<ScenarioExecutionResult>)

    verdicts accumulated so far.



35
36
37
# File 'lib/henitai/slot_scheduler.rb', line 35

def results
  @results
end

Instance Method Details

#done?Boolean

Returns:

  • (Boolean)


56
57
58
# File 'lib/henitai/slot_scheduler.rb', line 56

def done?
  pending.empty? && slots.empty?
end

#enqueue(mutants) ⇒ Object

Queues the mutants to be scheduled into worker slots.



52
53
54
# File 'lib/henitai/slot_scheduler.rb', line 52

def enqueue(mutants)
  @pending = mutants.dup
end

#fill_idle_slotsObject



60
61
62
63
64
65
# File 'lib/henitai/slot_scheduler.rb', line 60

def fill_idle_slots
  while slots.size < worker_count && !pending.empty?
    mutant = pending.shift
    spawn_into_slot(mutant)
  end
end

#next_event_timeoutObject



78
79
80
81
82
83
84
85
# File 'lib/henitai/slot_scheduler.rb', line 78

def next_event_timeout
  now = monotonic_time
  slot_timeouts = slots.each_value.filter_map do |slot|
    remaining_slot_timeout(slot, now)
  end

  slot_timeouts.min
end

#reap_all_completed_childrenObject



67
68
69
70
71
72
73
74
75
76
# File 'lib/henitai/slot_scheduler.rb', line 67

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