Class: Cyclotone::Scheduler
- Inherits:
-
Object
- Object
- Cyclotone::Scheduler
- Defined in:
- lib/cyclotone/scheduler.rb
Defined Under Namespace
Classes: SystemClock
Constant Summary collapse
- LOOKAHEAD =
0.3- INTERVAL =
0.05- DEFAULT_CPS =
Rational(9, 16)
- DEFAULT_STOP_TIMEOUT =
1.0- SENT_RETAIN_CYCLES =
4
Instance Attribute Summary collapse
-
#backend ⇒ Object
Returns the value of attribute backend.
-
#cps ⇒ Object
readonly
Returns the value of attribute cps.
-
#interval ⇒ Object
readonly
Returns the value of attribute interval.
-
#interval_cycles ⇒ Object
readonly
Returns the value of attribute interval_cycles.
-
#lookahead ⇒ Object
readonly
Returns the value of attribute lookahead.
-
#lookahead_cycles ⇒ Object
readonly
Returns the value of attribute lookahead_cycles.
-
#metrics ⇒ Object
readonly
Returns the value of attribute metrics.
Instance Method Summary collapse
- #current_cycle(now = monotonic_time) ⇒ Object
-
#initialize(backend:, cps: DEFAULT_CPS, lookahead: LOOKAHEAD, interval: INTERVAL, lookahead_cycles: nil, interval_cycles: nil, logger: nil, retry_failed: false, clock: nil) ⇒ Scheduler
constructor
A new instance of Scheduler.
- #last_error ⇒ Object
- #remove_pattern(slot_id) ⇒ Object
- #render(duration:, at: 0.0) ⇒ Object
- #reset_cycles ⇒ Object
- #running? ⇒ Boolean
- #set_cycle(value) ⇒ Object
- #setcps(value) ⇒ Object
- #start ⇒ Object
- #stop(timeout: DEFAULT_STOP_TIMEOUT) ⇒ Object
- #tick(now = monotonic_time) ⇒ Object
- #update_pattern(slot_id, pattern, cps: nil, phase: 0) ⇒ Object
Constructor Details
#initialize(backend:, cps: DEFAULT_CPS, lookahead: LOOKAHEAD, interval: INTERVAL, lookahead_cycles: nil, interval_cycles: nil, logger: nil, retry_failed: false, clock: nil) ⇒ Scheduler
Returns a new instance of Scheduler.
22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
# File 'lib/cyclotone/scheduler.rb', line 22 def initialize( backend:, cps: DEFAULT_CPS, lookahead: LOOKAHEAD, interval: INTERVAL, lookahead_cycles: nil, interval_cycles: nil, logger: nil, retry_failed: false, clock: nil ) @backend = backend @cps = normalize_cps(cps) @lookahead = lookahead @interval = interval @lookahead_cycles = lookahead_cycles&.to_f @interval_cycles = interval_cycles&.to_f @logger = logger @retry_failed = retry_failed @clock = clock || SystemClock.new @mutex = Mutex.new @patterns = {} @sent = {} @running = false @thread = nil @last_error = nil @start_monotonic = monotonic_time @start_wall_time = wall_time @start_cycle = 0.0 @last_cycle = 0.0 @metrics = { ticks: 0, last_tick_duration: 0.0, max_tick_duration: 0.0 } end |
Instance Attribute Details
#backend ⇒ Object
Returns the value of attribute backend.
20 21 22 |
# File 'lib/cyclotone/scheduler.rb', line 20 def backend @backend end |
#cps ⇒ Object (readonly)
Returns the value of attribute cps.
20 21 22 |
# File 'lib/cyclotone/scheduler.rb', line 20 def cps @cps end |
#interval ⇒ Object (readonly)
Returns the value of attribute interval.
20 21 22 |
# File 'lib/cyclotone/scheduler.rb', line 20 def interval @interval end |
#interval_cycles ⇒ Object (readonly)
Returns the value of attribute interval_cycles.
20 21 22 |
# File 'lib/cyclotone/scheduler.rb', line 20 def interval_cycles @interval_cycles end |
#lookahead ⇒ Object (readonly)
Returns the value of attribute lookahead.
20 21 22 |
# File 'lib/cyclotone/scheduler.rb', line 20 def lookahead @lookahead end |
#lookahead_cycles ⇒ Object (readonly)
Returns the value of attribute lookahead_cycles.
20 21 22 |
# File 'lib/cyclotone/scheduler.rb', line 20 def lookahead_cycles @lookahead_cycles end |
#metrics ⇒ Object (readonly)
Returns the value of attribute metrics.
20 21 22 |
# File 'lib/cyclotone/scheduler.rb', line 20 def metrics @metrics end |
Instance Method Details
#current_cycle(now = monotonic_time) ⇒ Object
162 163 164 |
# File 'lib/cyclotone/scheduler.rb', line 162 def current_cycle(now = monotonic_time) @mutex.synchronize { time_to_cycle(now, @cps, @start_cycle, @start_monotonic) } end |
#last_error ⇒ Object
97 98 99 |
# File 'lib/cyclotone/scheduler.rb', line 97 def last_error @mutex.synchronize { @last_error } end |
#remove_pattern(slot_id) ⇒ Object
122 123 124 125 126 127 |
# File 'lib/cyclotone/scheduler.rb', line 122 def remove_pattern(slot_id) @mutex.synchronize do @patterns.delete(slot_id) @sent.delete_if { |key, _| key.first == slot_id } end end |
#render(duration:, at: 0.0) ⇒ Object
170 171 172 173 174 175 176 177 178 179 180 181 182 183 |
# File 'lib/cyclotone/scheduler.rb', line 170 def render(duration:, at: 0.0) duration_value = duration.to_f raise ArgumentError, "render duration must be non-negative" if duration_value.negative? state = snapshot_state.merge(start_wall_time: at.to_f) state[:backend].begin_capture(at: state[:start_wall_time]) if state[:backend].respond_to?(:begin_capture) logical_end = state[:start_cycle] + (duration_value * state[:cps]) dispatch_until(logical_end, state) state[:backend].end_capture if state[:backend].respond_to?(:end_capture) state[:backend].write! if state[:backend].respond_to?(:write!) self end |
#reset_cycles ⇒ Object
144 145 146 |
# File 'lib/cyclotone/scheduler.rb', line 144 def reset_cycles set_cycle(0) end |
#running? ⇒ Boolean
166 167 168 |
# File 'lib/cyclotone/scheduler.rb', line 166 def running? @mutex.synchronize { @running } end |
#set_cycle(value) ⇒ Object
148 149 150 151 152 153 154 155 156 |
# File 'lib/cyclotone/scheduler.rb', line 148 def set_cycle(value) @mutex.synchronize do @start_cycle = value.to_f @start_monotonic = monotonic_time @start_wall_time = wall_time @last_cycle = value.to_f @sent.clear end end |
#setcps(value) ⇒ Object
129 130 131 132 133 134 135 136 137 138 139 140 141 142 |
# File 'lib/cyclotone/scheduler.rb', line 129 def setcps(value) normalized_cps = normalize_cps(value) now = monotonic_time wall_now = wall_time @mutex.synchronize do current_cycle = time_to_cycle(now, @cps, @start_cycle, @start_monotonic) @start_cycle = current_cycle @start_monotonic = now @start_wall_time = wall_now @cps = normalized_cps end end |
#start ⇒ Object
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 |
# File 'lib/cyclotone/scheduler.rb', line 55 def start @mutex.synchronize do return self if @running @running = true @last_error = nil @thread = Thread.new do Thread.current.abort_on_exception = false while running? tick_started = monotonic_time begin tick(tick_started) rescue StandardError => error log_runtime_error(error) end record_tick_duration(monotonic_time - tick_started) sleep(current_interval) end end end self end |
#stop(timeout: DEFAULT_STOP_TIMEOUT) ⇒ Object
82 83 84 85 86 87 88 89 90 91 92 93 94 95 |
# File 'lib/cyclotone/scheduler.rb', line 82 def stop(timeout: DEFAULT_STOP_TIMEOUT) thread = @mutex.synchronize do @running = false @thread end thread&.join(timeout) @mutex.synchronize do @thread = nil if @thread == thread && !thread&.alive? end self end |
#tick(now = monotonic_time) ⇒ Object
101 102 103 104 105 106 107 108 109 110 |
# File 'lib/cyclotone/scheduler.rb', line 101 def tick(now = monotonic_time) state = snapshot_state current_cycle = time_to_cycle(now, state[:cps], state[:start_cycle], state[:start_monotonic]) logical_end = if lookahead_cycles current_cycle + lookahead_cycles else time_to_cycle(now + lookahead, state[:cps], state[:start_cycle], state[:start_monotonic]) end dispatch_until(logical_end, state) end |
#update_pattern(slot_id, pattern, cps: nil, phase: 0) ⇒ Object
112 113 114 115 116 117 118 119 120 |
# File 'lib/cyclotone/scheduler.rb', line 112 def update_pattern(slot_id, pattern, cps: nil, phase: 0) @mutex.synchronize do @patterns[slot_id] = { pattern: Pattern.ensure_pattern(pattern), cps: cps&.to_f, phase: Pattern.to_rational(phase) } end end |