Class: Henitai::SlotScheduler::SlotTable
- Inherits:
-
Object
- Object
- Henitai::SlotScheduler::SlotTable
- 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
- #add(slot) ⇒ Object
- #any_draining? ⇒ Boolean
- #delete(slot_id) ⇒ Object
- #draining ⇒ Object
- #each_value ⇒ Object
- #empty? ⇒ Boolean
- #fetch(slot_id) ⇒ Object
-
#initialize ⇒ SlotTable
constructor
A new instance of SlotTable.
-
#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.
-
#next_slot_id! ⇒ Object
Monotonic and never reused.
- #register_pid(pid, slot_id) ⇒ Object
-
#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.
- #size ⇒ Object
Constructor Details
#initialize ⇒ SlotTable
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
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 |
#draining ⇒ Object
66 67 68 |
# File 'lib/henitai/slot_scheduler/slot_table.rb', line 66 def draining @slots.select { |_, slot| slot.draining } end |
#each_value ⇒ Object
33 |
# File 'lib/henitai/slot_scheduler/slot_table.rb', line 33 def each_value(&) = @slots.each_value(&) |
#empty? ⇒ 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 |
#size ⇒ Object
32 |
# File 'lib/henitai/slot_scheduler/slot_table.rb', line 32 def size = @slots.size |