Class: Origen::Generator::PatternThread
- Defined in:
- lib/origen/generator/pattern_thread.rb
Overview
An instance of PatternThread is created for each parallel thread of execution in a pattern sequence. One instance of this class is also created to represent the original main thread in addition to those created by calling seq.in_parallel
Instance Attribute Summary collapse
-
#cycle_count_start ⇒ Object
readonly
Returns the value of attribute cycle_count_start.
-
#cycle_count_stop ⇒ Object
readonly
Returns the value of attribute cycle_count_stop.
-
#events ⇒ Object
readonly
A record of when the thread is active to construct the execution profile.
-
#id ⇒ Object
readonly
Returns the value of attribute id.
-
#pending_cycles ⇒ Object
readonly
Returns the value of attribute pending_cycles.
-
#reservations ⇒ Object
readonly
Returns the value of attribute reservations.
-
#sequence ⇒ Object
readonly
Returns the parent pattern sequence object.
Instance Method Summary collapse
-
#advance(_completed_cycles = nil) ⇒ Object
Resume this context and return when it reaches its next cycle/wait point.
- #completed? ⇒ Boolean
- #current_cycle_count ⇒ Object
-
#cycle(options) ⇒ Object
Will be called when the thread is ready for the next cycle.
- #executed_cycles(cycles) ⇒ Object private
- #execution_profile(start, stop, step) ⇒ Object
-
#initialize(id, sequence, block, primary = false, pre_block = nil) ⇒ PatternThread
constructor
A new instance of PatternThread.
-
#primary? ⇒ Boolean
Returns true if this is main thread (the one from which all in_parallel threads have been branched from).
- #record_active ⇒ Object
- #record_cycle_count_stop ⇒ Object
-
#start ⇒ Object
private
This method is called once by the pattern sequence to start a new cooperative execution context.
-
#wait ⇒ Object
Yield to the pattern sequence until this context is advanced again.
-
#waiting? ⇒ Boolean
Returns true if the thread is currently waiting for the pattern sequence to advance it.
-
#waiting_for_serialize(serialize_id, skip_event = false) ⇒ Object
Will be called when the thread can't execute its next cycle because it is waiting to obtain a lock on a serialized block.
-
#waiting_for_thread(skip_event = false) ⇒ Object
Will be called when the thread can't execute its next cycle because it is waiting for another thread to complete.
Constructor Details
#initialize(id, sequence, block, primary = false, pre_block = nil) ⇒ PatternThread
Returns a new instance of PatternThread.
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
# File 'lib/origen/generator/pattern_thread.rb', line 17 def initialize(id, sequence, block, primary = false, pre_block = nil) if primary @cycle_count_start = 0 else @cycle_count_start = current_cycle_count end @events = [[:active, cycle_count_start]] @id = id.to_sym @sequence = sequence @block = block @pre_block = pre_block @primary = primary @waiting = false @pending_cycles = nil @completed = false @reservations = {} end |
Instance Attribute Details
#cycle_count_start ⇒ Object (readonly)
Returns the value of attribute cycle_count_start.
12 13 14 |
# File 'lib/origen/generator/pattern_thread.rb', line 12 def cycle_count_start @cycle_count_start end |
#cycle_count_stop ⇒ Object (readonly)
Returns the value of attribute cycle_count_stop.
13 14 15 |
# File 'lib/origen/generator/pattern_thread.rb', line 13 def cycle_count_stop @cycle_count_stop end |
#events ⇒ Object (readonly)
A record of when the thread is active to construct the execution profile
15 16 17 |
# File 'lib/origen/generator/pattern_thread.rb', line 15 def events @events end |
#id ⇒ Object (readonly)
Returns the value of attribute id.
10 11 12 |
# File 'lib/origen/generator/pattern_thread.rb', line 10 def id @id end |
#pending_cycles ⇒ Object (readonly)
Returns the value of attribute pending_cycles.
9 10 11 |
# File 'lib/origen/generator/pattern_thread.rb', line 9 def pending_cycles @pending_cycles end |
#reservations ⇒ Object (readonly)
Returns the value of attribute reservations.
11 12 13 |
# File 'lib/origen/generator/pattern_thread.rb', line 11 def reservations @reservations end |
#sequence ⇒ Object (readonly)
Returns the parent pattern sequence object
8 9 10 |
# File 'lib/origen/generator/pattern_thread.rb', line 8 def sequence @sequence end |
Instance Method Details
#advance(_completed_cycles = nil) ⇒ Object
Resume this context and return when it reaches its next cycle/wait point.
177 178 179 |
# File 'lib/origen/generator/pattern_thread.rb', line 177 def advance(_completed_cycles = nil) resume end |
#completed? ⇒ Boolean
157 158 159 |
# File 'lib/origen/generator/pattern_thread.rb', line 157 def completed? @completed end |
#current_cycle_count ⇒ Object
71 72 73 |
# File 'lib/origen/generator/pattern_thread.rb', line 71 def current_cycle_count tester.try(:cycle_count) || 0 end |
#cycle(options) ⇒ Object
Will be called when the thread is ready for the next cycle
125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 |
# File 'lib/origen/generator/pattern_thread.rb', line 125 def cycle() @pending_cycles = [:repeat] || 1 # If there are threads pending start and we are about to enter a long delay, block for only # one cycle to give them a change to get underway and make use of this delay if @pending_cycles > 1 && sequence.send(:threads_waiting_to_start?) remainder = @pending_cycles - 1 @pending_cycles = 1 end loop do wait if remainder @pending_cycles = remainder remainder = nil end # If another context requested fewer cycles, keep yielding the same # request until the coordinator has consumed it. The previous recursive # call to Integer#cycles retained one Ruby stack frame per partial # advance and could overflow on long concurrent delays. if @pending_cycles == 0 @pending_cycles = nil break elsif @pending_cycles < 0 fail "Something has gone wrong @pending_cycles is #{@pending_cycles}" end end end |
#executed_cycles(cycles) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
153 154 155 |
# File 'lib/origen/generator/pattern_thread.rb', line 153 def executed_cycles(cycles) @pending_cycles -= cycles if @pending_cycles end |
#execution_profile(start, stop, step) ⇒ Object
75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 |
# File 'lib/origen/generator/pattern_thread.rb', line 75 def execution_profile(start, stop, step) events = @events.dup cycles = start state = :inactive line = '' ((stop - start) / step).times do |i| active_cycles = 0 while events.first && events.first[1] >= cycles && events.first[1] < cycles + step event = events.shift # Bring the current cycles up to this event point applying the current state if state == :active active_cycles += event[1] - cycles end state = event[0] == :active ? :active : :inactive cycles = event[1] end # Bring the current cycles up to the end of this profile tick if state == :active active_cycles += ((i + 1) * step) - cycles end cycles = ((i + 1) * step) if active_cycles == 0 line += '_' elsif active_cycles > (step * 0.5) line += '█' else line += '▄' end end line end |
#primary? ⇒ Boolean
Returns true if this is main thread (the one from which all in_parallel threads have been branched from)
37 38 39 |
# File 'lib/origen/generator/pattern_thread.rb', line 37 def primary? @primary end |
#record_active ⇒ Object
67 68 69 |
# File 'lib/origen/generator/pattern_thread.rb', line 67 def record_active events << [:active, current_cycle_count] end |
#record_cycle_count_stop ⇒ Object
61 62 63 64 65 |
# File 'lib/origen/generator/pattern_thread.rb', line 61 def record_cycle_count_stop @cycle_count_stop = current_cycle_count events << [:stopped, cycle_count_stop] events.freeze end |
#start ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
This method is called once by the pattern sequence to start a new cooperative execution context. Pattern sequences intentionally run only one context at a time to keep generated output deterministic, so an OS thread and two synchronization events only add context-switching overhead. A Fiber provides the same yield/resume behavior without involving the thread scheduler.
48 49 50 51 52 53 54 55 56 57 58 59 |
# File 'lib/origen/generator/pattern_thread.rb', line 48 def start @fiber = Fiber.new do wait @pre_block.call if @pre_block @block.call(sequence) sequence.send(:thread_completed, self) record_cycle_count_stop @completed = true wait end resume end |
#wait ⇒ Object
Yield to the pattern sequence until this context is advanced again.
167 168 169 170 171 172 173 174 |
# File 'lib/origen/generator/pattern_thread.rb', line 167 def wait @waiting = true Fiber.yield # Thread-local variables are fiber-local on Ruby 2.6, so restore the # active context from inside the resumed fiber as well. PatSeq.send(:thread=, self) @waiting = false end |
#waiting? ⇒ Boolean
Returns true if the thread is currently waiting for the pattern sequence to advance it
162 163 164 |
# File 'lib/origen/generator/pattern_thread.rb', line 162 def waiting? @waiting end |
#waiting_for_serialize(serialize_id, skip_event = false) ⇒ Object
Will be called when the thread can't execute its next cycle because it is waiting to obtain a lock on a serialized block
111 112 113 114 115 |
# File 'lib/origen/generator/pattern_thread.rb', line 111 def waiting_for_serialize(serialize_id, skip_event = false) # puts "Thread #{id} is blocked waiting for #{serialize_id}" events << [:waiting, current_cycle_count] unless skip_event wait end |
#waiting_for_thread(skip_event = false) ⇒ Object
Will be called when the thread can't execute its next cycle because it is waiting for another thread to complete
119 120 121 122 |
# File 'lib/origen/generator/pattern_thread.rb', line 119 def waiting_for_thread(skip_event = false) events << [:waiting, current_cycle_count] unless skip_event wait end |