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.
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 |
# 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 @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.
131 132 133 134 135 |
# File 'lib/musa-dsl/transport/timer.rb', line 131 def continue @stop = false @next_moment = Process.clock_gettime(Process::CLOCK_MONOTONIC) @thread.run end |
#run { ... } ⇒ void
This method blocks the current thread
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 indefinitely until stopped. 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.
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 108 |
# File 'lib/musa-dsl/transport/timer.rb', line 77 def run @thread = Thread.current @next_moment = Process.clock_gettime(Process::CLOCK_MONOTONIC) loop do 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 is called sleep if @stop 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.
118 119 120 |
# File 'lib/musa-dsl/transport/timer.rb', line 118 def stop @stop = true end |