Class: Musa::Clock::Timer
Overview
High-precision timer for generating regular ticks.
Timer uses Ruby's monotonic clock (Process::CLOCK_MONOTONIC) for drift-free timing. It compensates for processing delays and reports when the system cannot keep up with the requested tick rate.
Precision Features
- Monotonic clock: Immune to system time changes
- Drift compensation: Calculates exact next tick time
- Overload detection: Reports delayed ticks when processing is slow
- Correction parameter: Fine-tune timing for specific systems
Usage Pattern
Timer is typically used internally by TimerClock, not directly. It runs in a loop, yielding for each tick and managing precise sleep intervals.
Timing Algorithm
- Record next expected tick time
- Yield (caller processes tick)
- Add period to next_moment
- Calculate sleep time = (next_moment + correction) - current_time
- Sleep if positive, warn if negative (delayed)
Instance Attribute Summary collapse
-
#period ⇒ Rational
The period between ticks in seconds.
Instance Method Summary collapse
-
#continue ⇒ void
Resumes the timer after being stopped.
-
#initialize(tick_period_in_seconds, correction: nil, stop: nil, delayed_ticks_error: nil, logger: nil, do_log: nil) ⇒ Timer
constructor
Creates a new precision timer.
-
#run { ... } ⇒ void
Runs the timer loop, yielding for each tick.
-
#stop ⇒ void
Pauses the timer without terminating the loop.
-
#terminate ⇒ void
Terminates the timer loop permanently.
Constructor Details
#initialize(tick_period_in_seconds, correction: nil, stop: nil, delayed_ticks_error: nil, logger: nil, do_log: nil) ⇒ Timer
Creates a new precision timer.
52 53 54 55 56 57 58 59 60 61 |
# File 'lib/musa-dsl/transport/timer.rb', line 52 def initialize(tick_period_in_seconds, correction: nil, stop: nil, delayed_ticks_error: nil, logger: nil, do_log: nil) @period = tick_period_in_seconds.rationalize @correction = (correction || 0r).rationalize @stop = stop || false @terminate = false @delayed_ticks_error = delayed_ticks_error || 1.0 @logger = logger @do_log = do_log end |
Instance Attribute Details
#period ⇒ Rational
The period between ticks in seconds.
38 39 40 |
# File 'lib/musa-dsl/transport/timer.rb', line 38 def period @period end |
Instance Method Details
#continue ⇒ void
Resets timing baseline to prevent tick accumulation
This method returns an undefined value.
Resumes the timer after being stopped.
Resets the next tick moment to avoid a burst of catchup ticks, then wakes the timer thread.
139 140 141 142 143 |
# File 'lib/musa-dsl/transport/timer.rb', line 139 def continue @stop = false @next_moment = Process.clock_gettime(Process::CLOCK_MONOTONIC) @thread.run end |
#run { ... } ⇒ void
This method blocks the current thread until #terminate is called
Uses monotonic clock for drift-free timing
This method returns an undefined value.
Runs the timer loop, yielding for each tick.
This method blocks and runs until terminated. For each tick:
- Yields to caller if not stopped
- Calculates next tick time
- Sleeps precisely until next tick
- Logs warnings if timing cannot be maintained
When stopped (@stop = true), the thread sleeps until #continue is called. When terminated (@terminate = true), the loop exits and this method returns.
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 108 109 110 111 112 113 114 115 116 |
# File 'lib/musa-dsl/transport/timer.rb', line 79 def run @thread = Thread.current @terminate = false @next_moment = Process.clock_gettime(Process::CLOCK_MONOTONIC) loop do break if @terminate unless @stop # Process the tick yield # Calculate next tick moment (compensates for processing time) @next_moment += @period to_sleep = (@next_moment + @correction) - Process.clock_gettime(Process::CLOCK_MONOTONIC) # Log timing issues if enabled if @do_log && to_sleep.negative? & @logger tick_errors = -to_sleep / @period if tick_errors >= @delayed_ticks_error @logger.error "Timer delayed #{tick_errors.round(2)} ticks (#{-to_sleep.round(3)}s)" else @logger.warn "Timer delayed #{tick_errors.round(2)} ticks (#{-to_sleep.round(3)}s)" end end # Sleep precisely until next tick (if not already late) sleep to_sleep if to_sleep > 0.0 end # When stopped, sleep thread until continue or terminate is called if @stop break if @terminate sleep end end end |
#stop ⇒ void
This method returns an undefined value.
Pauses the timer without terminating the loop.
The timer thread sleeps until #continue is called. Ticks are not generated while stopped.
126 127 128 |
# File 'lib/musa-dsl/transport/timer.rb', line 126 def stop @stop = true end |
#terminate ⇒ void
155 156 157 158 |
# File 'lib/musa-dsl/transport/timer.rb', line 155 def terminate @terminate = true @thread&.wakeup end |