module Eventbox::Timer
Simple timer services for Eventboxes
This module can be included into Eventbox
classes to add simple timer functions.
class MyBox < Eventbox include Eventbox::Timer async_call def init super # Make sure Timer#init is called timer_after(1) do puts "one second elapsed" end end end MyBox.new # Schedule the alarm after 1 sec sleep 2 # Wait for the timer to be triggered
The main functions are timer_after
and timer_every. They schedule asynchronous calls to the given block:
timer_after(3.0) do # executed once after 3 seconds end timer_every(1.5) do # executed repeatedly every 1.5 seconds end
Both functions return an {Alarm} object which can be used to cancel the alarm through {timer_cancel}.
{Timer} always uses one {Eventbox::Boxable#action action} thread per Eventbox
object, regardless of the number of scheduled timers. All alarms are called from this thread. {timer_after}, {timer_every} and {timer_cancel} can be used within the {file:README.md#event-scope event scope}, in actions and from external scope. Alarms defined within the event scope must be non-blocking, as any other code in the event scope. Alarms defined in action or external scope should also avoid blocking code, otherwise one alarm can delay the next alarm.
Note: Each object that includes the {Timer} module must be explicit terminated by {Eventbox#shutdown!}. It is (currently) not freed by the garbarge collector.
Public Instance Methods
@private
# File lib/eventbox/timer.rb, line 85 def init(*args) super @timer_alarms = [] @timer_action = timer_start_worker end
# File lib/eventbox/timer.rb, line 116 def timer_after(seconds, now=Time.now, &block) a = OneTimeAlarm.new(now + seconds, &block) timer_add_alarm(a) a end
# File lib/eventbox/timer.rb, line 137 def timer_cancel(alarm) a = @timer_alarms.delete(alarm) if a timer_check_integrity end end
# File lib/eventbox/timer.rb, line 128 def timer_every(seconds, now=Time.now, &block) a = RepeatedAlarm.new(now + seconds, seconds, &block) timer_add_alarm(a) a end
@private
# File lib/eventbox/timer.rb, line 170 def timer_fire(now=Time.now) while @timer_alarms.last&.timestamp&.<=(now) a = @timer_alarms.pop if a.fire_then_repeat?(now) timer_add_alarm(a) end timer_check_integrity end # the method result is irrelevant, but sync_call is necessary to yield the timer blocks nil end
@private
# File lib/eventbox/timer.rb, line 165 def timer_next_timestamp @timer_alarms.last&.timestamp end
@private
# File lib/eventbox/timer.rb, line 92 def timer_start_worker loop do begin interval = timer_next_timestamp&.-(Time.now) Thread.handle_interrupt(Reload => :on_blocking) do if interval.nil? Kernel.sleep elsif interval > 0.0 Kernel.sleep(interval) end end rescue Reload else timer_fire end end end
Private Instance Methods
@private
# File lib/eventbox/timer.rb, line 145 def timer_add_alarm(alarm) i = @timer_alarms.bsearch_index {|t| t.timestamp <= alarm.timestamp } if i @timer_alarms[i, 0] = alarm else @timer_alarms << alarm @timer_action.raise(Reload) unless @timer_action.current? end timer_check_integrity end
@private
# File lib/eventbox/timer.rb, line 157 def timer_check_integrity @timer_alarms.inject(nil) do |min, a| raise InternalError, "alarms are not ordered: #{@timer_alarms.inspect}" if min && min < a.timestamp a.timestamp end end