Class: Henitai::SlotScheduler::SlotTable

Inherits:
Object
  • Object
show all
Defined in:
lib/henitai/slot_scheduler/slot_table.rb

Overview

The slot table for one parallel run: live slots by id, the pid -> slot_id reverse index, and the slot-id sequence.

Two indexes rather than one because the two lookups have different lifetimes. A slot survives a flaky retry and keeps its id, but its pid changes with every respawn, so the reverse index is rebuilt while the forward entry stays put.

Instance Method Summary collapse

Constructor Details

#initializeSlotTable

Returns a new instance of SlotTable.



13
14
15
16
17
# File 'lib/henitai/slot_scheduler/slot_table.rb', line 13

def initialize
  @slots = {}
  @pid_to_slot = {}
  @next_slot_id = 0
end

Instance Method Details

#add(slot) ⇒ Object



19
20
21
# File 'lib/henitai/slot_scheduler/slot_table.rb', line 19

def add(slot)
  @slots[slot.slot_id] = slot
end

#any_draining?Boolean

Returns:

  • (Boolean)


70
71
72
# File 'lib/henitai/slot_scheduler/slot_table.rb', line 70

def any_draining?
  @slots.any? { |_, slot| slot.draining }
end

#delete(slot_id) ⇒ Object



23
24
25
# File 'lib/henitai/slot_scheduler/slot_table.rb', line 23

def delete(slot_id)
  @slots.delete(slot_id)
end

#drainingObject



66
67
68
# File 'lib/henitai/slot_scheduler/slot_table.rb', line 66

def draining
  @slots.select { |_, slot| slot.draining }
end

#each_valueObject



33
# File 'lib/henitai/slot_scheduler/slot_table.rb', line 33

def each_value(&) = @slots.each_value(&)

#empty?Boolean

Returns:

  • (Boolean)


31
# File 'lib/henitai/slot_scheduler/slot_table.rb', line 31

def empty? = @slots.empty?

#fetch(slot_id) ⇒ Object



27
28
29
# File 'lib/henitai/slot_scheduler/slot_table.rb', line 27

def fetch(slot_id)
  @slots[slot_id]
end

#next_free_worker_index(worker_count) ⇒ Object

Smallest index in 0...worker_count not held by a live slot, so concurrently-running children always see distinct values and freed indices are reused. Slot ids themselves grow monotonically and are unsuitable as a resource token.

The || used.size fallback covers a table already holding at least worker_count slots — reachable when a retry respawns into a table that a concurrent fill has since topped up.



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

def next_free_worker_index(worker_count)
  used = @slots.each_value.map(&:worker_index)
  (0...worker_count).find { |index| !used.include?(index) } || used.size
end

#next_slot_id!Object

Monotonic and never reused. Freed worker indices are recycled; slot ids are not, which is what makes them safe as hash keys across retries.



47
48
49
50
51
# File 'lib/henitai/slot_scheduler/slot_table.rb', line 47

def next_slot_id!
  id = @next_slot_id
  @next_slot_id += 1
  id
end

#register_pid(pid, slot_id) ⇒ Object



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

def register_pid(pid, slot_id)
  @pid_to_slot[pid] = slot_id
end

#release_pid(pid) ⇒ Object

Removes the mapping and answers the slot id it held, so a reap is a single operation: a pid can only be claimed once.



41
42
43
# File 'lib/henitai/slot_scheduler/slot_table.rb', line 41

def release_pid(pid)
  @pid_to_slot.delete(pid)
end

#sizeObject



32
# File 'lib/henitai/slot_scheduler/slot_table.rb', line 32

def size = @slots.size